2013年9月20日金曜日

PHP5.5.4にてstrict sessionsのバグ(bug65475)が修正されたがテストがないことに気づいた

以前のエントリで、PHP5.5.2にて大垣さん提案のstrict sessionsがマージされたと報告しましたが、PHP5.5.4にて、このバグ(bug65475)が修正されました。バグの例として紹介したアクセスカウンタも、カウントアップすることを確認しました。

しかし、bug65475のテストを見て、重大な抜けがあることに気づきました。
$ cat bug65475.phpt
--TEST--
Bug #65475: Session ID is not initialized when session.usr_strict_mode=1
--INI--
session.save_handler=files
session.name=PHPSESSID
--SKIPIF--
<?php include('skipif.inc'); ?>
--FILE--
<?php
ob_start();

echo "Testing file module".PHP_EOL;
session_start();
$_SESSION['foo'] = 1234;
$_SESSION['cnt'] = 1;
$session_id = session_id();
session_write_close();

session_start();
var_dump($session_id === session_id());
$_SESSION['cnt']++;
session_write_close();

session_start();
var_dump($session_id === session_id());
var_dump($_SESSION['cnt']); // Should be int(2)
session_write_close();

--EXPECTF--
Testing file module
bool(true)
bool(true)
int(2)
このテストは、session.use_strict_mode=1 の指定がないと意味がないはずですが、見あたりません。use_strict_modeのデフォルトは 0 ですから、これだと strict sessions モードでない状態でのテストになります。
試みに、上記指定を追加して、同バグのあるPHP5.5.2およびPHP5.5.3で上記をテストしてみました。
ockeghem@php552:~/php-5.5.2/ext/session/tests$ pear run-tests bug65475.phpt
Running 1 tests
PASS Bug #65475: Session ID is not initialized when session.usr_strict_mode=1[bug65475.phpt]
TOTAL TIME: 00:00
1 PASSED TESTS
0 SKIPPED TESTS
なんと、テストをPASSしてしまいました。バグがある状態でFAILしてくれないと、テストの意味がありません。ということで、上記テストは、use_strict_mode=1の指定がないだけでなく、テストとして不完全なようです。

それでは、他のテストはどうなんだろうと思って同じディレクトリをgrepしてみましたが、use_strict_mode=1に設定したテストはないようです。
PHP5.5.1からPHP5.5.2(strict sessionsが実装された)で、ext/session/tests内のテスト(*.phpt)は197で増えていません。PHP5.5.4でphptの数は200に増えましたが、上記の他にbug65359に関するもののようです。

まとめ

  • PHP5.5.4にてstrict sessionのバグ(65475)が修正され、ようやくstrict sessionsが使えるようになった
  • 上記バグに関するテストbug65475.phptにはバグがあり、PHP5.5.3以前でもFAILしない
  • PHP5.5.4までの時点で、strict sessionsに関するテストが1つも存在しない
ということで、strict sessionsはとりあえず使えるようになりましたが、テストが一つもない状態は今後の品質上不安ですので、テストを強化することを希望します。

2013年9月11日水曜日

PHPカンファレンス2013でトークします

PHPカンファレンス2013にてトークしますのでご案内します。
  • 日時:2013年9月14日(土曜日) 10時00分~(徳丸の出番は12:00~12:45)
  • 場所:大田区産業プラザ(PiO)
  • 費用:無料(申し込みはこちら)
  • 講演タイトル:安全なPHPアプリケーションの作り方2013
PHPカンファレンスでは2009年以降毎年トークさせていただいております。今年は、上記タイトルにて以下のテーマを取り上げます。
  • パスワードの守り方
  • セッションフィクセイション結局どうする
  • PHPのライフサイクルにどうつきあう?
  • HTML5セキュリティ入門
上の2つはPHPの最新版PHP5.5に関する話題です。また、PHPのリリースサイクルが短いということから、ライフサイクルについても(少し)お話しします。

HTML5のセキュリティについては少しずつ情報が出てきましたが、難しい、抽象的、結局なにが問題か分からない、という感想が多いのではないでしょうか。
そのため、架空の脆弱なレジストラ(ドメイン名屋さん)「おネーム.COM」に登場いただき、「HTML5で起こる実際の悪いこと」を見ていただこうと思います。
現在デモを準備中ですが、以下を予定しています。
  1. XHR Level2によるCSRFにてパスワード変更
  2. JSONハイジャックによる個人情報漏えい
  3. 広告モジュールのDOM Based XSS(単独では影響なし)
  4. ドメイン名販売ページでの潜在的なDOM Based XSS(単独では影響なし)
  5. 3と4の合わせ技で大変なことに…
関連技術としては下記があります。
  • XMLHttpRequest Level2 
  • localStorage
  • postMessage
  • クロスサイト・リクエストフォージェリ(CSRF)
  • DOM Based XSS

それでは、PiOでお会いしましょう。

2013年9月2日月曜日

ロリポップのサイト改ざん事件に学ぶシンボリックリンク攻撃の脅威と対策

既に報道されているように、ロリポップ!レンタルサーバーに対する改ざん攻撃により、被害を受けたユーザー数は8428件にのぼるということです。ここまで影響が大きくなった原因は、報道によると、(1)「WordPressのプラグインやテーマの脆弱性を利用」し、不正なファイルがアップロードされた、(2)パーミッション設定の不備を悪用されて被害が拡大した、ということのようです。
29日夜の時点では、攻撃者の改ざん手法について「WordPressのプラグインやテーマの脆弱性を利用」し、不正なファイルがアップロードされて「wp-config.phpの」の設定情報が抜き出されたと説明していたが、30日午後7時過ぎの説明で、この脆弱性が侵入経路となって同社のパーミッション設定の不備を悪用されたことが原因だったことを明らかにした。
「ロリポップ」のWordPressサイト改ざん被害、原因はパーミッション設定不備より引用
これ以上の詳細は本校執筆時点で公表されていないので憶測は控えますが、ロリポップからのリリースに以下の内容があることが気になるところです。
サーバーの設定を変更しFollowSymLinksを無効にしました。
当社サービス「ロリポップ!レンタルサーバー」ユーザーサイトへの第三者による大規模攻撃についてより引用
これは、httpd.confにて元々FollowSymLinksが有効になっていたか、レンタルサーバーの利用者が.htaccessによりFollowSymLinksを有効にできる状態であったという意味でしょう。この状況では、レンタルサーバーの悪意の利用者(サーバーへの侵入者を含む)が、同じサーバーを共有する別の利用者の秘密情報をシンボリックリンク攻撃により盗み読みすることができます。以下、シンボリックリンク攻撃の原理と脅威、対策について説明します。

