タグ: セッション管理

PHP:既知のセキュリティ脆弱性 – Session Adoption

追記:より新しい情報については間違いだらけのHTTPセッション管理とその対策をどうぞ。

PHPには広く知られているにも関わらず放置されている既知のセキュリティ脆弱性が幾つかあります。その一つがセッションモジュールのセッションアダプション(Session Adoption)脆弱性です。この脆弱性は現在広く利用されているWebアプリケーションの安全性に、非常に大きな影響を与える脆弱性です。

セッションアダプション脆弱性とはセッション固定化攻撃を可能とする脆弱性の一種です。セッションアダプションに脆弱なセッション管理システムは、ユーザ(ブラウザ)が送信してきた未初期化のセッションIDを受け入れ、セッションを初期化してしまいます。PHPに限らず、RailsやJavaのフレームワーク等、多くのWebフレームワークに発見されている脆弱性です。

セッションアダプション脆弱性を利用すると、攻撃者は長期間に渡って他人のIDを使う成りすましが可能になる場合があります。IDを盗みたい犯罪者には利用価値の高い脆弱性です。

この脆弱性は、10年以上前から問題視されている国別TLD(ccTLD)の属性ドメイン(国別Top Level Domain, co.jp, or.jpなどのドメイン)にも下位ドメインからクッキーが設定でき、サブドメインのクッキー設定が無視される(送信順序、優先順位がいい加減)、というとんでも無いクッキーの仕様と組み合わせると、大量の他人のユーザセッションIDを取得(設定)し、成りすます事ができます。

セッションアダプション問題は全てのPHP、PHP 4.4.9/PHP 5.2.8/PHP 5.3/PHP 6.0に共通する脆弱性です。

最近、影響範囲はかなり狭くなりましたが、まだまだ注意が必要な脆弱性であり、根本的な解決にはパッチと適切なセキュリティ対策が必要です。

もっと読む

PHPのSessionモジュールの脆弱性

たまたま目に止まったブログがあるので紹介します。

PHP:session_set_save_handlerリファレンスマニュアルのサンプルにパス・トラバーサル脆弱性
http://www.tokumaru.org/d/20080818.html#p01
[php]session_set_save_handlerのパストラバーサルで任意コマンドの実行が可能
http://www.tokumaru.org/d/20080819.html#p01

この危険性はStrict Sessionパッチが作られた頃にも議論されていました。このパッチを摘要するとセッションアダプション脆弱性も修正します。もし、互換性なので要件で厳格なSession管理ができない場合でもセッションIDに利用可能な文字は安全な文字のみに限定されるのでパストラバーサルなどの問題は発生しません。

話は横道にそれますが、セッションアダプションの問題については、PHP 4.4.9リリース前にPHP Security Response Teamと議論しました。リリースノートを見れば解りますが、残念ながら良い結果ではありませんでした。いくつかの脆弱性をレポートしましたが、その内の一部の脆弱性のみに対応しただけです。これについては時間が出来次第エントリを書きます。

話を戻しますが、セッションモジュールの問題は随分前(何年も前)に議論されており、開発者のスタンスとしては「ユーザ側の問題」「ファイルストレージがあるのに態々自分作って自分の足を打つようなユーザが問題」「RDBMSをストレージに使ってエスケープしないでSQLインジェクションされるのと同じ」といった結論になってしまったと記憶しています。

セッションアダプションの問題を解決すれば、パストラバーサルの問題もSQLインジェクション問題も解決するのですが…

セッションアダプション脆弱性はリスクが不当に低く評価され過ぎです。PHPを利用されている方全てにStrict Sessionパッチをお勧めします。

PHP 4.4.8用のStrict Sessionパッチ

桝形さんから

http://blog.ohgaki.net/php-5-2-strict-session 

で公開したパッチのPHP4.4.8版を送っていただきました。私のWikiにも添付ファイルとして掲載させていただきました。

http://d.hatena.ne.jp/masugata/20080714#p2

に掲載されているパッチと同じパッチです。

私は全てのPHPユーザがStrict Sessionパッチ適用すべきと考えています。詳しくはWikiをご覧ください。

