2013年2月23日土曜日

自己流のSQLインジェクション対策は危険

SQLインジェクションについて解説したブログ記事を読んだところ、ユニークな対策方法が紹介されていました。この対策方法の問題点について解説し、正しい脆弱性対処の重要性を説明します。

ユニークなSQLインジェクション対策


SQLインジェクションのブログ記事を読んでいて、対策として以下のスクリプトが紹介されていました。
$_GET[id]=strip_tags(htmlspecialchars($_GET[id]));
$_GET[id]=preg_replace(array(‘/[~;\'\"]/’,'/–/’),”,$_GET[id]);
現在は当該のスクリプトは削除されているので、当該エントリは参照せず、「昔々あるところにこのようなSQLインジェクション脆弱性対処スクリプトがあった」という想定で以下の議論を進めます。

問題点の説明

このスクリプトはいくつかの問題点があります。
  • $_GETを直接変更していて汎用性が乏しい
  • idをクォートしていないなどいくつかの文法エラー
  • 表示前提でないのにHTMLエスケープしている
  • HTMLエスケープした後strip_tagsを呼んでいるが、この段階ではタグはエスケープ済みなので、strip_tagsは何もしない
  • 「危険な文字」を削除するサニタイジング手法を用いている
このままでは動かすことができないので、以下のようにsanitize関数として少し修正したものを紹介します。
function sanitize($str) {
  $str = strip_tags(htmlspecialchars($str));
  return preg_replace(array('/[~;\'\"]/', '/-/'), '', $str);
}
この関数に、さまざまな文字を通した結果を下表に紹介します。

入力文字htmlspecialcharsstrip_tagspreg_replace(最終)
AAAA
5555
<&lt;&lt;&lt
>&gt;&gt;&gt
&&amp;&amp;&amp
"&quot;&quot;&quot
'''(空)
\\\\
;;;(空)
---(空)
~~~(空)
####

HTMLエスケープの対象となる < > & " の4文字は、文字実体参照に変換された後、preg_replace関数でセミコロンを削除してしまうので、中途半端な妙な文字化けになりそうです。
一般的な原則としては、データベースにはHTMLの形ではなくプレーンテキストの形で保存しておき、HTMLとして表示する直前にHTMLエスケープする方法で統一することで、上記のような文字化けやエスケープ漏れをなくすことがよいでしょう。

脆弱性はないのか

このsanitize関数に脆弱性はないでしょうか。上表のように、バックスラッシュ(円記号)を素通ししているので、MySQLや、設定によってはPostgreSQLの場合に、問題が生じそうです。以下、それを説明します。以下の説明では、MySQLを使う想定とします。
以下のように、ログイン処理を想定したSQL文組立があったとします。
$sql = sprintf("SELECT * FROM users WHERE id='%s' AND pwd='%s'", sanitize($id), sanitize($pwd));
ここで、$idと$pwdが以下だったとします。
$id = '\\';   // \ 1文字
$pwd = 'password';
$sqlは下記となります。
SELECT * FROM users WHERE id='\' AND pwd='password'
ここで、id=の後の文字列リテラルはどこまででしょうか。\'は「シングルクォートを\でエスケープしたもの」、すなわちリテラル内の文字と解釈されるので、その次のシングルクォートまでが文字列リテラルになります。つまり、 AND pwd=まで(黄色でマークした部分)が文字列リテラルと解釈されます。その後の password' は「文字列リテラルの外」、すなわちSQL文の構文と解釈されますが、SQL文として正しくないので、このSQL文は構文エラーになります。
それでは、passwordのところをSQL文として正しくなるように変更したらどうでしょうか。これがSQLインジェクション攻撃です。その例を以下に示します。
$id = '\\';   // \ 1文字
$pwd = ' OR 1=1#';
組み立てられたSQL文は以下となります。
SELECT * FROM users WHERE id='\' AND pwd=' OR 1=1#'
passwordの代わりに、OR 1=1(青くマークした部分)が正しいSQL文(の一部)となります。#以降はMySQLのコメントとして無視されます。すなわち、これで認証回避が可能となりました。

「自己流の脆弱性対策」の危険性

以上に説明したように、htmlspecialchars、strip_tags、preg_replaceの三種類の関数を使った「独自の」でSQLインジェクション対策には抜けがあり、SQLインジェクション攻撃を許してしまう可能性があることがわかりました。
SQLインジェクションを含めて、脆弱性対処の方法論は、世界中の専門家がさまざまな観点から研究しています。SQLインジェクション対策に関しては、数年前から「究極形」がわかっており、特別な事情のある場合を除いて、わざわざ別の方法を用いる必要はないでしょう。特別な場合とは以下の様なケースです。
  • データベースアクセスを含むフレームワークを新たに開発する場合
  • O/Rマッパーを自作する場合
  • phpMyAdminのようなデータベース管理ツールを自作する場合
大半の開発者は、上記を作らないでしょう。だから、以下に示す、SQLインジェクション対策の「究極形」に従うことを強く推奨します。それは下記のとおりです。
  • 文字列連結(sprintf等を含む)を用いてSQL文を組み立てない
  • 静的プレースホルダを用いてSQL文にパラメータを割り当てる
  • データベース接続時に文字エンコーディングを指定する
上記の理論的な根拠については、IPAの「安全なSQLの呼び出し方」を参照下さい。また、PHP+PDOを用いた実際的なSQLアクセスの方法については、「PDOにおける一応の安全宣言と残る問題点」を参照下さい。
また、SQLインジェクション対策の解説をこれから書こうとする人には、ぜひajiyoshiさんの素晴らしいブログ記事「Webアプリケーションとかの入門本みたいのを書く人への心からのお願い。」をお読み下さい。このような文章が、現場の開発者から出てくることは本当に素晴らしいことです。

まとめ

自己流の脆弱性対処の危険性について、具体例を用いて説明しました。SQLインジェクションは、非常に危険な脆弱性ですが、完全な対処の仕方が分かっていて、正しいプログラミングをする限り恐れる必要はありません。ここで紹介した解説資料を参照して、あなたのWebサイトを安全にしてください。



2013年2月15日金曜日

ログアウト機能の目的と実現方法

このエントリでは、Webアプリケーションにおけるログアウト機能に関連して、その目的と実現方法について説明します。

議論の前提

このエントリでは、認証方式として、いわゆるフォーム認証を前提としています。フォーム認証は俗な言い方かもしれませんが、HTMLフォームでIDとパスワードの入力フォームを作成し、その入力値をアプリケーション側で検証する認証方式のことです。IDとパスワードの入力は最初の1回ですませたいため、通常はCookieを用いて認証状態を保持します。ログアウト機能とは、保持された認証状態を破棄して、認証していない状態に戻すことです。

Cookieを用いた認証状態保持

前述のように、認証状態の保持にはCookieを用いることが一般的ですが、Cookieに auth=1 とか、userid=tokumaru などのように、ログイン状態を「そのまま」Cookieに保持すると脆弱性になります。これについては、以前のエントリ「CookieにログインIDを保存してはいけない」にて説明しました。
このため、通常はセッション変数にログイン状態を記録し、CookieにはセッションIDのみを保存します。セッション変数であれば、外部から書き換えることは不可能なので、Cookieに生の値を入れる方法に比べて安全です。

ログアウト機能は本当に必要なのか?

ここで、Webアプリケーションにおいて、本当にログアウト機能が必要なのかを検討します。
仮に、ログイン機能のあるWebアプリケーションにログアウト機能がない、あるいはログアウト機能が不完全である場合、脆弱性診断業者は、この状態を脆弱性と指摘しますが、通常は「低危険度の脆弱性」と判定すると思います。なぜ、低危険度なのでしょうか。

実は私達は、ログアウト機能のない認証方式を知っています。それはHTTP認証(BASIC認証やダイジェスト認証の総称)です。HTTP認証の場合、サーバー側で認証状態は保持せずにリクエスト毎に認証するので、そもそもログアウトという概念がない(認証状態を保存しないので破棄の必要がない)のですが、利用者が入力したIDとパスワードをブラウザが一時的に保存するため、外見上は「ログイン状態が保持されている」ように見えます。
HTTP認証はログアウトができないので不便だという意見はよく目にしますし、それを克服するため、401応答ヘッダを返すことでHTTP認証のログアウトを実現するという例も見かけます。しかし、一般的には、HTTP認証はログアウトができない(ブラウザがIDとパスワードを保持したままになる)ので、以下のような「運用で対処」している場合が多いでしょう。

  • ブラウザを終了する(HTTP認証のID・パスワードは消える)
  • ブラウザの機能によりHTTP認証のID・パスワードを消去する

ブラウザによる認証情報消去機能の例として、Firefoxの画面を以下に紹介します。以下は「最近の履歴を消去」メニューにより表示されるダイアログです。


ここで「現在のログイン情報」にチェックして「今すく消去」ボタンをクリックすると、HTTP認証のIDとパスワードが消去され、見かけ上はログアウトしたように見えます。
これと類似のことは、フォーム認証でもできます。セッションCookie(Expires指定のないCookie)はブラウザの終了と共に消えますし、ブラウザの機能によりCookieを削除することもできます。すると、利用者からは、認証していない状態、すなわちログアウトした状態に見えます。

以上のように、我々は「ログアウト機能のない世界」を知っており、一応それを受け入れていることになります。もしもHTTP認証にログアウト機能(アプリケーションからHTTP認証情報を破棄する機能)がないことが本質的に危険であれば、たとえばJavaScriptの機能としてHTTP認証情報を破棄するメソッドが実装されてもよさそうなものですが、そのような機能はありません。

HTTP認証とフォーム認証では、状態保持の場所が違う

ここで、HTTP認証とフォーム認証では、状態保持の場所が異なることを確認します。
HTTP認証は、認証状態を保持しておらず、リクエスト毎に認証処理が動きますが、利便性のため、一度入力したIDとパスワードはブラウザがキャッシュしています。
一方、フォーム認証の一般的な実装では、認証結果をセッション変数に記憶します。セッション変数はサーバー側で保存する場合が多いので、結果として、認証状態はサーバー側で保持していることになります。以上の内容を下表に整理します。

保持する内容 保存場所
BASIC認証 IDとパスワード ブラウザ
フォーム認証 認証結果 サーバー

すなわち、フォーム認証の場合、セッションIDのクッキーを消しただけでは、サーバー側に認証状態は残ったままということになります。

ログアウト機能がないと困ること

先に、HTTP認証ではアプリケーション側でのログアウト機能が一般的でなく、我々はそれを受け入れていると説明しましたが、フォーム認証も含め、ログアウト機能がないことで困ることを検討します。

まず問題になるのは、共有PCを使っている場合や、離席中のパソコンを勝手に使われるケースです。この場合、ログインしたままのアプリケーションがあれば、それを勝手に使われ、なりすましができてしまいます。

これに対しては、以下の回避策があります。
  • 共有PCでは認証機能のあるアプリケーションを使わない
  • 共有PC使用後はブラウザを閉じる
  • アプリケーションのセッションタイムアウト時間を短くする。セッションタイムアウトすると認証情報も破棄される
  • 離席中のPCを使われること自体が重大な問題なので、離席時にはPCをロックする
  • 同じく、離席中はノートPCを持ち歩く
次に検討するのは、クロスサイト・スクリプティング(XSS)やクロスサイト・リクエストフォージェリ(CSRF)の回避策としてのログアウト機能です。これら受動的攻撃は、ログアウト状態であれば、セッションハイジャックやCSRFによる重要な機能の悪用は避けることができます。

最後に、仮に通信路上でセッションIDが盗聴された場合でも、ログアウトしてしまえば、それ以降の悪用を避けることができます。セッションIDが盗聴されるシナリオでは、ログイン時のIDとパスワードも盗聴されるとも考えられますが、以下のようなサイトであれば、「IDとパスワードは盗聴されないが、セッションIDは盗聴される」という状況は起こりえます。
  • 認証フォームはSSLでIDとパスワードを暗号化して送信している
  • 認証後のページは平文のHTTPであり、セッションIDも平文送信される
このようなサイトの場合、利用者がログアウトすると、その後のセッションハイジャックは止めることができます。

ログアウト機能の効用のまとめ

以上に見てきたように、ログアウト機能は本質的に何かを解決する機能ではありません。何か悪いことが起こった際に、被害を軽減するものでしかありません。下表に、ログアウト機能による被害権限の内容と、対応する根本的解説策をまとめました。

ログアウトによる被害軽減 根本的解決策
離席中のなりすましの防止 離席中はPCをロックする、あるいはノートPCを持って移動する
共有PCによるなりすましの防止 共有PCではログインしない、あるいは利用後にブラウザを閉じる
XSS被害の軽減 XSS脆弱性をなくす
CSRF被害の軽減 CSRF脆弱性をなくす
セッションID盗聴の被害軽減 サイト全体をSSLで暗号化する

このため、HTTP認証でログアウト機能がないことも、「不便だが本質的ではないので許容しよう」ということになっているのでしょう。

ログアウト機能の実装法

ここからは、フォーム認証におけるログアウト機能の実装方法について説明します。
認証状態はセッション変数に保持されているので、以下のいずれかにより、確実に認証状態を破棄することができます。
  • セッション自体を破棄する
  • 認証状態を保持するセッション変数に「ログインしていない」ことを示す値を代入する
PHPの場合、セッションの破棄は以下で実現出来ます。
session_destroy();
しかし、このままだとセッション変数の値までは破棄されません。このため、ログアウト処理のページで、ログアウト機能以外にも実行する場合は、以下のようにセッション変数の破棄も行うと良いでしょう。
session_start();
$_SESSION = array();
session_destroy();
個人的には、ログアウトページでログアウト以外のことをするシナリオは想像がつきませんが、絶対にないとは言い切れないので、保険的にセッション変数の破棄もやっておくとよいということです。

また、ショッピングサイトなどで、ログアウトはするがセッションは破棄したくないという場合も有り得ます。その場合は、下記のように、認証状態のみをクリアします。
$_SESSION['userid'] = false;

セッションIDのCookieを破棄する必要はあるか

PHPのマニュアルのsession_destroy()関数の説明には、ログアウト時にはセッションIDのクッキーを削除しなければならない(原文は"the session cookie must be deleted")とあります。
セッション ID の受け渡しに クッキーが使用されている場合(デフォルト)には、セッションクッキーも 削除されなければなりません。
http://www.php.net/manual/ja/function.session-destroy.phpより引用
しかし、私は、セッションIDのクッキー(PHPのデフォルトはPHPSESSID)は削除する必要はないと考えます。その理由は、セッションが破棄された状態では、セッションIDは単なる乱数であり、攻撃の余地はないからです。

これはホテルのカードキーに例えることができます。最近のホテルはカードキーを採用するところが増えていますが、カードキーにも二種類あり、部屋ごとのカードを使いまわす場合と、チェックアウトのたびにカードキーをリセットして、別のカードキーを使う場合があります。
後者の場合、チェックアウト済みのカードキーには価値がないので、顧客が記念に持ち帰ってもよいところも多いですね。
セッション変数はホテルの部屋に例えることができます。部屋にアクセスするにはカードキー(セッションID)が必要です。顧客は滞在中カードキーさえあればいつでも部屋(セッション変数)にアクセスできますが、チェックアウト(ログアウト)すると、部屋にアクセスできなくなります。入室できないカードキーには意味が無いので、持ち帰っても差し支えないというわけです。

セッションIDには、記念品としての意味はありませんが、認証状態を破棄するという意味においては、ブラウザ側に残っていても不具合はありません。これが、「ログアウト時にセッションIDのCookieを破棄する必要はない」という根拠になります。

セッションIDのCookieを削除したい理由

とはいえ、セッションIDのCookieを削除してはいけない理由もないので、場合によっては削除してもよいでしょう。セッションIDのCookieを削除したくなる理由をいくつか考えてみました。他にもあればご指摘下さい。

  • 不要なものが出続けるのはなんとなく気持ち悪い
  • リクエストヘッダが不要なCookieのサイズ分大きくなるのは非効率である
  • プライバシーポリシーにおいて、Cookieはログイン中のみ使用すると約束している
  • ログアウト後もCookieが送信されることにより、トラッキング等の「あらぬ疑い」をかけられたくない

私は、セキュリティコンサルタントとしては珍しく(?)、余計なこと(保険的対策としても効果の薄いもの)をするのは嫌いなので、「Cookieが残るのは許容してもいい」という意見ですが、Cookieを削除すべきでないとまでは思いません。

脆弱性診断の実務として、ログアウト後にセッションIDのCookieが残ることを指摘する業者はあるかもしれませんが、指摘したとしても「情報」(脆弱性とまでは言えないが一応ご報告)というレベルだと思います。


まとめ

  • ログアウト機能の目的は、セッションハイジャックなどに対する保険的対策である
  • ログアウト機能の実現方法は、セッションを破棄するか、認証状態を保持するセッション変数をクリアすることである
  • ログアウト時にセッションIDのCookieを削除する必要はないが、別の理由があれば削除してもよい

2013年1月29日火曜日

IPAから『DOM Based XSS』に関するレポート出ました

DOM Based XSSに関しては、IPA「安全なウェブサイトの作り方」でも、拙著でもごく簡単にしか触れておらず、まとまった解説が要望されていましたが、本日(2013年1月29日)にIPAから「IPA テクニカルウォッチ『DOM Based XSS』に関するレポート」が公開されました。
同冊子の目次は下記の通りです。
はじめに
1. DOM Based XSSの概要
2. IPAに報告されたDOM Based XSSの脆弱性
3. DOM Based XSSの事例
4. DOM Based XSSの対策方法
コラム
おわりに
IPAのレポートと言うことで、DOM based XSSの届出の状況も説明されています。以下にグラフを引用します。


一見して「急増」していることと、昨年の10月から12月の3ヶ月で92件も届出があったということで、対策が急務となっている状況が見て取れます。これは、この時期に「脆弱なサイトやアプリが増えた」ということでは恐らくなく、潜在的に脆弱な状況であったものが発見され、届出されたということだと理解しています。

1章では、XSSの種類や、DOMとは何かというところから説明されています。本書の対象読者は一応DOMについては知っているはず(べき)だとは思いますが、この機会に理解を整理していただくとその後のDOM based XSSの理解が容易になると思います。
2章は届出の概要、3章は届け出られた脆弱性の中から4例を脆弱なコードとともに紹介しています。やはり「脆弱なコードの現物」は貴重ですし、勉強になることが多いと思いました。
4章は対策をコード例とともに載せています。原則はDOM操作用のメソッドやプロパティを使うことですが、document.writeやinnerHTMLを使わざるを得ないケースについては、文脈に応じたエスケープについて説明しています。

さて、徳丸はIPA非常勤研究員として、同冊子のレビュアーという形で参画していますが、DOM based XSSがテーマだけに、「あー、○○さんにもレビューいただけたらなぁ」(○○さんは複数)と思うことしきりでした。それだけに、今回公開して終わりと言うことではなく、さまざまな方からの知見を集めて、より良いものにしていきたいと希望しております。

なお、このエントリは個人の見解として書いているものであり、IPAとしての見解ではないことを付記いたします。

2013年1月24日木曜日

実はそんなに怖くないTRACEメソッド

Cross-Site Tracing(XST)という化石のような攻撃手法があります。「化石」と書いたように、既に現実的な危険性はないのですが、XSTに関連して「TRACEメソッドは危険」というコメントを今でも見ることがあります。
このエントリでは、XSTという攻撃手法について説明し、XSTおよびTRACEメソッドについてどう考えればよいかを紹介します。

TRACEメソッドとは

HTTP 1.1(RFC2616)では、8種類のメソッドが定義されています。GET、POST、HEADなどはおなじみのものですが、それ以外にPUT、DELETE、OPTIONS、TRACE、CONNECTの5種があります。
このうち、TRACEメソッドは、HTTPリクエストを「オウム返しに」HTTPレスポンスとして返すもので、以下のようにGET等の代わりにTRACEとしてWebサーバーにリクエストします。
TRACE /auth/index.php HTTP/1.1
Host: example.jp
User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:18.0) Gecko/20100101 Firefox/18.0
Cookie: PHPSESSID=4lel0hml53u2tbhcd9pmo7pkc4
Authorization: Basic eWFtYWRhOnBhc3N3b3Jk
Connection: keep-alive
レスポンスの例を以下に示します。
HTTP/1.1 200 OK
Date: Tue, 22 Jan 2013 13:51:09 GMT
Server: Apache/2.2.14 (Ubuntu)
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: message/http
Content-Length: 198

TRACE /auth/index.php HTTP/1.1
Host: example.jp
User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:18.0) Gecko/20100101 Firefox/18.0
Cookie: PHPSESSID=4lel0hml53u2tbhcd9pmo7pkc4
Authorization: Basic eWFtYWRhOnBhc3N3b3Jk
Connection: keep-alive
レスポンスボディ(青字の部分)に注目すると、HTTPリクエストがそのまま返されていることが分かります。この中には、CookieヘッダやAuthorizationヘッダも含まれます。

Cross-Site Tracing(XST)攻撃とは

XST攻撃とは、XSSとTRACEメソッドを組み合わせた攻撃手法であり、2003年1月に発表されました
XSS単独では、XSSによりJavaScript等が動いているブラウザ上のレスポンス(ヘッダ及びボディ)は取得出来ますが、リクエストヘッダを取得することはできません。このため通常は、HttpOnly属性の指定されたCookieや、Authorizationヘッダ(Basic認証のIDとパスワード)を取得することはできません。
しかし、TRACEメソッドを実行すると、先に見たようにリクエストヘッダがそのままレスポンスとして返るので、XSS単独ではとることのできないHttpOnly属性の指定されたCookieや、Authorizationヘッダを盗み出すことができます。これがXST攻撃です。とくに、BASIC認証のIDとパスワードを盗むことができると、長期にわたって不正にログインすることができてしまうため、XSSの影響が大きくなります。

XST攻撃の実際

それでは、XST攻撃の実際を見てみましょう。以下はXST攻撃のコード例です。
<body>
<script type="text/javascript">
function sendTrace (trace) {
 try {
  var xhr = 0;
  if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
  } else {
     xhr = new ActiveXObject("Microsoft.XMLHTTP");
  }
  if (trace == 1) {
    xhr.open("TRACE", "/auth/", false);
  } else if (trace == 2) {
    xhr.open("\nTRACE", "/auth/", false);
  } else {
    xhr.open("GET", "/auth/", false);
  }
  xhr.send();
  res = xhr.responseText;
  alert(res);
 } catch (e) {
  alert('Error:' + e.message);
 }
}
</script>
<input type="button" onclick="sendTrace(0);" VALUE="GET">
<input type="button" onclick="sendTrace(1);" VALUE="TRACE">
<input type="button" onclick="sendTrace(2);" VALUE="\nTRACE">
</body>
Windows XP SP1のIE6での実行結果は下記となります。CookieとAuthrizationヘッダが取得できていることがわかります。この場合、HttpOnlyのCookieでも取得出来ます。


さて、Windwos XP SP1というと、とっくにサポートが切れているバージョンですが、より新しいバージョンのWindowsではどうでしょうか。Windows XP SP2(SP2を適用し他のパッチは適用していないもの)の場合、実行結果は下記となります。


JavaScriptの例外が発生しています。これは、TRACEメソッドをXMLHttpRequestオブジェクトから発行することが禁止されたことを意味しています。
しかし、このチェックには漏れがあり、"TRACE"の代わりに、"\nTRACE"(先頭に改行を含む)の場合は、TRACEメソッドが通ってしまいます。この内容については金床さんの記事が参考になります。

このチェック漏れですが、SP2に対するパッチで修正された模様で、SP2にすべてのパッチを適用したIE6だと、エラーになります。

XSTがブラウザ側で対策されたのはいつ?

さて、現在ではすべてのブラウザでXST対策がとられ、XST攻撃を行うことはできなくなっています(参考:XMLHttpRequestオブジェクトを使ったTRACEメソッド送信のブラウザ対応状況を確認してみる)。それでは、ブラウザ側でXST対策されるようになったのはいつ頃でしょうか。
上記のように、Windows XP SP2では不完全ながらXSTの対策がとられていますが、Windows XP SP2が出荷されたのは2004年9月ですから、当時既に「XSTはブラウザ側で対処すべし」という意識になっていたと思われます。また、GSXの白石さんの調査によると、IE以外のブラウザでXST攻撃ができたのは、Firefoxの1.0.5(2005年9月リリース)までであり、2005年11月リリースのFirefox1.5では攻撃が抑止されています。その他の、Opera、Google Chrome、Safariについては、XST可能なバージョンはありません。

これらの状況から、主要ブラウザに関しては2006年頃の段階では既にXSTの対策を終えており、現在サポートが継続されているバージョンについては、XST攻撃可能なブラウザはないと言えます。

XSTおよびTRACEメソッドをどのように捉えるか

このような状況にあるにも関わらず、最近でも「TRACEメソッドは危険」という記事を見かけることがあります(参考:TRACEメソッドって怖いんです - カイワレの大冒険)。しかし、以下の理由により、TRACEメソッドが危険というのは、あたらなくなっているというのが実情ではないでしょうか。
  • XSTは元々XSS脆弱性が存在しないと危険性はない
  • XSS単独でも危険な脆弱性である
  • XSTがなくてもXSS単体でBASIC認証のあるコンテンツを盗むことはできる
  • 主要ブラウザでは5年ほど前からXST防御機能が入っている
  • 古いブラウザを使うこと自体が大変危険な行為である
それにも関わらず、TRACEメソッドはWebサーバー側で止めておけと言われている理由は下記理由からです。
  • XSTで万一BASIC認証のパスワードが漏洩すると影響が長期に及ぶ
  • XSSを完全に排除することは実際には困難である
  • TRACEメソッドはApacheの設定で極めて容易に止めることができ、副作用もない
つまり、現在ではXSTの危険性はほとんどないが、TRACEを止めることは簡単に実現でき副作用もないので「TRACEを止めておけば安心」という程度のものであるわけです。

このため、TRACEをサーバー側設定で禁止することはやってもよいと思いますが、いまやTRACEを許容しているからと言って、サーバー設定の脆弱性とまでは言えないと考えられます。

脆弱性診断の実務では、TRACEメソッドを許容した状態を多くの診断業者は指摘すると考えられますが、危険度は「情報」(脆弱性とまでは言えないが一応ご報告)が妥当と考えます。今時、TRACEメソッドの許容を「重大な脆弱性」と指摘する事業者がもしあれば、それはちと過剰な指摘だなと個人的には考えます。

2013年1月9日水曜日

EMET3.5でIEを防御する(CVE-2012-4792)

先のエントリ「IE6,7,8のゼロデイ脆弱性(CVE-2012-4792)の対処法」で紹介したIE6~8のゼロデイ脆弱性(CVE-2012-4792)については、Fix Itによる回避策が提供されていますが、これを回避する攻撃が開発されたようです。
Researchers at Exodus Intelligence reported today they have developed a new attack that bypasses the FixIt issued by Microsoft. They were able to bypass and compromised a fully-patched system using some variation of the exploit published this week.
https://isc.sans.edu/diary/14824
試訳
Exodus Intelligenceの研究者たちは、Microsoftから提供されたFixItをバイパスする新しい攻撃を開発したと今日報告した。彼らは、今週発表されたexploitのいくつかのバリエーションを使用して、完全にパッチを適用したシステムをバイパスして危殆化することに成功した。 
一方、EMET(Enhanced Mitigation Experience Toolkit) 3.5を用いて、CVE-2012-4792の攻撃を回避できたとする報告もあるようです。EMETは元々Microsoftが回避策として提案していたツールです。
EMETは少し扱いの難しいツールですが、業務の都合上などでどうしてもWindows XP上のIE8以下のブラウザを使用しなければならないケースを想定して、Windows XP上にEMET 3.5 Tech Previewを導入して、IE用の設定をする方法を説明します。効果などは、前述のブログ記事を参照下さい。

EMET 3.5のダウンロードと導入

EMET 3.5の導入は以下のURLから可能です。

http://www.microsoft.com/en-us/download/details.aspx?id=30424


右下のDOWNLOADをクリックすると、ダウンロードが始まります。インストールは特に難しいところはありません。

EMET 3.5の設定

インストールが終了したら、EMETを起動します。


右下のConfigure Appsというボタンをクリックします。


Application Configurationというダイアログが表示されます。FileメニューからImport ...をクリックします。ファイル選択のダイアログが表示されます。



C:\Program Files\EMET (Tech Preview)\Deployment\Protection Profiles\Internet Explorer.xmlというファイルを開きます。これは、IE用の設定を予め記述したものです。


上記は、Application ConfigurationダイアログのAllタブをクリックした状態です。EMET3.5では、上記のように多くのメモリ保護の手段が提供されており、IEに対しては、そのすべてが有効となっています。これら機構のいずれかが働くことによって、任意のコマンド実行を防ぎます。

注意

合同会社セキュリティ・プロフェッショナルズ・ネットワークの塩月誠人さんに教えていただいたところによると、IEのアドオンによっては、誤検知によりIEごと落ちてしまうようです。その場合は、原因となったアドオンを無効にするか、EMET側の対応するルールを無効にする必要があります。
また、EMETはかなり強力なツールではありますが、IEの更新プログラムの代わりになるものではありませんで、IEの更新プログが提供され、適用するまでは、IEの使用は最小限に留めることをおすすめします。
企業等で導入する場合は、事前に十分にテストしてから展開してください。

追記(2013/1/9 15:50)

北河さん(@kitagawa_takuji)から、前述のExodus IntelligenceはEMETもバイパスできると主張していることを教えていただきました。北河さん、ありがとうございます。


まだこのツイートのみで詳細は不明ですが、EMETは有力なツールではあるものの過信は禁物であり、IE8以前の使用は社内システムなど必要最低限に留めるべきであることをあらためて強調したいと思います。


2013年1月3日木曜日

Ruby on Railsのfind_by_*メソッドにSQLインジェクション脆弱性(CVE-2012-5664)

Ruby on Rails(3.2.9, 3.1.8, 3.0.17以前)のfind_by_*メソッドにSQLインジェクション脆弱性が見つかりました(CVE-2012-5664)。このエントリではその概要と対策について説明します。

概要

Ruby on Railsのfind_by_*メソッドの引数としてハッシュを指定することで、任意のSELECT文を実行できる脆弱性があります。

検証

Ruby on Rails3.2.9の環境を用意して、以下の2つのモデルを用意しました。
$ rails g scaffold user name:string email:string
$ rails g scaffold book author:string title:string
モデルUserは個人情報を保持しており、自分自身の情報のみが閲覧できるという想定です。モデルBookは書誌データベースであり、すべて公開情報であるとします。

自動的に生成されたコントローラのshowメソッドは下記となっています。
  def show
    @book = Book.find(params[:id])
    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @book }
    end
  end
これが生成するSQL文は下記となります。
    SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT 1 [["id", "1"]]
プレースホルダを用いてSQL文が生成されているので、いい感じです。
画面表示は下記となります。


次に、findメソッドをfind_by_idに変更してみます。
    @book = Book.find_by_id(params[:id])
生成されるSQL文は以下となります。プレースホルダを使っていないので嫌な感じです。
    SELECT "books".* FROM "books" WHERE "books"."id" = 1 LIMIT 1
念のため、1の代わりに、1 or 1 = 1を指定してみましょう。検証のため、条件をハードコードします。
    @book = Book.find_by_id('1 or 1 = 1')
生成されるSQL文は下記となります。大丈夫ですね。
    SELECT "books".* FROM "books" WHERE "books"."id" = 1 LIMIT 1
ところが、以下のように、find_by_idの引数をハッシュにすることで、任意のSQL文を指定できます。
    @book = Book.find_by_id({:select =>"name as author, email as title, id from users limit 1 --"})
生成されるSQLは以下の通りです。列名を合わせるために、SELECT文で別名を指定しています。本来のSQL文は--によりコメントアウトされています。
    SELECT name as author, email as title, id from users limit 1 -- FROM "books" WHERE "books"."id" IS NULL LIMIT 1
この際の表示は下記となります。本来、書誌情報が表示されるべきところに、個人情報が表示されてます。


以上のように、find_by_*のパラーメタとしてハッシュを指定することが出来れば、任意のテーブルの情報を取り出すことができることがわかりました。

攻撃経路について

私自身がRuby on Railsに詳しくないので、find_by_idの引数にハッシュを指定できる可能性がどれくらいあるのか評価できないのですが、この問題を最初に取り上げたブログ「Let Me Github That For You」によると、なんらかの理由でsecret_token.rbに定義されたsecret_token値が既知である場合(公開されたソースを修正しないでそのまま使っている場合など)に、セッションCookieを改変してuser_credentials_idに上記のハッシュをシリアライズしたものを埋め込む可能性を指摘しています。
しかし、secret_token 値が既知だという前提では、セッションの内容を第三者が改変できてしまうため、これ自体が重大な問題です。このため、この想定は少々乱暴なように思えます。それに、先のブログでは以下のように指摘されています。
The simple problem is, that most developers are simply not aware of the confidentiality of this file , and in result they 'll happly(happilyのtypoか?) check it into Github or other online repositories
試訳
単純な問題点は、大部分の開発者がこのファイル(注: secret_token.rb)を機密にすべきことを単に承知しておらず、その結果、開発者たちはそのファイルをご機嫌でGithubその他のオンラインレポジトリにチェックインして(その結果公開して)しまうことだ。
すなわち、ブログ「Let Me Github That For You」の趣旨は、Ruby on RailsのSQLインジェクションがあることの指摘ではなく、多くのアプリケーションがsecret_tokenを適切に秘匿していないことを問題視しているように私は感じました。

対策

既にRuby on Railsの対策版が公開されています(3.2.10, 3.1.9, 3.0.18)。Ruby on Rails 3.2.10で先と同じことをすると、生成されるSQL文は下記となります。
    SELECT "books".* FROM "books" WHERE "id"."select" = 'name as author, email as title, id from users limit 1 --' LIMIT 1
WHEREの後の"id"."select"が微妙な感じではありますが、実害はないでしょう。これにより、SQLインジェクション攻撃を防止しています。
また、すぐにRuby on Railsのバージョンアップが難しい場合の回避策が、SECLISTS.ORGに紹介されています。 find_by_*を呼び出している箇所を探して、以下のようなメソッド呼び出しがあれば、
  Post.find_by_id(params[:id])
以下のように文字列に変換します。これにより、ハッシュがfind_by_*メソッドに渡されることを防止できます。
  Post.find_by_id(params[:id].to_s)

まとめ

Ruby on Railsのfind_by_*メソッドのSQLインジェクション脆弱性について説明しました。find_by_*メソッドの引数にハッシュが渡る可能性の評価については、私にはできませんので、Ruby on Railsに詳しい方の評価を待ちたいと思います。

追記(2013/1/4 2:10)

twitterにて、@hasimoさんから、この問題の優れた解説「Rails SQL injection vulnerability: hold your horses, here are the facts」を紹介いただきました。これによると、クエリ文字列 id[select]=xxxx の形でパラメタidにハッシュを与えることはできるが、ハッシュのキーが文字列になるためにSQLインジェクションには至らないということでした。ためしてみると、確かにそうなります。

アプリケーションに以下のクエリ文字列を指定して呼び出した場合、
    id[select]=name+as+author,+email+as+title,+id+from+users+limit+1+--
Ruby on Railsアプリケーションの受け取る値は下記となります。
    {"select"=>"name as author, email as title, id from users limit 1 --"}
一見、先のPoCと似ていますが、ハッシュのキーが、PoCではシンボル :select であるのに対して、クエリ文字列の場合は、キーが文字列 "select" です。そして、キーが文字列の場合は、SQLインジェクションにはなりません。

上記から考えても、CVE-2012-5664によってSQLインジェクションの攻撃を受けるシナリオというのは、ゼロではないにしても、相当レアなケースであると考えられます。

しかしながら、SQLインジェクション攻撃の影響は甚大です。できる限り、Ruby on Railsの対策版へのバージョンアップか、上記回避策の導入を推奨いたします。

2013年1月1日火曜日

IE6,7,8のゼロデイ脆弱性(CVE-2012-4792)の対処法

昨年末に、Internet Explorer(IE)のゼロデイ脆弱性(CVE-2012-4792)が発表されました。既にこの脆弱性を悪用した攻撃が観察されているということですので、緊急の対応を推奨します。

概要は、Microsoft Security Advisory (2794220)にまとめられていますが、英文ですので、JVNのレポート「JVNVU#92426910 Internet Explorer に任意のコードが実行される脆弱性」をご覧になるとよいでしょう。

影響を受けるシステム:
  • Internet Explorer 6
  • Internet Explorer 7
  • Internet Explorer 8

想定される影響:
細工された HTML ドキュメントや Office ファイル等を閲覧することで、任意のコードが実行される可能性があります。

対策方法:
2012年12月31日現在、対策方法はありません。

対策方法がないということですので、IE9以降を使うか、IE以外のブラウザを使うことで回避しましょう。IE6~IE8を使う場合に、Enhanced Mitigation Experience Toolkit (EMET) を適用するなどの緩和策も紹介されていますが、EMETの設定などが今のところ示されていないことと、どこまで攻撃を緩和してくれるのか不明なので、対策パッチが出るまで下記の回避策を実施するのが良いと考えます。

追記(2013/1/9): EMETによる回避策の有効性が分かってきましたので、その設定方法を「EMET3.5でIEを防御する(CVE-2012-4792)」にまとめました。

Windows XP、Windows Server 2003ユーザ

Windows XPとWindows Server 2003にはIE9を導入できないので、IE以外のブラウザとしてGoogle Chrome、Firefox、Operaなど別のブラウザの使用を推奨します。Windows版Safariについては致命的な脆弱性があり、アップデートの予定もないことから、使用停止の勧告が既に出ています(JVN#42676559)。

追記(2013/1/8)
合同会社セキュリティ・プロフェッショナルズ・ネットワークの塩月誠人さんから、IE以外のブラウザという表記についてご指摘をいただきました。見かけ上IE以外の名称のブラウザであっても、IEエンジンを使用しているブラウザの場合、IEと同様の影響を受けます。塩月氏によると、以下のブラウザで当該脆弱性の影響を確認しているとのことです。
例)
Lunascape 6 (Tridentレンダリングエンジン)
Sleipnir 3 (IE互換モード)
Firefox + IE Tab2
Chrome + IE Tab
ご指摘の通りです。塩月さん、ありがとうございました。
(追記終わり)


この機会にWindows 8(あるいはWindows 7)への移行を検討するのもよいでしょう。

Windows Vista、Windows 7、Windows Server 2008(R2含む)ユーザ

IE9では当該脆弱性の影響を受けないとされていますので、IE9に移行することで脆弱性を回避できます。
あるいは、IE以外のブラウザ(Google Chrome、Firefox、Opera)を使うことも有力な回避策です。
普段IEを使っていない方も、この機会にIE9に移行しておきましょう。

Windows 8、Windows Server 2012ユーザ

Windows 8とWindows Server 2012には元々IE10がインストールされているので、当該脆弱性の影響は受けないと考えられます。

まとめ

IEのゼロデイ脆弱性(CVE-2012-4792)の対処方法について、Windowsのバージョンごとに紹介しました。対策パッチが出て適用するまでは、IE6~IE8は使用禁止にするしかなさそうです。
前述のとおり、Windows版Safariには致命的な脆弱性があり、アップデートの予定もないことから、使用停止の勧告が昨年の10月23日に出ています(JVN#42676559)。
現在休暇中の企業・団体が多いと思いますので、休暇明けの即座の展開を推奨します。

追記(2012/1/2 12:30)

Microsftからこの脆弱性に対するFix itが公開されました。Fix itとは、セキュリティ問題を含むさまざまな現象に対して、設定変更による解決を簡便に実施するためのプログラムです。
CVE-2012-4792に対しては、Shimというものを使った回避策を提供するFix itのようです。


あくまでも回避策であって、Fix itの説明にも下記のように説明されています。
The Fix it solution that is described in this section is not intended to be a replacement for any security update. We recommend that you always install the latest security updates. However, we offer this Fix it solution as a workaround option for some scenarios.
http://support.microsoft.com/kb/2794220
試訳
このセクションに記載したFix itソリューションは、セキュリティ更新プログラムの代替として意図したものではありません。常に最新のセキュリティ更新プログラムをインストールすることをお勧めします。しかしながら、いくつかのシナリオに対する回避策のオプションとして、このFix itソリューションを提供するものです。
ということで、回避策として有効ではあるのでしょうが、すべての脅威を取り除くことまでは期待できないようです。なんらかの理由で、IE6~IE8を残したままにしている環境では、このFix itを導入するとよいでしよう。
Fix itを導入するには、先のページにて以下のアイコンの箇所を探します。


左側が、この問題の回避策のFix ix、右側がそれを解除するためのFix itです。すなわち、通常は左側のみを導入することに注意してください。よくわからないからと両方導入してしまうと、Fix itを導入して、即座に解除することになってしまいます。

結論としては、IE6~IE8の導入された環境にはこのFix itを導入するとよいと思いますが、インターネット利用の際には、IE以外のGoogle Chrome、Firefox、Opera等を利用することを、引き続き推奨します。

フォロワー

ブログ アーカイブ