デモ環境の説明

以下は、ロリポップに似せた設定の架空のレンタルサーバーのホームディレクトリ設定です。ユーザはsuzukiとtanakaで、それぞれsuzuki.example.jpとtanaka.example.jpがホスト名です。以下は、suzuki.example.jpの表示です。


ホームディレクトリの設定は下記となります。
$ ls -l
drwx-----x  3 suzuki      LolipopUser 4096 Aug 31 17:26 suzuki/
drwx-----x  3 tanaka      LolipopUser 4096 Aug 31 17:39 tanaka/
$
各利用者のホームディレクトリのパーミッションが701となっていて、同じグループ(LolipopUser)に属していますが、これはレンタルサーバー特有の設定です。これは、レンタルサーバーの利用者同士は同じグループに属するため、他のユーザのファイルにアクセスできず、apache等のサービスは、LolipopUserグループに属さないユーザの権限で動くため、各ユーザのファイルにアクセスできるという設定になっています。

レンタルサーバーの利用者間では、ファイルは閲覧できない

ここで、tanakaさんが、suzukiさんのファイルを閲覧できないことを示します。
$ su - tanaka
Password:  ←パスワードを入力
tanaka$ pwd
/home/tanaka
tanaka$ ls -l
total 8
-rw-r--r-- 1 tanaka LolipopUser  465 Sep  1 23:45 log.txt
drwxr-xr-x 2 tanaka LolipopUser 4096 Sep  1 23:39 www
tanaka$ ls -l ../suzuki/
ls: cannot open directory ../suzuki/: Permission denied  ← 別ユーザのディレクトリは参照できない
tanaka$ cat ../suzuki/www/index.html
cat: ../suzuki/www/index.html: Permission denied  ← 別ユーザのコンテンツも同様
tanaka$

閲覧権限がないファイルにシンボリックリンクを設定できる

ところが、tanakaさんは、suzukiさんのファイルに対してシンボリックリンクを設定することは可能です。
tanaka$ cd www
tanaka$ ln -s ../../suzuki/www/index.html suzuki.html
tanaka$ cat suzuki.html
cat: suzuki.html: Permission denied  ← シンボリックリンクは作れるが参照はできない
tanaka$
上記のように、シンボリックリンクは、権限のないファイルに対して設定することができます(存在しないファイルにも可)。上記のように、このシンボリックリンクを指定してもファイルを閲覧することはできませんが、apacheによる表示は可能です。apacheは、シンボリックリンク自体とリンク先の両方にアクセス権があるからです。


ただ、これだと、元々公開している情報を別のホストで表示しているだけなので、攻撃としての価値はあまりありません。問題は、この方法で以下が可能になる場合があることです。
  • CGIプログラムやPHPスクリプトのソースが閲覧できる
  • 非公開ディレクトリのファイルが閲覧できる場合がある

閲覧できない情報をシンボリックリンク攻撃により表示する

以下、これを試してみましょう。suzukiさんのホームページ上にメールフォームがあり、そのファイル名が inquiry.php であることが分かっているとします。以下のように、これに対して inquiry.txt というシンボリックリンクを設定します。
tanaka$ ln -s ../../suzuki/www/inquiry.php inquiry.txt
これを閲覧すると、下記となります。


拡張子を.txt に変更したことで、PHPスクリプトのソースが見えてしまっています。
実は、拡張子が.php のままだと、レンタルサーバー環境の場合、PHPスクリプトは実行できないと考えられます。レンタルサーバーの場合は、CGIプログラムやPHPスクリプトは、suEXECにより、各ユーザの権限で動作します。上記のシンボリックリンクの場合、tanakaの権限により、suzukiのスクリプトを実行しようとしますが、今まで見たように、tanakaの権限ではスクリプトファイルの読み込みができないからです。しかし、apacheの実行ユーザだと読み込み権限があるため、ソースの閲覧は可能です。
さて、上図のソースから、ログファイルが ../log.txt だと分かります。このファイルにもシンボリックリンクを設定して、閲覧できるか調べてみましょう。
tanaka$ ln -s ../../suzuki/log.txt log.txt
閲覧画面は下記となります。


上記のように、通常は閲覧できないディレクトリ上の、攻撃者に読み込み権限のないデータファイルも、シンボリックリンクを設定することで、外部から閲覧できることが分かりました。この際のlog.txtのパーミッションは下記となっています。
tanaka$ su - suzuki
Password:
suzuki$ ls -l log.txt
-rw-r--r-- 1 suzuki LolipopUser 465 Sep  1 23:45 log.txt
suzuki$
すべてのユーザーに対して読み込み権限が与えられています。これは、PHPスクリプトのfopenで作成したファイルに与えられるパーミッションですが、仮にアプリケーションの実行に最低限の権限(600)が設定されていたら、apacheから読み込むことができず、このファイルに対するシンボリックリンク攻撃は成立しません。

シンボリックリンク攻撃が成立する条件

冒頭に書いたように、シンボリックリンク攻撃が成立する条件は下記の両方が成立する場合です。
  • FollowSymLinksが有効になっているか、攻撃者が.htaccessによりFollowSymLinksを有効にできる
  • 攻撃者が、公開ディレクトリにシンボリックリンクを設定できる
通常のWebサーバーでFollowSymLinksが問題にならないのは、後者の条件が成立しないからです。一方、レンタルサーバーの場合は、以下のいずれかにより、攻撃者が公開ディレクトリにシンボリックリンクを設定可能です。
  • 攻撃者がレンタルサーバーのユーザとなる(お試し等でも可)
  • 攻撃者がレンタルサーバーのユーザtanakaを攻撃して、tanakaの書き込み権限を得る
通常のWebサーバーでも、攻撃者が書き込み権限を得ることはあり得ますが、他のユーザの権限を得る動機があまりないところが、レンタルサーバー環境との違いです。

シンボリックリンクを作成する時点で、相手側のディレクトリ一覧は参照できない場合が多いのですが、以下の手順でファイル名が推定可能です。
  • 外部公開のURLからファイル名を推定する
  • スクリプトのソースファイルからファイル名を得る
  • WordPress等の標準的なファイル構成からファイル名を得る