http://wiki.ohgaki.net/index.php?PHP%2Fpatch%2FStrictSession

桝形さん、パッチありがとうございました。

PHPセッションの問題修正

Stefanさんのブログに書いてあったので気がついたのですが

http://cvs.php.net/viewvc.cgi/php-src/ext/session/session.c?r1=1.417.2.8.2.35&r2=1.417.2.8.2.36

に結構面白いコミットがされています。CVS版なので正式リリースになるかは不明です。成り行きはがどうなるかは興味深いです。

MOPBで解説されていたセッションIDにインジェクションができる問題を修正しようとブラックリスティングで対応させようとしたようです。ブラックリスティングより暗号化した方が安全性と互換性を確保するのが簡単なのですがZend社の社員によるコミットだったのですがZend Platformとの互換性も維持できないパッチになっているようです。Stefanさんのブログを見るとかなり皮肉な書き方であることが分かります。このような文面になった背景にはZend社の社員により「PHP開発者に知らせず脆弱性を公開した」とあらぬ言いがかりを付けられためだと思います。

非常に古いPHPの場合、セッションIDには好みの文字列が設定できたのですが最近のPHPは

\r\n\t <>’\”\\

が不正な文字として登録されています。\r\nはヘッダスプリティング、<>'”\はXSSに明らかに利用できるので不正としても仕方ない文字ですが、

()@,;:[]?={}&%

も新たに不正な文字に加えられたました。:はZend Platformでも使っている文字だそうです。
備考: セッションIDに管理用の文字列を付け加えるのは珍しいことではありません。負荷分散や分散されたサーバ間でのデータ共有など様々な用途に管理用の文字列を付け加える場合があります。

暗号化による対処の方がシンプルかつ安全な対策だと思います…

ところで、単純にクッキーだけでセッションIDを使っている場合には問題は発生しません。つまりsession.use_trans_sid=0, session.use_only_cookies=1でPHPを使っていれば現在のPHPを使っていてもここで問題としている不具合には影響されません。

ログイン後にsession_regenerate_id()を実行するだけで十分か?

忙し過ぎてタイムリーにブログが書けないです。最近セッション管理の問題が一部で話題になっていました。そこの中に以下のような議論がありました。

ログイン後にsession_regenerate_id()を実行すれば外部からのセッションIDを受け入れても安全

確かにログイン後のセッションIDは本来セッションIDが持つべき属性

  •  一意な値であること
  • 第三者に予測不可能であること

を持っています。

しかし、ログイン後にセッションIDを再生成するだけでは不十分な場合は2つ直ぐに思いつきます。

– CSRF(XSRF)防御にセッションID(だけ)を利用している場合
– 外部に出力したデータの改ざん防止にセッションID(だけ)を利用している場合

これらの仕組みはログイン後にのみ利用する機能ではありません。フォーム送信は認証無しで行うことは多いです。ウィザード型の入力フォーム(検証済みの前のページの入力値を次ページに保存する方法が最も柔軟な処理方法)も認証前に使用されます。

私はCSRF(XSRF)防御にはフォームに予測不可な一意なIDを割り当てる方式を、検証済みデータの改ざん防止にはセッションID+マジック文字列(予測できない秘密の文字列)を利用しています。CSRF(XSRF)対策には影響ないですし、セッションIDを利用してセキュリティを維持する仕組みであっても必ずマジック文字列を使用しているので危険な状態にはなりません。しかし、全てのアプリケーションがこのような対策を取っているとは思えないので外部からのセッションIDを受け付けない厳格なセッションID管理を導入するのはセキュリティ上意味があります。

ほとんどのアプリケーションはログイン後にsession_regenerat_id()を実行するだけで十分な安全性を確保することが可能ですが一部のアプリケーションはそれでは不十分であることは知っておいたほうが良いと思います。

追記:2001年からウィザード型フォームで前のページのデータを安全に保存するため等に利用できる関数をZendのコードギャラリに載せています。ウィザード型フォームはこのような関数を使用し検証済みの値のメッセージダイジェストを取っておけと繰り返し入力値を検証する必要性がなくなります。