シンボリックリンク攻撃の脅威

一般的には、シンボリックリンク攻撃が成立すると、攻撃者の有する権限よりも高い権限を獲得することができます。上記の例では、攻撃者が持っているtanakaの権限に加えて、apacheの権限により、権限を持たないファイルも閲覧可能になります。
おおざっぱに言って、シンボリックリンク攻撃の影響は、ディレクトリトラバーサル攻撃の影響と似ています。

対策

一般に、シンボリックリンク攻撃が問題になる場合は、ファイルのオープン時にシンボリックリンクかどうかを確認して、シンボリックリンクの場合はエラーにします(参考)。apacheの場合は、FollowSymLinksを無効にすることで、これが実現できます。
しかし、これだけではだめです。攻撃者が.htaccessの設定で、FollowSymLinksを設定する可能性があるからです。このため、AllowOverride ディレクティブにて、Noneを指定するか、Options=にて、許可したいオプション(FollowSymLinks以外)を明示的に列挙する方法があります。
利用者側でシンボリックリンクを安全に設定したい場合は、SymLinksIfOwnerMatchを利用できます。これは、「シンボリック先のファイルまたはディレクトリが、 シンボリックリンクの所有ユーザ ID と同じ場合にのみシンボリックリンクを たどれるようにします。」というものです(参照)。
レンタルサーバーの利用者が上記を確認する方法についてはここでは説明しませんので、レンタルサーバー事業者に個別に確認いただくのがよいと考えます。レンタルサーバー事業者は、今回の事件を受けて、自社の設定状況と安全性を説明いただく(あるいは急いで設定を修正する)とよいでしょう。

まとめ

シンボリックリンク攻撃の脅威と対策について説明しました。これは、シンボリックリンクを悪用して、上位権限のプロセスにファイルをアクセスさせる手法です。前述のように、レンタルサーバー環境で、apacheのFollowSymLinksが有効な場合、シンボリックリンク攻撃が成立してしまいますが、これはレンタルサーバー利用者の中に悪意のユーザが存在する可能性があるからです。
似たような状況として、Androidアプリケーションがあります。Androidアプリケーションは、アプリケーション毎にLinuxのアカウントが割り当てられるので、悪意のアプリケーション(マルウェア)が端末に導入された場合、root権限で動作するプロセスに対してシンボリック攻撃を仕掛け、マルウェアがroot権限を奪取するような攻撃に使われます。

追記(2013/09/03 10:00)

さとうふみやすさんから、apacheの-FollowSymLinksでは、レースコンディションによりシンボリックリンク攻撃の防御が回避されてしまうという指摘をいただきました(参照: Apache HTTPD: `Options -FollowSymLinks` は不完全)。さとうさん、ありがとうございます。
これは、TOCTOU 競合状態という問題で、参考文献として紹介したJPCERT/CCのドキュメントでも言及されています。また、TOCTOU競合を避ける方法についても、同じJPCERT/CCの解説に、「POS35-C. シンボリックリンクの有無のチェック時の競合状態を避ける」として紹介されています。ただ、これはapache(などのhttpd)を開発する側でとれる対策で、apacheの利用者としては、apache側で対策してくれるのを待つしかありません。
さとうさんは、『この攻撃の根本的な対策方法は「シンボリックリンクを作らせない」 しかない』としていますが、これもなかなか難しいと思います。現在のレンタルサーバーの多くは、シンボリックリンク攻撃のTOCTOU競合については受容している(あきらめている)のだと理解しています。
利用者側でとれる対策としては、CGIやPHPのスクリプトや、これらからアクセスするファイルのパーミッションを600や400として、apacheから読み取らせないようにすることです。ただし、.htaccessや.htpasswdはapacheから読めないとまずいので、この方法では保護できません。
ということで、この問題が受容できない利用者は、さとうさんの言及されている「ユーザーごとに別権限の Web サーバーを立ち上げる」サービスを提供しているレンタルサーバーやVPSなどに移行するしかないと考えます。
(追記終わり)

参考文献

2013年8月26日月曜日

PHP5.5.2以降のstrict sessionsモードでセッションフィクセイション対策はどうすればよいか

先日のエントリ『【速報】PHP-5.5.2にて大垣さんのstrict sessionsが実装されました』にて、PHP5.5.2でセッションアダプションが解消されたことを報告しました(session.use_strict_mode=1の場合)。
セッションアダプションとは、未初期化のセッションID(たとえばPHPSESSID=ABC)をPHPが受け入れる問題のことです。strict sessionsを使用すると、PHPが生成し、現在有効であるセッションIDのみを受け入れ、そうでない場合、PHPはセッションIDを振り直します。

あいにくPHP5.5.2(PHP5.5.3も)にはバグがあり、session.use_strict_mode=1によるstrict sessionsは使用できませんが、既に大垣さん自身によりバグ修正されているので、PHP5.5.4からは使えるようになるでしょう。

strict sessionsの主な目的は、セッションフィクセイション攻撃に対する影響緩和ですが、では、strict sessionsを用いると、セッションフィクセイション対策はどのように変わるでしょうか?

従来のPHP、すなわちセッションアダプションがある状態では、セッションフィクセイション対策は下記の通りでした。
  • ログイン直後にsession_regenerate_id関数によりセッションIDを振り直す
これに対して、セッションアダプションがない状態ではどうでしょうか。先に『セッションアダプションがなくてもセッションフィクセイション攻撃は可能』で説明したとおり、攻撃者が、対象サイトのセッションIDを取得することにより、セッションフィクセイション攻撃は可能です。従って、strict sessionsの状況でも、セッションフィクセイション対策は下記の通りです。
  • ログイン直後にsession_regenerate_id関数によりセッションIDを振り直す
何も変わらないわけです。

…とここで終わりにしてもよいのですが、せっかく大垣さん8年越しのstrict sessionsが使えるようになったので、セッションIDの振り直しをしないでセッションフィクセイション対策ができないか考えてみましょう。

先に説明したように、セッションアダプションがない状況では、攻撃者は、対象サイトから有効なセッションIDを取得する必要があります。ここに着目し、「攻撃に使えるセッションIDを攻撃者に渡さない」アプローチを考えてみます。セッションアダプションがなく、セッションIDがとれない状況では、セッションフィクセイション攻撃はできません。

まず、上記がだめな状況として、ログイン状況でなくてもセッションを使っているサイトが挙げられます。この場合、サイトにアクセスさえすれば有効なセッションIDが取得できるため、セッションフィクセイション攻撃が可能になります。このため、以下では、セッションはログイン状態でのみ使用しているという前提とします。

以下、ログイン状態でのみセッションを使っているアプリケーションを想定して、セッションIDの振り直しをしないでセッションフィクセイション対策する方法を検討します。以下の処理毎に検討します。
  • ログイン前
  • ログイン処理
  • ログイン状態の確認処理
  • ログアウト処理

ログイン前

前述のように、ログイン前にセッションを有効にすると、そのセッションIDを悪用してセッションフィクセイション攻撃ができてしまいます。このため、ログイン前にはセッションは使えません。これによる副作用として、ログイン時にCSRF対策したい場合でも、それが困難になります。CSRFの標準的な対策にはセッションを利用するからです。

ログイン処理

ログイン処理の注意として以下があります。
  • ログインに失敗した場合は、セッションID悪用防止のためセッションを破棄する
  • 元々ログイン状態の場合は、ログイン処理を継続しない。攻撃者のログイン状態のセッションIDによる攻撃を防ぐため
これを実現する擬似コード例を下記に示します。
session_start();
if (ログイン済みの状態) {
  echo "ログイン済みです";
  // マイページなど、遷移可能なページへのリンクを表示
  // あるいは、異常事態としてセッション破棄するという考え方もあり
} else if (idとパスワードが有効) {
  // ログイン処理
  $_SESSION['user'] = $id;
  // その他の処理
} else {
  echo "IDまたはパスワードが違います";
  // 認証失敗の場合はセッションを破棄
  session_destroy();
}
重要なポイントとしては、(1)ログイン済みなのに他のユーザでログインすることを許さない、(2)認証に失敗した場合は必ずセッションを破棄する、ということがあげられます。
また、懸念点として、ログイン処理中にアプリケーションが異常終了すると、session_destroyが呼び出されず、有効なセッションが残ってしまう可能性があります。対策は、セッションを有効にする区間をできるだけ短くすることですが、詳しい説明を割愛します。

ログイン状態の確認処理

ログイン前の攻撃者がセッションIDを取得する方法として、攻撃者が、認証の必要なページにアクセスするというものがあります。
典型的なログイン確認は以下の通りです。
session_start();
if (! isset($_SESSION['user'])) {
  echo 'ログインしてください';
  // ログインページへのリンクを表示
  exit;
}
これだと、セッションIDは生成され、セッションは有効なままなので、攻撃者は有効なセッションIDを取得できます。これを防ぐには、exitする前にセッションを破棄します。
session_destroy();
これにより、セッションは有効でなくなるため、セッションフィクセイション攻撃に使えるセッションIDは取得できなくなります。この処理は、ログイン状態を確認するページ全てで、もれなく実装する必要があります。

ログアウト処理

ログアウト処理において重要なポイントは、かならずセッションを破棄するということです。そうしないと、ログアウト後に残ったセッションのセッションIDを用いて、セッションフィクセイション攻撃ができてしまいます。
具体的には、下記のようなログアウト処理はだめだと言うことです。
$_SESSION['user'] = false; // 認証ユーザをfalseにすることでログアウトとする

アプリ側のセッションタイムアウトに対する注意

アプリケーション仕様によっては、PHPのセッションは有効だが、アプリ側で定めたセッションタイムアウトになっているという状況が考えられます。この場合、「ログアウト状態だがセッションIDは有効」という状態になり、セッションフィクセイション攻撃に使えるセッションIDができてしまいます。
この対策としては、下記が考えられます。
  • ログイン状態の確認処理の中で、タイムアウトしたセッションを破棄する
  • ログイン処理においては、タイムアウトしたセッションは、いったん破棄して、再度セッションを開始する。
ログイン状態の確認…については、前述の処理でカバーされているとも考えられますが、ログイン処理の方は特に注意が必要です。

一方、PHP処理系側でタイムアウトしたセッションは、既に破棄されているため、アプリケーション側で特に注意することはありません。

まとめ

strict sessionsにおいても、セッションフィクセイション攻撃対策として、認証成功直後のsession_regenerate_idは必須ですが、敢えてこれをしないで、セッションフィクセイション攻撃対策する方法を検討し、以下が必要であることを示しました。
  • ログイン前にはセッションを使用しない
  • ログインに失敗した場合は、セッションを破棄する
  • 元々ログイン状態の場合は、ログイン処理を継続しない
  • ログイン状態の確認において、ログイン状態でない場合はセッションを破棄する
  • ログアウト処理ではセッションを破棄する
  • ログイン状態の確認処理の中で、タイムアウトしたセッションを破棄する
  • ログイン処理においては、タイムアウトしたセッションは、いったん破棄して、再度セッションを開始する
ご覧のように、strict sessionsによってセッションフィクセイション脆弱性を気にしないですむどころか、至る所でセッションフィクセイション脆弱性に配慮しなければならないことがわかりました。上記の1カ所でも漏れると、セッションフィクセイション脆弱性の可能性が生じます。

したがって、strict sessionsにおいても、下記を推奨します。これだと、セッションフィクセイション対策は、ログイン処理1カ所に集約できます。
  • ログイン直後にsession_regenerate_id関数によりセッションIDを振り直す
ということで、strict sessionsにおいても、アプリケーションの書き方は変わらない、というのが結論です。

2013年8月17日土曜日

【速報】PHP-5.5.2にて大垣さんのstrict sessionsが実装されました