解答:まちがった自動ログイン処理

問題:まちがった自動ログイン処理の解答です。このブログエントリは最近作られたアプリケーションでは「問題」にしたような実装は行われていないはず、と期待していたのですがあっさり期待を破られたのでブログに書きました。このブログの方が詳しく書いていますけが「Webアプリセキュリティ対策入門」にも正しい自動ログイン処理を書いています。

参考:自動ログイン以外に2要素認証も重要です。「今すぐできる、Webサイトへの2要素認証導入」こちらもどうぞ。HMACを利用した安全なAPIキーの送受信も参考にどうぞ。

もっと読む

PHPのStrictセッション

Strictセッション管理パッチのダウンロード数がやっと?100を超えました。

PHP5用のパッチなのが一番の問題なのでしょうか?非常にダウンロード数が少ないように思えます。私はこのパッチはセキュリティ上かなり重要なパッチだと思っています。

PHP4のパッチが必要なのかな? 枡形さんが作ってましたっけ?

関連:
http://blog.ohgaki.net/index.php/yohgaki/2006/02/02/strict_sessioncric_a_a_a
http://blog.ohgaki.net/index.php/yohgaki/2006/02/05/phpa_rsession_fixationa_ei
http://blog.ohgaki.net/index.php/yohgaki/2006/02/27/a_ma_sa_sa_ma_ca_a_ma_a_sa_f

Strict Session管理パッチ

PHPのセッションID管理がいまひとつであることは

http://blog.ohgaki.net/index.php/yohgaki/2005/12/24/strict_sessioncric

にも書きました。PHP 5.1.2用のパッチですがsqliteと一緒にコンパイルするとsqlite用のvalidationパッチが含まれていないのでビルドできませんでした。

MomongaLinux用にsqliteも一緒にビルドできるパッチを作成したのでWikiに載せました。もちろんSQLiteをセッションID管理に使用しても厳格なセッションID管理になります。必要な方は是非どうぞ。

http://wiki.ohgaki.net/index.php?PHP%2Fpatch%2FStrictSession

追記:
厳格なセッションID管理を行うパッチを適用したPHPをインストールした後、php.iniに

session.use_strict_mode = 1

を追加してください。これを追加しないと厳格なセッションID管理になりません。

Strict Session管理パッチ

Hardedned PHPプロジェクトのStefan EsserさんがPHP SESSIONをより安全に運用するパッチを公開しています。

このパッチはPHPのセッション管理の問題に対応します。PHPのSESSIONモジュールがSession Fixation(セッションIDの固定化)に対して脆弱であることは随分前から広く(?)知られていました。このパッチを適用すればSession Fixation問題も随分改善します。

例えば、PHP SESSIONを利用しているサイトで

http://example.jp/index.php

と言うURLがクッキーを利用したセッション管理行っているとします。セッションデータが初期化されサーバに保存されているか、されていないかに関わらず、ブラウザが送信したクッキーによって指定されたセッションIDがそのまま使われます。このパッチはセッションIDのデータが無ければ、セッションIDを再生成してセッションを開始するよう動作が変わります。(通常のPHPのセッション管理はpermissiveなセッション管理と呼ばれています。パッチ適用後はstrictなセッション管理も選択できるようになります。)クッキーだけを使ったセッション管理では、セッションIDが固定化する問題はあまり深刻な問題ではありませんが、好ましい動作とは言いがたいです。

session.use_only_cookiesがoffの場合、問題は深刻です。

http://example.jp/index.php?PHPSESSID=123456

とすると、セッションデータが無い場合、セッションIDが123456になってしまいます。session.use_cookies=on設定(デフォルト値)でクッキーを利用するセッション管理でも、PHPSESSID=123456をクッキーに設定されます。つまり、デフォルトの状態のPHPのセッション管理はsession.use_only_cookiesがoffである為、普通にSession Fixationに脆弱になってしまいます。一応、サイト外のURLに埋め込まれたセッションIDを受け付けないようにするsession.referer_check設定もあるのですが、REFERERを使っているので十分な対策とはいえません。許可するドメインなどを記載する設定項目なので、当然ですが、デフォルトでは何も設定されていません。何も設定されていない時はREFERERチェックは行われません。