大垣さんのツイートで、strict sessionsがPHPにマージされることを知りました。
本日、PHP-5.5.2が公開されましたので、ChangeLogを確認したところ、確かに入っているようです。
  • Sessions:
    • Implemented strict sessions RFC (https://wiki.php.net/rfc/strict_sessions) which protects against session fixation attacks and session collisions.
PHP 5 ChangeLog より引用
8年越しのstrict sessionsのマージ、まことにおめでとうございます。

さっそく、PHP-5.5.2をビルドして確認してみました。まずはデフォルトの状態(php.ini-productionを使用)。


確かに、session.use_strict_modeが追加されていますね。デフォルトはOffのようです。これは後方互換性に配慮したものでしょう。session.use_strict_modeを有効にするには、php.iniに下記を追加すればよいのでしょうね。
session.use_strict_mode = On
これを設定して再度phpinfoを実行すると、下記の画面に。


いい感じです(当たり前か)。
さっそくこの状態で試してみましょう。スクリプトとして以下を用いました。
<?php
  session_start();
  if (isset($_SESSION['counter'])) {
    $_SESSION['counter']++;
  } else {
    $_SESSION['counter'] = 0;
  }
  echo 'phpversion: ' . htmlspecialchars(phpversion()) . '<br>';
  echo 'session_id: ' . htmlspecialchars(session_id()) . '<br>';
  echo 'counter : ' . htmlspecialchars($_SESSION['counter']);
これを実行すると下記の表示になります。
phpversion: 5.5.2
session_id: i0h3ej9hcs6dd40sopac9pf483
counter : 0>
セッションファイルを見ると、確かに生成されています。
$ sudo cat /tmp/sess_i0h3ej9hcs6dd40sopac9pf483
counter|i:0;$
しかし、セッションクッキーとして違うIDが返ってきています(HTTPレスポンスの抜粋)。
HTTP/1.1 200 OK
Set-Cookie: PHPSESSID=7irqs0dkccnusq9ckjcnlmkn21; path=/
Content-Length: 74
Content-Type: text/html

phpversion: 5.5.2<br>session_id: i0h3ej9hcs6dd40sopac9pf483<br>counter : 0
このため、新規にセッションを開始することができず、永遠にカウンタは 0 のままです。この状態で、PHPの下記の警告が表示されます。
PHP Warning:  session_start(): The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in /var/www/session.php on line 2
ちなみに、session.use_strict_mode = Off (あるいは指定しない)の場合は、セッションは正常に使用でき、カウンターはインクリメントされます。そして、有効なセッションがある状態(セッションクッキーとセッションファイル名が一致している状態)では、session.use_strict_mode = On でもセッションは有効に使用できます。

まとめ

PHP-5.5.2にて、大垣さんの提唱されてきたstrict sessionsがマージされました。私の検証した範囲では、まだ不具合があり使えないようですが、私の環境に依存する問題かもしれませんので、PHP関係者が追試されることを希望します。
このstrict sessionsにて、PHPのセッションアダプションが解消されるはずです。私は「PHPのセッションアダプションは重大な問題ではないが、ない方がよい」という認識でしたので、この改善を私は歓迎いたします。
現時点ではPHP5.3とPHP5.4にはstrict sessionsはマージされていないようですが、だから言ってこれらPHPのバージョンが脆弱だとまではいえないと考えます。

追記(2013/08/17 21:00)

その後テスト用のサンプルをいじっているうちに、セッションが空の場合session_regenerate_id(true);を実行すると、期待したとおりに動作することが分かりました。上記のサンプルだと、「} else {」 の次の行に session_regenerate_id(true); を追加すると、セッションが維持できるようになります。

追記(2013/08/21 9:00)

bugs.php.netにバグ報告したところ、大垣さん自身が速攻で修正してクローズしていただきました。大垣さん、ありがとうございました。

関連するエントリ

2013年8月16日金曜日

MACHIDA.KANAGAWA.JPはなぜ「まぎらわしい」のか

私は検証用にいくつかのドメイン名を登録していますが、そのうちの一つが「発掘」され、世間をお騒がせすることになってしまいました。

このページのページビューは、8月16日(金) 9:00現在で、31438となっています。ずいぶんよく参照されています。ちなみに、「本物の」町田市のホームページは下記の通りです。


しかし、上記のような主張はいままでにもあったし、今更二番煎じのこのネタが、上記のような簡素な内容で話題になる理由としては、やはり MACHIDA.KANAGAWA.JPというドメイン名にあるのでしょう。これは「都道府県型JPドメイン名」というもので、「日本国内に住所を持つ個人・組織であれば、いくつでも登録ができます」(こちらより引用)。

では、どうして紛らわしいドメイン名が登録できるかという理由を説明します。
都道府県型JPドメイン名ができる前に地域型JPドメイン名というものがあり、多くの地方公共団体が利用しています。以下は、地域型JPドメイン名のうち、「地方公共団体ドメイン名」の説明です。JPDirectの説明ページから引用しました。


上記のように、政令指定都市をのぞく「市」のドメイン名は以下のルールに従います。


地域型JPドメイン名のルールは複雑なので、「地域型ドメイン名は廃止してはどうか」という指摘もあったものの、市のホームページに限っていえば、上記のルールに従ったドメイン名は「市」のものであることが確実でありました。
世の中には偽サイトというものがあり、フィッシングや偽情報の流布に使われます。ドメイン名を暗記していないサイトの場合、サイトの内容やデザインを見ただけでは区別がつかないので、ドメイン名の形式から簡単に地方公共団体のものであることが判別できると安心です。たとえば、日本の政府機関のドメイン名は「.go.jp」で終わるドメイン名となっている(例外もある)ので、簡単に判別できます。

地域型JPドメイン名は、個人や一般の団体でも取得できましたが、第4レベルが選べるもので、「市」のドメイン名とは簡単に区別がつきました。
  • TOKUMARU.BUNKYO.TOKYO.JP (私のドメイン名)
  • MITA.MINATO.TOKYO.JP (三田典玄氏のドメイン名)
※ 三田典玄氏のドメイン名は、港区三田という地名が実際にあるのでややこしいですが、大きな誤解は生じないでしょう。このドメイン名もシャレなんでしょうね。

さて、地域型JPドメイン名は、とにかく長いという欠点があり、あまり活用されていなかったようです。このため、2012年3月31日で新規登録が終了し、2012年11月から都道府県型JPドメイン名というものが使えるようになりました。その形式は下記となります(都道府県型.jpより引用)。


上図のように、「〈都道府県名〉.JP」の部分が旧の地域型JPドメイン名と重なっているため、地域型JPドメイン名の部分は予約され、都道府県型JPドメイン名としては登録できないようになっています。予約名の一覧はこちらから参照できます。
このように、ドメイン名を見ただけでは、地域型JPドメイン名なのか都道県型JPドメイン名なのか区別がつかなくなってしまいました(地域型JPドメイン名をすべて暗記していない限り)。このため、以下のようにややこしいことが起こります。
  • CITY.MACHIDA.TOKYO.JP : 町田市の本物のドメイン名(地域型JPドメイン名)
  • CITY.MACHIDA.KANAGAWA.JP : 筆者のドメイン名(のサブドメイン名)(都道府県型JPドメイン名)
すなわち、都道府県型JPドメイン名が出現するまでは、下記の形式のドメイン名は「市のドメイン名」であることが確実だったのに、その保証がなくなってしまったことになります。従来地域型JPドメイン名を使っていた地方公共団体や、市民にとっては迷惑な話ですね。


では、どうすればよいかというと、地方公共団体には、LG.JPドメイン名に移行してもらうしかないでしょう。
LG.JPドメイン名創設の目的
地方公共団体では、政府がe-Japan戦略に掲げる電子政府・電子自治体の実現に向け、住民・企業がインターネットを利用していつでもどこでも申請・届出等の手続が行える仕組みづくりを進めています。
匿名性が高いインターネット上で、住民・企業の皆様が安心して行政サービスを利用できるようにするためには、まずは行政サービスの提供者が地方公共団体であることを、住民・企業の皆様に分かりやすく示す必要があります。このため、政府機関等を収容するドメイン空間「GO.JP」に対応する、厳密に地方公共団体及び地方公務員を収容した住民・企業の皆様に分かりやすい地方公共団体行政専用のドメイン空間が必要とされました。
LG.JPドメイン名の創設は、地方公共団体組織認証基盤(LGPKI)の整備などと合わせて、地方公共団体が提供する電子行政サービスの信頼性を確保し、住民・企業の皆様が安心してサービスを受けられるようにすることを目的としています。
LG.JPドメイン名について - 財団法人 地方自治情報センター(LASDEC) より引用
以下は、LG.JPドメイン名を活用している地方公共団体の例です。
LG.JPドメイン名の登録開始が2002年10月で、それから10年以上を経ていますので、そろそろ地方公共団体は地域型JPドメイン名からLG.JPドメイン名に移行してほしい・・・ということなのでしょう。

まとめ

以前から地域型JPドメイン名の一種として「地方公共団体ドメイン名」というものがあり、地方公共団体の識別ができるドメイン名として今も多数利用されていますが、都道府県型JPが登場したことで、簡単には地方公共団体のドメイン名であることが識別できなくなりました。既にLG.JPドメイン名という識別しやすいドメイン名が用意されているので、できるだけ早くLG.JPドメイン名への移行が望まれます。
住民の立場からは、地方公共団体のサイトを利用して、重要な情報を得る場合や個人情報を入力する場合は、ドメイン名がLG.JPでないサイトについては、本当に地方公共団体のサイトであるか、信頼出来る方法で確認した方がよいでしょう。
例えば、地方自治情報センター(LASDEC)のホームページには、全国自治体マップ検索というページが用意されています。LASDECのサイトはEV SSLを利用しているので、本物であることの確認が容易に行えます。

追記

地域型JPドメイン名と都道府県型JPドメイン名を簡単に区別する方法として、Firefoxを使って閲覧するというものがあります。冒頭の画面キャプチャのアドレスバーを見ていただくと、都道府県型JPドメイン名(1番目の画像)の方はmachida以下が濃くなっていますが、地域型JPドメイン名(2番目の画像)だと、city以下が濃くなっています。これで区別ができますが、一般の方が活用するのは難しいでしょうし、Google ChromeやIEではこの方法は使えません。Firefoxをお使いの方は、試してみてください。

2013年8月8日木曜日

パスワードの定期的変更について徳丸さんに聞いてみた(2)

高橋: こんにちは、高橋です。前回に引き続き、徳丸さんをお招きして、パスワードの定期的変更問題についてお話を伺います。徳丸さん、よろしくお願いします。

徳丸: はい。よろしくお願いします。

高橋: 前回は、「オンライン攻撃に対する予防としてパスワードの定期的変更は意味がない」という結論でしたが、今回は、事後の被害軽減策として、パスワードの定期的変更に意味があるか、というテーマですね。

徳丸: はい。その事後の話ですが、2つの話題があります。まず、前回の続きで、パスワードハッシュ値が漏洩してオフライン攻撃で解読されるまでの時間稼ぎとして、パスワードの定期的変更に意味があるか、次に、パスワードそのものが漏れている場合の緩和策として、定期的変更に意味があるかです。

高橋: それでは、まずハッシュ値が漏洩しているケースについてお願いします。

徳丸: はい。まず、前提として「パスワードを3ヶ月毎に変更する」というポリシーを想定しましょう。3ヶ月に深い意味はありませんが、ありがちな想定です。

高橋: 四半期毎だと、企業人としてもキリがいい感じです。

徳丸: 次に、ハッシュ値の解読に要する時間がどれくらい掛かるかが問題です。これが短過ぎても、長過ぎても、パスワードの定期的変更には意味がありません。

高橋: なぜでしょうか?

徳丸: まず、短すぎる場合、例えば 1週間でハッシュからパスワードが分かるとすると、定期的変更の前にパスワードが悪用される可能性が高いですよね。逆に、解読に30年掛かる場合は、変更しなくても、30年も経てばそのパスワードは使わなくなっている可能性が高いでしょう。

高橋: それでは、ハッシュの解読に1年掛かるくらいに予め想定して、パスワードの保存方法を決めれば良いのではありませんか? そうすると、3ヶ月毎のパスワードの変更がいい感じです。

徳丸: そうはいかないのですよ。まず、ハッシュの解読に 1年掛かると想定しても、攻撃側がマシンの台数を 12倍に増やせば、1ヶ月で終わってしまいます。パスワードの変更が間に合わない可能性が高いです。

高橋: ハッシュの解読は並列処理が容易という話題は、前回にも出てきました。

徳丸: 次に、わざわざ 1年で解読できるように調整する必要もないですよね。1000年でも、1万年でも、うーんと長くすれば、パスワードの定期的変更の必要はないわけですから。

高橋: 前回教えて頂いたソルト、ストレッチングや、長いパスワードの設定により、それが可能になるわけですね。

徳丸: そうです。8文字英数字パスワードのハッシュが、GPUの活用で、 1日で解読できるとしましょう。同じ条件で、英数字記号で12桁のパスワードに変えると、ハッシュの解読にはどれくらい掛かると思いますか?

高橋: 前回のお話では、1000万倍ということでしたね。

徳丸: それは、さまざまな条件を考慮した控え目な(安全側の)数字です。計算してみると、約56億倍掛かります。

高橋: 徳丸さん、さばを読み過ぎ…逆か、控え目過ぎたのではないですか?

徳丸: パスワードに記号を使えるようにした寄与もあり、それが12乗で掛かるので莫大な差になるのです。年で言うと、1500万年掛かる計算です。

高橋: ひゃー、ハードの性能向上や攻撃マシンの台数増強を考慮しても、十分な余裕がありそうですね。

徳丸: そうです。加えて、ソルトやストレッチングの寄与もあり得るわけですが、利用者に目に見える範囲だけでも、これだけの改善ができるわけです。

高橋: なるほど…でも、前回のお話では、まだ8文字英数字のパスワードのサイトが多いというお話でしたよね。

徳丸: そうなんです。例えば、三井住友銀行のネットバンキングでは、「半角4~8桁の英数字」となっています。

高橋: 4桁もありですか!

徳丸: まぁ、4桁パスワードをつけてしまうは利用者の責任としても、上限は緩和して欲しいですね。

高橋: もっとマシな銀行はないですのか?

徳丸: どこも似たり寄ったりですし、実は、三井住友のネットバンキングやめようと思った矢先に、ワンタイムパスワードのトークンを無償化するという英断があったので、引き続き利用しています。

高橋: ワンタイムトークンの無償化はいいですね。

徳丸: そうなんです。前は、月に105円払ってましたから、ありがたいです。

高橋: また、話がそれてしまいました(_ _) 次に、パスワードそのものが漏れてしまった場合の緩和策について教えて下さい。

徳丸: わかりました。この場合重要なことは、「パスワードが漏れた事実を利用者が気づけるか、それはいつか」ということです。

高橋: 時々、個人情報漏洩のニュースやリリースが発表されますが、それのことですか?

徳丸: はい。加えて、利用者がサイトを使っていて気づくこともありますね。

高橋: どんな場合でしょうか?

徳丸: 正しいパスワードを入力しているのにログインできないとか、不正利用(メッセージスパムの送信、意図しない振込…)に気づいたとかですね。

高橋: パスワード漏洩のお知らせがないのに、実際にはパスワードが漏洩しているとすると、サイト側が気づいていないか、気づいているのに隠しているということでしょうか?

徳丸: そういうケースもあり得るとは思いますが、実際に多いのは利用者の不注意でパスワードが漏洩するケースだと思います。

高橋: 具体的に教えて下さい。

徳丸: 一番ありそうなのはフィッシングですね。こちらの記事も参照して下さい。

高橋: なるほど、自分の不注意で漏らしてしまったら、中々気づけないし、サイト側も気づかない場合が多そうです。

徳丸: そうです。最悪の場合、パスワードが漏洩したことに、利用者は永遠に気づかないことになりますね。

高橋: それは困ります。それでは、やはりパスワードの定期的変更が必要なのではありませんか?

徳丸: パスワードの定期的変更は最後の手段として、もっとよい方法がないか考えましょうよ。

高橋: そんな方法がありますか?

徳丸: はい。まず、定期的にログイン履歴を確認するという方法があります。下図は、Googleの最近のアクティビティです。

高橋: 結構詳しい情報が表示されていますね。

徳丸: そうです。これを見て、怪しいログインがあれば、不正ログインの可能性と見てパスワードを変更します。

高橋: ……結局、定期的にチェックして、怪しければパスワードを変更するので、手間はあまり変わらない気がしますが…

徳丸: でも、手間が似たようなものとしても、管理レベルは随分違います。パスワードの定期的変更は、不正アクセスがあるかないか分からない前提で、とにかくパスワードを変更するわけですから。しかし、もっとよい方法もあります。

高橋: なんだ、早くそれを教えて下さい。

徳丸: 2段階認証が使える場合は、2段階認証をぜひ設定しましょう。

高橋: Googleや、facebookやYahoo!で導入されている、6桁数字を追加で入れるあれですか。

徳丸: はい。2段階認証を設定しておけば、フィッシングに関しては十分な対策になります。

高橋: フィッシング以外の原因、例えばサイトが不正アクセスされて情報が漏洩した場合も大丈夫ですか?

徳丸: 大丈夫な場合と大丈夫でない場合があり得ますね。

高橋: 大丈夫でない場合とは?

徳丸: 2段階認証にワンタイム生成アプリを使っている場合、ワンタイムパスワードは秘密のシード(シークレットキー)と時刻からパスワードの数字を生成します。このシードはサイト側でも保存していないとパスワードの照合ができないので、この値まで漏洩したら、第三者がワンタイムパスワードを知り得ることになります。

高橋: そんなことがあり得るのでしょうか?

徳丸: あり得るのかと疑問に思うのはもっともですが、RSAのワンタイムトークンのシードが漏洩して不正アクセスされたという事例があります。IIJの根岸さんが素晴らしい解説記事を書いておられるので、ぜひお読み下さい。

高橋: 2段階認証まで破られる可能性があるとしたら、やはりパスワードは定期的に変更した方がよいのではありませんか?

徳丸: レアなケースだとは思いますが、そこまで心配するのであれば、ログイン履歴の定期的な確認をお勧めしますよ。

高橋: でも、ログイン履歴が確認できないサイトも多いですよ。

徳丸: そこです。どのサイトに対してもログイン履歴を定期的に確認するなんて現実には不可能です。ですが、全てのサイトのパスワードを3ヶ月毎に変更することも、労力が大きすぎます。

高橋: どうすればいいの? と思ってしまいますが。

徳丸: サイトの性質によって、管理レベルを区別することをお勧めします。区別の基準として、サイトに不正ログインできる場合、短期的な攻撃で終わるものと、長期に攻撃が続くものがあります。

高橋: 短期的に攻撃が終わってしまうことがあり得ますか? 攻撃者はいつまでも攻撃したいのでは?

徳丸: 具体的には、メッセージスパム、寸借詐欺、不正な振込、不正な物品購入等は、長期的に攻撃したいと思っても、被害者が直に気づいて、パスワード変更などの処置をしてしまいます。攻撃者側も心得ていて、たとえば預金のありったけを振り込みしてしまいます。

高橋: なるほど、ちびちび攻撃したら、儲けが減るということですね。では、長期に攻撃が続くものとはどのようなものでしょうか?

徳丸: 利用者に気づかれない攻撃としては、情報の漏洩が代表的です。たとえば、メールの盗聴が目的であれば、スパム送信などしないで、じっと静かに盗聴を続けるでしょうね。

高橋: それは怖い! やはりパスワードの定期…

徳丸: (遮って)いや、情報漏洩が心配ということは、秘密情報を扱うサイトということなので、そのようなサイトは少数に絞り、2段階認証やログイン履歴確認ができるサイトを選定することですね。

高橋: あー、ようやく道筋が見えてきたような気がします。秘密情報を扱うサイトは、2段階認証をサポートしたサイトに限定しろ、と。

徳丸: それをお勧めします。

高橋: でも、現時点で2段階認証に対応しているサイトは大抵米国のサービスだから、CIAが傍受しているのではないですか?

徳丸: CIAではなく、国家安全保障局(NSA)ですが、政府関係者ならともかく、民間のメールの中身を一々確認するほど彼らも暇ではない気がしますが、心配であれば、Yahoo!ジャパンなど、日本のサービスで2段階認証に対応しているものもありますよ。

高橋: まぁ、そこはよく考えます。

徳丸: あと、メールに関しては、元々盗聴のリスクがあるものなので、機密性の高いメールはPGPなどで暗号化するほうがよいでしょうね。

高橋: 分かりました。その他のサイトはどうなんでしょうか?

徳丸: オンラインバンキング等も含めて、短期的に被害を受けるサイトは、パスワードの定期的変更では間に合わないので、他の施策をとるのがいいですね。ジャパンネット銀行と三井住友銀行は無料でワンタイムトークンを配っていますし、有料でトークンを配布している銀行もあります。パスワードリスト攻撃の被害の多いネットゲームも、2段階認証に対応しているサイトがあります。

高橋: わかりました。では、そろそろ今回のまとめをいただけますか?

徳丸: はい。先に述べたように、利用者側で警戒が特に必要な状況は、長期にわたって情報が漏洩し続ける事態ですから、そのような秘密情報を預けるサイトはむやみに増やさず、2段階認証に対応した安全性の高いサイトに絞るのがよいでしょう。これができれば、パスワードの定期的変更をすべき理由はなくなります。

高橋: その他のサイトはどうでしょうか?

徳丸: 秘密情報の有無に関わらず、攻撃者にとって金銭的価値の高いサイト、オンライン銀行やクレジットカードを扱うECサイト等は、短期集中的に狙われやすいので、パスワードの定期的変更は意味がありませんが、それ以外で、パスワードの文字数が多く設定できるとか、パスワードリセットの機能がちゃんとしているなど、セキュリティ施策のしっかりしているサイトを選んだ方がいいですね。

高橋: ちょっと素人には難しい判断ですね。

徳丸: 機会があれば別の記事に書きましょうかね。

高橋: 結局、パスワードの定期的変更は意味がないという結論なんですか?

徳丸: そう言いたいところですが、理論的には、次の条件を満たすサイトを使わないといけない場合は、パスワードの定期的変更が効果がなくはない、ということになります。

高橋: 微妙な言い方ですね。どのような条件ですか?

徳丸: はい。箇条書きにしますね。以下の3条件を満たす場合、パスワードの定期的変更が意味がある可能性があります。

  • 長期に情報が漏洩し続けると被害の拡大するメール、メッセージング、ストレージなどのサービスである
  • 2段階認証、リスクベース認証、ログイン履歴確認画面などのセキュリティ施策がない
  • 情報漏洩があっても、サイトからアナウンスされない可能性がある or フィッシング被害にあう心配がある

高橋: フィッシングは利用者の責任ですが、他は残念な感じがしますね。しかし、このようなサイトを使う場合は、パスワードの定期的変更で安全性が高まるのですか?

徳丸: いや、攻撃を受けると、過去のデータは全て漏洩した上で、次のパスワード変更までは情報が漏洩し続けます。

高橋: 次回の「定期的変更」で漏洩が止まる、と。

徳丸: いや、それも確実なものではありません。

高橋: あっ、そうなですか?

徳丸: はい。情報が漏洩してもサイトからアナウンスがないということは、サイト側も気づかない「完全犯罪」の可能性が高いわけです。この場合は、攻撃者が再度攻撃すると、パスワードを変更していても防御できません。

高橋: なんか、残念な上に、パスワードを変更しても、攻撃を断ち切れる訳ではないのですか…

徳丸: それが現実です。フィッシングの方はパスワードの変更で情報流出が確実に止まりますが、フィッシング対策という点では、2段階認証なら事後対策だけでなく、侵入も止められるわけですから、2段階認証が使えるならぜひ使うべきです。

高橋: わかりました。ところで、今まで、住所、氏名、メールアドレス、電話番号などの個人情報の話が出てこなかったと思いますが、これらはどうですか?

徳丸: 個人情報は侵入された時点で漏洩しているはずですので、諦めてください。パスワードの定期的変更による被害緩和は、新しい情報までもが漏洩し続けないようにする話です。

高橋: つらいですね(>_<) では、最後に、利用者のとるべきセキュリティ施策についてまとめていただけますか?

徳丸: はい、これも箇条書きにしましょう。

  • パスワードはできれば12文字以上で、できるだけ長く設定する
  • パスワードは他のサイトとは別のものに設定する(パスワードリスト攻撃対策)
  • 秘密情報を扱うサービスは、少数に絞り、以下のセキュリティ施策のあるサイトを可能な限り選択する
    • 2段階認証に対応している(強く推奨)
    • リスクベース認証、ログイン履歴の確認画面(推奨)
    • 長いパスワードを設定できること(12文字以上推奨、長いほど安全)
  • 秘密情報を扱うサービスは、定期的にログイン履歴を確認する
  • サイトやWebニュースなどで、サイトのパスワード変更の案内が来た場合は、すみやかにパスワードを変更する
  • マルウェア対策として以下を実施する
    • 端末のOSやソフトウェアを常に最新の状態に保つ
    • ウイルス対策ソフトを導入してパターンファイルを最新に保つ
    • ブラウザのプラグインやアドオンは最低現に絞り最新に保つ

高橋: ありがとうございました。これで、パスワードの定期的変更に関する徳丸さんへのインタビューは終わりです。みなさま、ごきげんよう~


※注: ここに登場する人物はすべて架空の人物です。文責は徳丸浩にあります。

フォロワー

ブログ アーカイブ