追記:色々書いていますが、要するにデフォルト設定のPHPだとSession Fixationに脆弱である、と言うことです。

より厳格なセッションID管理を行うには、パッチを適用後、

session.use_strict_mode = on

と設定するだけです。

このパッチを適用するとユーザ定義セッションセーブハンドラで以下の2つの関数が利用(定義)できるようになります。

string create_sid()
bool validate_sid($key)

create_sid関数は新しいセッションIDを作成します。
validate_sid関数は$keyが正しいかチェックします。

ところで、デフォルトではsession.use_only_cookiesはoffに設定されています。しかし、普通のサイトはsession.use_only_cookiesはONで運用するべきです。もし、offで運用されている場合、強くONに変更して運用することをお勧めします。先ほども書きましたが、session.use_only_cookies=onであればSession Fixation問題はそれほど重大な問題とは言えません。パッチを適用していない場合でもsession.use_only_cookies=onであればそれほど心配する必要はありません。

# 随分古いInternet ExplorerではXMLHttpRequestで他のサイトにリクエスト
# を送信できたりしました。しかし、今は出来ないのでクッキーを利用した
# セッション管理に対して効果的なSession Fixation攻撃は出来ないと思い
# ます。

session_regenerate_idの使い方に注意!

久しぶりにPHP-users MLを見るとsession_regenerate_id関数が「古いセッションIDに関連したセッション情報を削除しないのは困るよね」と投稿がありました。

バグレポートではBogus(バグじゃない)というステータスになっていました。確かにsession_regenerate_id関数は新しいセッションIDを生成する為の関数として追加されました。新しいセッションIDを作れば仕様上は問題ない、と主張できるかもしれません。しかし、アプリケーションの作り方によってはセキュリティ上の問題を発生させる原因になるので変更するべきですね。

セッションモジュール、セーブハンドラモジュール、シリアライザモジュールの各グローバル変数がマクロでアクセスされる作りになっています。(セッションモジュールは更にサブモジュールと持つ構造になっています。随分前ですがWeb+DB Pressに書いた通りです。興味のある方はどうぞ。)さらに引数などもマクロで定義されているため、初めて読むプログラマには非常に分かりづらいソースになっています。

PHPセッションモジュールのソースを読んだ事がある方なら分かりますが、グローバル変数を使用している、今回の場合はPSマクロで利用するグローバル変数、のでセッションID、PS(id)、を保存しないとならないように思えます。
# 今思いつきましたが、PS(data)にempty_stringを入れて
# おけばOKなような気がします。

session_destory関数にはバグがあるようでセッション情報は空になりますが、ファイルをunlinkしたいようなコードになっているのですが…、unlinkされていません。XFSなど、ディレクトリエントリにB-Treeを使用しているファイルシステムでは大量のファイルがあっても性能上や使用上問題ありません。個人的には50万ファイル(50万セッション)までファイル作成してベンチマークしてみましたが、性能は全く変わりませんでした。

ext3を利用している場合、2の問題で困る可能性があります。一つはinodeの枯渇ともう一つパフォーマンスの低下です。デフォルトではinodeが枯渇する可能性は大きいと思います。DoSの原因にもなるので注意しましょう。Linuxの場合、ディレクトリエントリはキャッシュされているので多数のファイルがある場合でもかなり良い性能ですが、それでも何十万ファイルレベルになると遅くなります。

セッション管理は有効期限を0に設定したセッションクッキー(ブラウザの終了と同時に削除されるクッキー)を利用し、自動再ログインなどの仕組みはアプリケーションでセキュリティ上の問題が許容される取り扱い方法を独自に実装する方が良い、と考えています。

もし有効期限が長いセッションIDを利用してログイン状態を保ち、セッションIDの再生成でXSSのリスクを軽減されている場合はsession_destroyを使用してセッションの削除を忘れないようにしなければなりません。ご注意ください。