2014年11月7日金曜日

自宅の鍵を定期的に取り替える佐藤君(仮名)の話

パスワードの定期的変更について元々違和感を持っています。今まで、理詰めでその違和感を解明しようとしてきましたが、それでも私の頭のなかのもやもやをうまく説明できたわけではありません。そこで、パスワードの定期的変更を「自宅の鍵を定期的に変更する比喩」を用いて、そのもやもやを説明したと思います。比喩によって精密な議論ができるとは思っておりませんので、あくまでも主観的な「もやもや」を説明する方便として読んでいただければ幸いです。ここに登場する佐藤は架空の人物です。

徳丸: 佐藤君は自宅の鍵を定期的に取り替えていると聞いたんだけど、本当?

佐藤: 本当ですよ。毎年に替えています。毎年年末に鍵を取り替えて、安心な気持ちで新年を迎えるんです。徳丸さんは替えてないんですか?

徳丸: 替えないよ。鍵を落としたりしたらまた別だけど、そういうのでもなければ替えないよね。佐藤君はなぜ毎年替えるの?

佐藤: だって心配じゃないですか。

徳丸: 何が?

佐藤: 僕の知らないうちに合鍵が作られているかもしれないじゃないですか?

徳丸: 証拠でもあるの?

佐藤: 証拠はありません。でも、たとえば彼女が部屋に来るのに僕が留守だなんて場合に、郵便受けに鍵を入れておいたりするんで、誰かがこっそり鍵をとって合鍵を作っているかもしれません。

徳丸: そんな運用しなければいい。

佐藤: たとえば、ですよ。リスクをゼロにはできません。

徳丸: で、誰かが合鍵で部屋に入った兆候でもあるの?

佐藤: 今のところありません。

徳丸: だったら、そういう兆候が見つかったら替えたら?

佐藤: 徳丸さん、そんな意識でいいんですか? 万一の場合にも備えないとダメです。

徳丸: そうはいうけど、一年に1回変えるのでは、最悪ケースだと約一年間は合鍵で部屋に入られるわけでしょ。

佐藤: それはそうですが、永遠に入られ続けるよりましです。

徳丸: 悪い奴は、部屋に侵入されたら即座に部屋のものを盗んでいくんじゃないの?

佐藤: それは分かりません。僕の大事な情報だけ盗んでいくかもしれません。

徳丸: でも、何回も盗みに来るかね? 鍵を変えた後には、もう盗むべき情報はないのでは?

佐藤: 毎日仕事をして、生活をしていれば、その間に秘密情報も増え続けるんですよ。

徳丸: それは犯人にとって価値のある情報かな?

佐藤: それは分かりませんが、ひょっとしたら価値があるかもしれないじゃないですか。

徳丸: まぁ、そうだね。じゃあ、兆候が見つけられないんだったら、誰かが部屋に入ったらセンサーが作動するようにしたら?

佐藤: そうか、センサーがあれば、別人が部屋に入ったらわかりますね。

徳丸: そうそう、そうしなよ。

佐藤: 分かりました。そうします。


佐藤は自宅に侵入検知のセンサーを取り付けて運用を開始した。それから2年が経った。

徳丸: やぁ、佐藤君、久し振りだね。その後どう?

佐藤: いやぁ、相変わらずですね。

徳丸: センサーの調子はどう? 誰か入ってきた?

佐藤: センサーはちゃんと動いているようです。別人が侵入した兆候はないですね。

徳丸: なら、鍵を取り替える必要もなくなったね。

佐藤: いや、念のため、昨年末に鍵を替えました。

徳丸: え゛っ、なぜ?

佐藤: だって、心配じゃないですか?

徳丸: センサーまでつけたのに?

佐藤: 徳丸さん、考えてみたんですよ。誰も侵入していないとしても、合鍵を作られていない証拠にはなりません。

徳丸: どうして?

佐藤: 犯人は合鍵を作ったけれども、すぐに使わずに侵入するタイミングを伺っているかもしれないじゃないですか。

徳丸: それはそうだけど、そんな悠長なことするかね?

佐藤: それは分かりません。私は悪い奴の気持ちはわからないですが、そうしないという証拠はありません。

徳丸: うーん、合鍵を作られたけどすぐには悪用しないというケースを想定するのであれば、鍵の取り替えで対応するには、一年に1回とかでは不十分だと思うよ。

佐藤: やはり、毎日鍵を替えないとだめでしょうか?

徳丸: そんなの現実的ではないよね。

佐藤: あー、心配だ。どうすればいいんだ。

徳丸: 合鍵が心配だったら、ICカードキーとか生体認証を併用して二要素にしたらいいのでは?

佐藤: 二要素にしたら費用が掛かるし、毎日の鍵の開け閉めが面倒くさいじゃないですか。

徳丸: いやいや、佐藤くんがそこまで心配するなら、そうすべきだと僕は思うけどね。

佐藤: …(徳丸と佐藤の会話はまだまだ続く)


「侵入検知センサー」はパスワードの場合はログインアラートに相当します(参照)。
心配症の佐藤君の考え方が、そのままパスワードの定期的変更に当てはまると主張しているわけでありません。そこは精密な議論が必要ですが、私の主観的なもやもやの中身の一部は、上記の対話の比喩で説明できるのではないかというお話です。

2014年11月4日火曜日

ログインアラートはパスワード定期的変更の代替となるか

パスワードの定期的的変更には実質的にはあまり意味がないのではないかという議論(疑問)から出発した議論を続けておりますが、こちらなどで表明しているように、パスワードの定期的変更が効果をもつ場合もあります。
そこで、本稿ではパスワードの定期的変更の代替手段としてログインアラートの運用に着目し、ログインアラートの運用がパスワードの定期的変更の代替となるのか、残る課題は何かについて検討します。

パスワード定期的変更の効果まとめ

まず前提条件について説明します。ウェブサイトAの利用者xが自身のパスワード voc3at を定期的変更として変更する(voc3atはあくまで例です)場合、これが効果を発揮する条件と効果は、以下と考えられます。条件1と条件2はAND条件です。
  • 条件1: パスワード voc3at が既に漏洩していて、今後悪用される可能性がある
  • 条件2: パスワード漏洩に利用者 x は気づいておらず(あるいは気づいたにも関わらず)、パスワードを(非定期には)変更していない
  • 効果: パスワードの定期的変更後、サイトAにおける パスワード voc3at の悪用はできなくなる
条件1のパスワード voc3at が漏洩する経路については制約はありませんが、以下の様なケースが考えられます。
  • サイトAからパスワード漏洩が起こった
  • 利用者 x がパスワード voc3at を別のサイトBでも利用していて、サイトBから漏洩した
  • 利用者 x 自身がパスワード voc3at を別人に教えてしまった
  • 利用者 x がフィッシング詐欺にあってしまい、パスワード voc3at が漏洩した
条件2に関して、利用者が気づく局面の例としては以下があります。利用者が気づかない状況とは、以下のいずれにも該当しないケースです。
  • LINEのアカウント乗っ取りのように、利用者 x 自身は利用できなくなったり、友人が教えてくれる
  • 不正送金被害にあい、銀行口座から預金がなくなってしまった
  • なりすまし投稿により自身のアカウントが炎上してしまった
  • サイトAがパスワード漏洩事件を公表し、パスワード変更を呼びかけた
  • ログイン履歴を見ていて気づいた

ログインアラートとは

ログインアラート(ログイン通知とも)とは、誰か(利用者自身も含むが第三者かもしれない)がサイトにログインした際に、その旨を登録済みメールアドレスに通知する機能です。多くのサイトではログインに成功した場合に通知しますが、LINEウェブストアに関しては、ログインに失敗しても通知します。
以下は、楽天のログインアラートのメール例です。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【楽●天】ログインアラート通知(2014/10/20 09:53)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

xxxxxxxx 様

お客様が楽天のサービスにログインしましたので、お知らせいたします。
このメールはログインアラート設定を行っているお客様に
送信させて頂いております。

◆ログイン情報
・ログイン日時 :2014/10/20 09:53
・IPアドレス   :xxx.xxx.xxx.xxx

◆ログイン履歴
https://history.id.rakuten.co.jp/
よりログイン履歴がご確認いただけます。

◆心当たりのないログインの場合
お客様以外の第三者がお客様に成りすましてログインしている可能性があります。
その場合、次のURLより会員情報管理画面にてパスワードを
変更されることをおすすめします。
https://member.id.rakuten.co.jp/rms/nid/menufwd
利用者は、ログインしていないのにこのメールを受け取った場合、別人が自分のアカウントでログインしていることを疑い、IPアドレスを確認した上で必要に応じてパスワードを変更することになります。

※注: 楽天のログインアラートメールには、このパスワード変更のURLが記載されていますが、このURLにアクセスすると楽天のログイン画面になるわけで、ログインアラートがフィッシングに悪用される懸念を考えると、メールの文面は悩ましいところではあります。

ログインアラートの効果

パスワードの定期的変更とログインアラートは、どちらもパスワードが漏洩してからの事後の緩和策と位置づけることができますが、効果については微妙に異なります。
ログインアラートの利点は以下の通りです。
  • 不正ログインの事実を確認できる
  • 不正ログインの際の日時やIPアドレス等から犯人追跡の情報が得られる
  • 不正ログインから速やかな対処が可能
  • 不必要なパスワード変更をする必要がないので利用者側の負担が少ない
  • 実装上のコストは比較的低い

一方、ログインアラートではカバーできない状況として下記があります。

  1. パスワード漏洩後、なんらかの理由で数ヶ月後に犯人が初めてログインした
  2. サイトBから漏洩したパスワードリストを犯人が購入し、漏洩から数ヶ月後にサイトAで試した。利用者はサイトAとサイトBで同じパスワードを設定していた
  3. 利用者のリテラシーが低く、ログインアラートに適切に対処できない

1.に関しては、パスワードを得て「数ヶ月放っておく」動機があるのが問題になります。
2.に関しては、あり得るシナリオではありますが、サイトAに重要情報がるのであれば、やはりパスワードの使い回しを避けるほうが安全で、その方が総合的には手間も掛からないと考えます。
3.に関しては、ログインアラートに適切に対処できない人が自発的にパスワードの定期的変更をするとは考えにくいので、パスワードの有効期限を定めて強制的に変更させないと実効性がないでしょう。ただし、私自身はパスワードの有効期限のあるサイトは、できれば使いたくないと感じます。

ログインアラートはパスワード定期的変更の代替になるか

ログインアラートについて紹介し、パスワードの定期的変更運用の代替になるか(ログインアラートを適切に運用すれば、パスワードの定期的変更をしなくてすむか)について検討しました。
前述のように、ログインアラートは完全にパスワードの定期的変更の代替にはならないものの、残るリスクは比較的軽微であり、実用上はログインアラートがあれば、パスワードの定期的変更はしなくてもよいケースが多いのではないかと考えます。
もちろん、利用者側で心配であれば、ログインアラートに加えてパスワードの定期的変更もすればよいと思います。しかし、そこまで心配するのであれば、二段階認証など、さらに強固な認証手段を提供しているサイトを選択する方が良いと考えます。二段階認証を設定していれば、パスワードの定期的変更は必要ないでしょう。
今回私が二段階認証ではなくログインアラートに着目した理由は、前述したように実装コストが低く、利用者・サイト提供者共に負担が軽いからです。このため、不正ログイン事件が多発している現状において、最低限度のセキュリティ施策として、ログインアラートの実装の検討をおすすめします。

2014年10月25日土曜日

New Class of Vulnerability in Perl Web Applicationsの紹介

Redditを眺めておりましたら以下の記事が目に止まりました。
New Class of Vulnerability in Perl Web Applications
ざっくりというと以下の様な内容です
  • CGI.pmのparamメソッドの返り値をハッシュに突っ込んでいる箇所がある
  • クエリ文字列に同名のパラメータを複数セットすると、配列値が返る
  • 配列をハッシュに突っ込むことにより、別のキーの値が変更される
私はこの内容に興味を持ちましたので、以下に詳しく説明します。

PoC

元エントリにもPoCが出ておりますが、少し手をいれたものを以下に示します。
#!/usr/bin/perl
use strict;
use CGI;

my $cgi = new CGI;

my $loginname = 'smith';
my $password  = 'a3k!sz9';

my %user = ('login' => $loginname,
            'realname' => $cgi->param('realname'),
            'password' => $password);
print <<END;
Content-Type: text/plain; charset=utf-8

login = $user{'login'}
realname = $user{'realname'}
password = $user{'password'}
END
ご覧のように、キーとしてlogin、realname、passwordを取るハッシュを作成しています。loginとpasswordは定数(セッション変数などから取得する想定)、realnameはクエリ文字列realnameから取得します。

このCGIプログラムをクエリ文字列 realname=John+Smith で呼び出すと結果は下記となります。
login = smith
realname = John Smith
password = a3k!sz9
次に、以下のクエリ文字列で呼び出します。
realname=hoge&realname=login&realname=yamada
結果は以下となります。
login = yamada
realname = hoge
password = a3k!sz9
なんと、定数で指定しているはずのloginがyamadaに化けています。なぜでしょうか?

CGI.pmは同名パラメータ指定により配列を受け取ることができる

CGI.pmのparamメソッドは、以下のようにすると、配列の形でパラメータを受け取ることができます。
my @foo = $cgi->param('foo');
例えば、以下のクエリ文字列を指定した場合、
foo=1&foo=2&foo=3
@fooは 1, 2, 3を値にもつ配列となります。

ハッシュ生成時に配列を混ぜることでハッシュの別キーをインジェクションできる

PoCの%user の部分は以下のように呼び出されることになります。
my %user = ('login' => 'smith',
            'realname' => ('hoge', 'login', 'yamada'),
            'password' => 'a3!sz9');
Perlの場合、=>とカンマ(,)はほぼ同じ意味だそうで、上記は以下のように展開されます。私はPerlの細かい文法には自信が無いため、以下の部分の説明に誤りがあればご指摘ください。
my %user = ('login', 'smith',
            'realname', 'hoge', 'login', 'yamada',
            'password', 'a3!sz9');
整形すると、以下と同等です。
my %user = ('login' => 'smith',
            'realname' => 'hoge', 
            'login' => 'yamada',
            'password' => 'a3!sz9');
右辺は配列定義ですが、'login' => が2箇所あります。したがって、配列をハッシュに変換する際に、キーloginに対する値は、'yamada'に上書きされます。すなわち、以下と同等です。
my %user = ('realname' => 'hoge', 
            'login' => 'yamada',
            'password' => 'a3!sz9');
以上の手順により、$user{'login'}が本来smithであるところ、外部からの入力により、yamadaに変更させられました。

このようなケースはありえるのか?

上記のPoCのようなことが現実にあり得るかというと、ちょっと微妙な気はするものの、ないこともないかなという印象です。
たとえば、Web APIなどで、入力はurlencodedだけど出力はJSONであって、そのJSONを作る際にいきなりハッシュ定義に入力値を放り込んでいるケースなどです。
元記事のコメント欄には、ふつーバリデーションするだろというツッコミに対して、著者は、realnameは任意の文字を受け取るのでバリデーションは必要ないと答えています。しかし、制御文字などは弾く必要があるため、アプリケーション要件としても、最低限度のバリデーションは必要と考えます。

対策

この問題を避ける簡便な方法として、元エントリではrealnameの値をスカラーに変換する方法が紹介されています。
my %user = ('login' => $loginname,
            'realname' => scalar $cgi->param('realname'),
            'password' => $password);
あるいは、値をいったんスカラー変数で受けるという方法があります。これにより、realnameがスカラであることが保証されます。
my $realname = $cgi->param('realname');
my %user = ('login' => $loginname,
            'realname' => $realname,
            'password' => $password);

まとめ

前回のエントリでも説明したように、スカラ値を想定している入力が、実は配列やハッシュになっている可能性を想定して、スカラであることのチェック、あるいはスカラに変換する等の処理が必要な場合があります。
入力値のバリデーションをすれば脆弱性が顕在化することはありませんが、脆弱性が混入するその箇所で対策することを考えると、ハッシュに突っ込む箇所で、当該変数がスカラーであることが一目瞭然になっているのがよいでしょう。
そういう意味で、バリデーションによる対策をとらず、脆弱性の発生箇所での対策を主張されている元エントリの著者Gerv(Gervase Markham)さんとは、一緒に旨い酒が飲めそうだと感じましたw

2014年10月20日月曜日

DrupalのSQLインジェクションCVE-2014-3704について調べてみた

既に日本でも報道されているように、著名なCMSであるDrupalのバージョン7系にはSQLインジェクション脆弱性があります(CVE-2014-3704)。この脆弱性について調査した内容を報告します。

ログイン時のSQL文を調べてみる

MySQLのクエリログを有効にして、Drupaのログイン時に呼び出されるSQL文を調べてみます。リクエストメッセージは以下となります(一部のヘッダを省略)。
POST /?q=node&destination=node HTTP/1.1
Host: xxxxxxx
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Cookie: has_js=1; Drupal.toolbar.collapsed=0
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 122

name=admin&pass=xxxxxxxx&form_build_id=form-xQZ7X78LULvs6SyB9MvufbZh5KXjQYRHS05Jl2uD9Kc&form_id=user_login_block&op=Log+in
ログイン時には複数のSQL文が呼び出されますが、以下のSQL文に注目します。
SELECT * FROM users WHERE name = 'admin' AND status = 1
次に、name=adminの部分を以下のように変更してみます。
name[]=user1&name[]=user2
生成されるSQL文は以下の通りです。
SELECT * FROM users WHERE name = 'user1', 'user2' AND status = 1
あれ、配列として渡したuserが、SQL文では、'user1', 'user2'とカンマ区切りで列挙されています。これはSQL文法違反となっていますが、DrupalのSQL文ジェネレータの機能で、配列のパラメータを自動的にSQLのプレースホルダに展開する機能があるのです。以下、こちらの記事のサンプルをお借りして説明します。

以下のようなdrupal APIの呼び出しを題材に用います。
<?php
db_query("SELECT * FROM {users} where name IN (:name)", array(':name'=>array('user1','user2')));
?>
上記IN句に対して、プレースホルダは :name 一つだけ、バインドする値は2個あります。この場合、SQL文は以下のように自動的に改変されます。
SELECT * from users where name IN (:name_0, :name_1)
なんて便利なんでしょう! SQLインジェクション対策としてプレースホルダを使えと呼びかける際に決まって問題となる IN句の展開をやってくれていますね。つまり、バインド値を配列にする呼び出し方は主に IN句を想定したものであり、最初に見たname[]を複数指定する呼び出し方は、想定外といってよく、その結果として生成されるSQL文が文法違反となりました。
文法違反というだけでかなり嫌な予感がしますが、この予感は不幸にも的中します。

連想配列のキーを指定したらどうなるか

ここで元のログイン時のSQL文に戻り、今度はname配列にキー文字列をつけて呼び出してみましょう。以下のname[]配列を用います。
name[id1]=user1&name[id2]=user2
生成されるSQL文は以下の通りです。
SELECT * FROM {users} WHERE name = :name_id1, :name_id2 AND status = 1
一見筋の通った処理に思えますが、キーに空白があったらどうなるでしょうか?
name[1 xxxxx]=user1&name[2]=user2
生成されるSQL文は下記となります。
SELECT * FROM {users} WHERE name = :name_1 xxxxx, :name_2 AND status = 1
あれあれ、プレースホルダがちぎれて、:name1と xxxxx に分かれてしまいました。これはもちろん文法違反ですが、実は呼び出す前にエラーになります。
この際のバインド値は以下の通りです。
array(2) {
  [":name_1 xxxxx"] => "user1"
  [":name_2"] =>  "user2"
}
SQL文中には :name_1 というプレースホルダがありますが、バインド値の配列には :name_1がありません。このため、PDOの動的プレースホルダが値をバインドできず、呼び出す前にエラーになるわけです。
それでは、エラーにならない方法はあるでしょうか? あります。:name_2 はあるわけですから、最初のプレースホルダ側でも :name_2 を使ってしまえばよいのです。
今度は、以下のname[]値で呼び出してみます。user1は使われないので削除しました。
name[2 xxxxx]=&name[2]=user2
生成されるSQL文
SELECT * FROM {users} WHERE name = :name_2 xxxxx, :name_2 AND status = 1
SQL文の中で使われているプレースホルダは :name_2 のみとなりました。この際のバインド値は以下の通りです。
array(2) {
  [":name_2 xxxxx"] => ""
  [":name_2"]=> "user2"
}
キー :name_2 はあるので、SQL文は呼び出されるはずです。ログを見ると、以下のSQL文がみつかります。
SELECT * FROM users WHERE name = 'user2' xxxxx, 'user2' AND status = 1
呼び出されていますね。ただし、xxxxxの部分でSQL文法違反となっているので、MySQL側でエラーになり、実行はされません。このxxxxxを文法違反にならないように辻褄をあわせてやると、SQLインジェクション攻撃ができます。

SQLインジェクションを試す

いよいよSQLインジェクションです…が、まだ発表されたばかりの脆弱性ですので、実害のあるものは避けて、10秒待つだけのSQLを実行してみましょう。ということで、SELECT sleep(10) というSQL文を実行してみましょう。POSTパラメータは以下となります。
name[2 ;SELECT sleep(10) -- ]=&name[2]=user2
Burp SuiteのRepeater機能で実行した例を下図に示します。


図の右下に 10,079millisとあることから、10,079ミリ秒、すなわち約10秒待っていることがわかります。この際に呼び出されているSQL文は下記の通りです。
SELECT * FROM users WHERE name = 'user2' ;SELECT sleep(10) -- , 'user2' AND status = 1
実験に使用した環境はMySQLを使っていますが、SQLの複文が実行できていることになります。これは、DrupalがPDOを使っているためで、詳しくは以下のエントリを参照ください。

ソース上の脆弱性箇所

この脆弱性の発生原因は、ソース上では以下のexpandArgumentsメソッドにあります。
// includes/database/database.inc
protected function expandArguments(&$query, &$args) {
  $modified = FALSE;
  // $argsの要素から配列のみ処理対象として foreach
  foreach (array_filter($args, 'is_array') as $key => $data) {
    $new_keys = array();
    // $dataは配列であるはずなので、foreach 可能。 $i(キー)に注目
    foreach ($data as $i => $value) {
      $new_keys[$key . '_' . $i] = $value;
    }
    // $queryを改変 $new_keysのキーをarray_kesyでSQL文に混ぜていることが問題
    $query = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $query);
    unset($args[$key]);
    $args += $new_keys;

    $modified = TRUE;
  }
  return $modified;
}
元のコメントはすべて削除して、簡単な説明をコメントとして追加しました。
先のPoCから呼び出される場合、$args['name'] に配列値が入っている場合、配列のキーが変数 $i 経由でなんのチェックもなくSQL文に流し込まれることが問題です。
この脆弱性が対策されたDrupal7.32では、内側のforeachは以下のように修正されています。
foreach (array_values($data) as $i => $value) {
$dataにarray_values関数を通すことで、キーを取り除くことにより対策しています。
これで一応脆弱性は対処されていると思いますが、最初の方で指摘したようにクエリ文字列nameを配列とした場合に、文法違反のSQL文が生成される問題は直っていません。
Drupalは必要最小限のバリデーションのみをしているように見えますが、クエリ文字列が(配列ではなくスカラの)文字列であることのチェックくらいはした方がよいと思います。これはアプリケーション要件として必要なチェックだと考えます。

この脆弱性による影響

SQLインジェクションによる一般的な影響はすべて可能性がありますが、とくにデータベースの改変による攻撃経路が重要であると考えます。詳細は伏せますが、管理者権限をうばったり、管理者権限のあるユーザを登録できることを実験で確認しています。
DrupalはCMSですので、管理者権限が得られた後は、ファイルをアップロードするなどして任意のPHPスクリプト実行なども可能になると考えます。既にさまざまな攻撃が実際に来ているようですので、早急の対策をおすすめします。

影響を受けるバージョンと対策

Drupal 7.xのみが影響を受けます。Drupal 7.32にて対策されているので、該当バージョンを利用の場合、早急のアップグレードを推奨します。
すぐにアップグレードできない場合は、WAFによる攻撃緩和も有効と考えられます。Drupal対応のシグネチャがなくても、既存のシグネチャによる防御が期待できます。ただし、シングルクォートを使わなくても攻撃は可能なので、WAFの性能次第というところはあります。

まとめ

Drupal 7.xのSQLインジェクション脆弱性について説明しました。このSQLインジェクション脆弱性は、SQL文を動的生成する際に、プレースホルダ中に誤って配列パラメータ中のキー値をなんのチェックもせずに混入させてしまったことによるものです。
配列パラメータのキーによる攻撃は結構あるパターンでして、以下のエントリでも説明しています。
O/RマッパーやSQLジェネレータを開発する場合は、パラメータが配列である場合や、配列のキーが指定された場合についもテストをしておくとよいでしょう。

2014年10月16日木曜日

パスワードの定期的変更はパスワードリスト攻撃対策として有効か

パスワードリスト攻撃の対策として、パスワードの定期的変更に意味があるのかという議論があります。私は(利用者側施策としては)実質意味がないと思っていますが、まったく意味がないというわけでもありません。
このエントリでは、パスワードの定期的変更がパスワードリスト攻撃に対してどの程度有効かを検討してみます。

前提条件

パスワードリスト攻撃を以下のように定義します。
別のサイトから漏洩したアカウント情報(ログインIDとパスワードの組み合わせ)の一覧表(パスワードリスト)があり、そのログインIDとパスワードの組をそのまま、攻撃対象に対してログイン試行する攻撃
パスワードの定期的変更の一例として以下の条件を前提とします
  • 利用者は、すべてのサイトのパスワードを90日毎に変更する
  • 利用者はすべてのサイトで同じログインIDを用いている
  • 変更後のパスワードはすべてのサイトで同じとする
    ※ サイト毎にパスワードを別にすれば、それ以降はパスワードをまったく変更しなくてもパスワードリスト攻撃はできなくなるためこの条件を設定

攻撃条件(1)

攻撃者はサイトAから窃取したアカウント情報を直ちにサイトBに対して試行する場合

この場合、サィトAに登録されたIDとパスワードはサイトBでも有効なので、パスワードリスト攻撃が成功する。すなわち、パスワードの定期的変更に効果はない。

攻撃条件(2)

攻撃者はサイトAから窃取したアカウント情報を用いてサイトBを攻撃するが、アカウント情報は古いものであるとする。

※ アカウント情報が古いシナリオとしては、サイトAのパスワードがハッシュ値で保存してあったために解読に時間がかかった、あるいは攻撃者がパスワードリストを購入したが、最新ではなく古いものであった、などの可能性があります。

この場合、パスワードが漏洩してから攻撃があるまでの間に、パスワードが変更されていれば、パスワードリスト攻撃は成立しません。パスワードが変更される前であれば、攻撃は成立します。

評価

パスワードの定期的変更を実施していると、仮に全サイトで同じパスワードを設定していても、パスワードリストが古いものである場合、攻撃のタイミングによってはパスワードリスト攻撃を防ぐことが出来ることができます。パスワードリストの売買の報道例についてはこちらを参照ください。
それにも関わらず私が「実質意味がない」と思う理由は、どうせパスワードを変更するのであれば、そのタイミングで、サイト毎に異なるパスワードを設定すればいいじゃないかと思うからです。いったんサイト毎に異なるパスワードを設定しておけば、その後はパスワードを変更しなくても、パスワードリスト攻撃に関しては完全に防御することができます。

一方、パスワードの定期的変更では、「運が良ければ防げるが、防げない可能性も高い」という性質のものなので、利用者側の立場としてのパスワードリスト攻撃対策は以下の一点でよいと考えます。
  • サイト毎に異なるパスワードを設定する
他の攻撃に関しては、別の対策も併用する必要があります。
また、パスワードの定期的変更の、リスト型攻撃以外に対する効用については、以下のエントリを参照ください。

追記(2014/10/16 15:30)

@machuさんからコメントをいただきました。
『管理者側は違うパスワードは強制できないけど、定期変更は強制できる』という指摘は鋭い着眼ですね。そういえば、総務省の公表した『リスト型アカウントハッキングによる不正ログインへの対応方策について』では、「利用者にパスワードの定期的変更を求める」という表現ではなく、「パスワードの有効期間設定」となっていました。総務省の資料は、「サイト管理者などインターネットサービス提供事業者向け対策集」ということなので、このような表現になっているのだと思います。これに対する評価は以下の通りです。

  • パスワードの定期的変更は強制できる
  • 結果としてパスワードリスト攻撃への効果は限定的(ないわけではない)

ですが、パスワードの管理は本来は利用者側の責任であるわけで、そこにサイト運営者が介入する方向性としては、できるだけ利用者の負担増が少なく、かつ効果の高いものであるべきだと考えます。この点、「パスワードの有効期間設定」は、利用者の負担が大きく、かつ効果が限定的であるという点で、よくない施策であると考えます。













2014年10月15日水曜日

気づけばプロ並みPHP 副読本:お助け電子BOOKへの寄稿の顛末

谷藤賢一さんの著書『気づけばプロ並みPHP~ショッピングカート作りにチャレンジ! 』に、発売1周年の謝恩キャンペーンとして『副読本:お助け電子BOOK』が公開されました(*1)。私はこの副読本の中で、『第2章 【徳丸 浩氏 スペシャル寄稿】安全なWebアプリケーションのために』を寄稿しています。このエントリでは、寄稿の顛末を報告したいと思います。

動機

私が本書『気づけばプロ並みPHP』を購入したのは昨年の10月29日ですから、本書が出版されてまもなく、今から約1年前です。私は本書を一読して、セキュリティ上の多数の問題があることに気がつきました。
以前は、セキュリティ上の問題が多い本は書評をブログ記事として書くことも多かったのですが、この際は書評という形にするのはためらいがありました。その理由は以下の様なものです。
  • 私のブログの読者層はセキュリティに関心の高い方たちであり、本書の読者とは重ならない
  • 著者に私の意図が伝わるかが疑問
  • 別の著者の方と書評が原因でトラブルになりかけたことがある
私は衝動的に、著者の谷藤さんの経営する株式会社C60(シーロクマル)に押しかけていって、ちょっとこの本は問題ですよと直接伝えようかとも思いました。前述のように別の著者の方とは書評が元でトラブルになりかけましたが、谷藤さんとはそうならないだろうという直感がありました。谷藤さんはプログラミングの初心者にPHPを習得してもらうことを通じて、若者の自立とキャリア形成の支援をされています。言わば、PHPの講習を通じて社会を少しでも変えようという活動をなさっている方なので、きちんとお話すれば、当方の意図はわかってくださるのではないかと思いました。
しかしこの時は、忙しさにかまけて、行動に移すことはありませんでした。

きっかけ

ところが、とある偶然から、ことは動き始めました。PHPカンファレンス関西2014のスピーカーを依頼されて、今年の6月28日に会場の大阪産業創造館を訪問した時のことです。自分の出番を終えて、ロビーでPHP界隈の方々と談笑していると、谷藤さんの方から挨拶に来られたのです。
名刺交換をしながら、私が「いや、実は私のほうから谷藤さんの会社に乗り込もうかと思っていたのですがね」と申し上げたところ、「ぜひ乗り込んできてください」とのお返事。その後頂戴したメールにもその旨記載されていたので、それではということで、谷藤さんとの面会が決まりました。

面会

7月某日、谷藤さんの会社で、リックテレコムの担当編集者を交えて谷藤さんと面会をいたしました。私からは、本書の内容にセキュリティ上の問題が多いこと、開発の現場で多くの開発者が「コピペ」でプログラムを作り、そのため「お手本」に脆弱性があると、その脆弱性ごとコピペされて脆弱なサイトがネットに公開されてしまうこと、などをお伝えしました。
谷藤さんからは、セキュリティが重要であることは承知しているが、自分の扱う受講生のレベルではセキュリティまでは無理であると考えていること、また開発現場のコピペの現状については承知しておらず、驚いているというお話がありました。
ここで、リックテレコムの担当編集者からは、実は本書を「プロ並み」というタイトルにしてしまったこともあり多くの問い合わせを受けていること、このため、フリーの電子書籍の形でFAQの副読本を発行する計画があることを伺いました。そして、徳丸の示した内容も、その副読本に含めさせてもらえないだろうかという提案をいただきました。
私はその提案に同意し、そのために私が調べた内容をメモとして提供することになりました。

副読本

私のメモは元々作成していたものをすぐにお渡ししたのですが、せっかく乗りかかった船なので、もう少し詳しく調べたいと考えました。本書のソースは、断片的な形でしか記載されておらず、実際に打ち込んでみないと全容がわからない状態でした。このため、当方からデジタルデータとしてソースコードの提供を希望し、承諾されました。私はお預かりしたソースを実際に動かしたり、ソースコードを確認するなどの方法でセキュリティ上の問題を調べ、先のメモに追記しました。また、当初脆弱性の疑いを持っていた箇所が、完全なソースを確認すると脆弱性ではないことがわかった箇所もありました。なお、現在は、こちらのページから、ソースコードがダウンロードできるようになっています。
私が提供したものは、あくまで内容のみを記した簡潔なメモでしたので、徳丸の負担を下げたいという担当編集者の配慮から、そのメモを「読み物」の形に仕上げる上では、編集という形で編集者の加筆がかなり入っていることを報告いたしましす。しかし、できあがった文章を私はチェックしておりますので、担当した第2章の文責は徳丸にあります。

概要

私の担当である副読本第2章の概要は下記のとおりです。

2-1 本アップには欠かせない——本書プログラムの脆弱性対策
 ◆P088 スタッフの削除機能にCSRF脆弱性
 ◆P100 商品追加機能に2種の脆弱性
 ◆P113ほか HTMLエスケープ処理の漏れ
 ◆P233 自動返信メールにメールヘッダインジェクション脆弱性
 コラム(1)「サニタイジング」という用語について

2-2 中級以上を目指す方へ——プログラム品質を高める改善案
 ◆P061・P113・P232 HTMLエスケープを行う場所について
 ◆P061 htmlspecialcharsの引数
 ◆P061他 データベース接続に例外処理の設定を
 ◆P065 データベース接続の文字エンコーディング指定方法
 ◆P100 正規表現による全体一致チェック
 ◆ 商品を購入したらカートは空になっているべき
 ◆ 商品画像のファイル名にルールがほしい
 ◆P268 注文データ漏えいの恐れあり
 ◆ 許可されていない文字がDBに登録されてしまう
 コラム(2) MD5によるパスワードの暗号化

まとめ、感想

一月ほど前に、はせがわようすけさんの以下のツイートに対して、
以下のように返答していますが、上記を想定した発言でした。

今回は、ひょんなきっかけから、「著者とのコミュニケーション」が実現されたことになります。私からは多くの指摘をさせていただきましたが、内容についての質問はあったものの、私の指摘はすべて掲載されています。谷藤さんと担当編集者の懐の深い対応に感謝申し上げます。

とは言え、課題は山積みです。私は開発入門者が最初に目にする機会の多いPHP入門書に注目してウォッチングを続けていて、以前から少し改善は見られる(参照)ものの、望ましい水準にはまだまだというところです。しかし、様々な形で著者の方々にセキュリティの重要性が伝わることで、改善が図られていくものと期待をしています。

*1 こちらのサイトにダウンロード方法が書かれています。個人情報として氏名とメールアドレスの入力が必要です。

2014年9月10日水曜日

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

高橋: こんにちは、高橋です。今日は徳丸さんをお招きして、今話題のパスワードの定期的変更について、本当のところ効果がないのか、その効能についてご説明いただきます。徳丸さん、よろしくお願いします。

徳丸: 徳丸です。いつもはパスワードの定期的変更にはあまり意味がないと主張していますが、今日はパスワードの定期的変更を擁護する立場なんですね。面白そうです。よろしくお願いします。

高橋: まず問題の整理についてです。IPAより9月3日に『「ID・パスワードのセキュリティ対策促進に関する広告等業務」 係る企画競争 』の仕様書(PDF)が公開されました。その仕様書中の行動喚起を促す対策事例の一つに「ID・パスワードは定期的に変更する」 があったので、セキュリティクラスタが騒ぎ出し、その結果かどうかは分かりませんが、9月9日に同仕様書が改定され、パスワードの定期的変更は対策例から削除されました。一連の議論についてはこちらを御覧ください。

徳丸: 高橋さん、最初は、パスワードの定期的変更の『効能』を説明する人選に僕を選ばれたのはどうしてだろうと思っていたのですが、考えてみると、僕は適任かもしれないと思いました。

高橋: ようやく気づいていただけましたか!

徳丸: 遅まきながら(笑い)。僕は、パスワードの定期的変更については批判的な立場ですが、公平な批判をするためには、パスワードの定期的変更の効能についても客観的に把握しないといけませんからね。

高橋: 確かに。

徳丸: しかも、パスワードの定期的変更を主張している人達は、従来、そうすべき理由をはっきり説明してこなかったのです。

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

徳丸: はい。説明があったとしても、簡単に論破できるようなものが多かったです。なので、パスワードの定期的変更の効能をそもそも何だろうとずっと考えてきました。私の過去のエントリについてはこちらを参照ください。

高橋: 最初のエントリは6年半前ですか。

徳丸: そうですね。考えてきた結果、パスワードの定期的変更の「効能」は以下の3つだろうと考えます。

  1. パスワード解読防止
  2. パスワードの漏洩後の緩和策
  3. 個人に割り当てられていないIDのパスワード管理の簡便法

高橋: それでは、1からご説明いただけますか? 1はすごく効能がありそうですが…

徳丸: まず前提として、これはオンライン攻撃ではなく、オフライン攻撃を対象にしています。

高橋: オフライン攻撃とは何でしょうか?

徳丸: オフライン攻撃というのは、ネットワークを通じてではなく、攻撃者の手元で攻撃することです。パスワードの場合は、暗号化されたパスワードあるいはパスワードのハッシュ値が漏洩して、それから元のパスワードを求める行為を指します。

高橋: パスワードが漏れちゃった後の話なんですか…

徳丸: そうです。しかし、昔のUNIXでは、それが当たり前だったのですよ。

高橋: え!? それは本当ですか?

徳丸: はい。UNIX系OSには /etc/passwd というファイルがあって、OSにログインしている人であればだれでも参照はできますよね。このファイルにパスワードのハッシュ値が格納されていたのです。

高橋: そうなんですか! passwdというファイル名なのにパスワードが入ってないなと思っていたのですが、昔は入っていたのですね。

徳丸: はい。/etc/passwdはログインユーザであれば誰でも参照できるので、時間をかければ元のパスワードが解読できてしまいます。それで、定期的にパスワードを変えないと現実的にまずいということがありました。

高橋: パスワードを定期的に変更する合理的な理由があった、と。

徳丸: はい。しかし、そもそもパスワードのハッシュと言えども、誰でも見られる状態で保存するのはまずいということになり、今では /etc/shadow など、rootのみが参照できるファイルにパスワードのハッシュ値が入っています。

高橋: 過去にはパスワードを定期的に変更する理由があったが、今はそうではない、と。

徳丸: 1に関してはそうなりますね。

高橋: わかりました。それでは、2について説明をお願いします。

徳丸: 2は、パスワードが「知らないうちに」漏れているかもしれないので、定期的にパスワードを変更すれば、「パスワードが漏れてない状態」になる、というものです。例えば、パスワードを三ヶ月毎に変更すれば、最長でも被害は三ヶ月で食い止められる…ということですが、実はそうとも言い切れません。

高橋: なぜでしょうか?

徳丸: パスワードが知らないうちに漏れていたとすると、パスワード変更後に再度漏れる可能性があります。

高橋: 必ずそうなりますか?

徳丸: そうとは限りません。パスワード漏洩がフィッシングによるものだとすると、二度目のフィッシングには利用者が気づく可能性もあります。また、既に攻撃側が目的を達していた場合、再度パスワードを得ようといないかもしれません。

高橋: なんか、微妙なラインですね。どういうシステムが、パスワードを定期的に変更したほうがよいのでしょうか?

徳丸: 社内ネットワークのWindowsアカウントのパスワードや、メールの(POP3の)パスワード等は、パスワードの盗用に気づきにくいし、長期間にわたって被害がある可能性が高いので、パスワードの定期的変更が有効という局面はありそうです。

高橋: そういえば、Windowsはポリシーでパスワードを強制的に定期的変更させる機能がありますね。

徳丸: はい。パスワード定期的変更懐疑派の筆頭格であるシュナイアー氏も、会社のパスワードは時々変える(You should change your corporate login password occasionally)ことを勧めていますね。

高橋: 徳丸さんも会社のパスワードは時々変えているのですか?

徳丸: いえ、変えていません。

高橋: ガタッ、だめじゃないですか。

徳丸: 私の会社は、私一人しかいないのでPCはほぼスタンドアロンの利用ですよ。リモートログオンできるわけではないので、脅威はほぼマルウェアに限定されますから、パスワード変えても脅威は減らないです。

高橋: なるほど、環境によってパスワード変更の重要度が変わってくるのですね。

徳丸: そうです。だから、インターネット上のPOP3のパスワードは先日変更しました。と言っても、SSLのみ許可しているし、不正アクセスの可能性は低いので、マルウェアの脅威くらいしかないのですが、これはまぁ気分の問題です。

高橋: 気分ですか、その程度の重要性ということですね。そういえば、先ほど、被害の期間が長期間にわたってとありましたが、被害が短期間が終わってしまう場合があるのですか?

徳丸: あります。spam投稿が目的のなりすましや、代表例は不正送金ですね。

高橋: なぜでしょうか? 長期間に渡って不正送金できたほうが、犯人は嬉しいでしょうに。

徳丸: さすがに悪用された側が気づくでしょう。

高橋: さるセキュリティ専門家は、自分のクレジットカードは(利用額が大きいので)20万円くらい不正使用されても気づかないだろうと豪語されていたようですが…

徳丸: そういう方は、パスワードを定期的に変更するより前に、自分の口座を定期的に確認する必要がありますね。

高橋: それもそうですね。

徳丸: しかし、オンラインバンキングでも、法人口座の場合は定期的変更が有効になる可能性がありまして、それが3番の理由になります。

高橋: なぜでしょうか?

徳丸: 法人口座の場合は、口座の管理者が複数いて、複数の人がパスワードを共有している場合があるからです。

高橋: どうして定期的変更になるのでしょうか?

徳丸: 複数の管理者のうち1人が退職したり、異動になった場合、「パスワードを知っていてはいけない」状態になりますよね。パスワードを例えば3ヶ月毎に変えれば、「パスワードを不正に知っている」状態は最長でも3ヶ月で止まります。

高橋: 銀行口座の場合だと3ヶ月でも長過ぎますね。かといって、毎日パスワードを変更するわけにもいかないし…

徳丸: その通りです。幸いなことにもっと良い方法があります。

高橋: なんだ、その方法を教えて下さい。

徳丸: 一番良いのは、担当者毎にアカウントを分けて別のパスワードにすることです。たとえばジャパンネット銀行の場合ですと、「利用者ID機能」というものがあり、同じ口座に対して利用者毎にIDを変えることができます。

高橋: それは素晴らしいですね。担当者が退職したら、その方のIDを解除するか、パスワードを変更して別の人が使えば良いわけですね。他の方のパスワードは変更しなくてもよい、と。徳丸さんの会社でも「利用者ID機能」を使っているのですか?

徳丸: だーかーらー、私の会社は私一人しかいないので、使う必要がありません。

高橋: でしたねー。でも、そういう機能が利用できない場合は、定期的に変更するしかないのでは?

徳丸: いえ、そうではありません。共用のパスワードを知っている人の退職や異動のタイミングでパスワードを変更するべきです。そうすれば、「パスワードを不正に知っている人」をゼロにできるはずです。理論的には…

高橋: 歯切れが悪いですね。

徳丸: 今までの話は、職務上パスワードを知っているはずの人の話ですが、元々パスワードを知っているはずのない人がパスワードを知っている場合に、定期的にパスワードを変更すると、その人の知っているパスワードを無効にできますね。

高橋: しかし、パスワードを知っていてはいけない人がパスワードを知っていること自体が重大な問題だと思いますが…

徳丸: はい。でも現実にはあり得ますよね。サーバー管理者が外出中にrootで作業をしなければならないのに、リモートからログインできないので、同僚にrootパスワードを教えて作業を代行してもらうケースとか。

高橋: はー、教科書的にはだめだけど、現実にはありそうです。

徳丸: なので、元々管理レベルが低い場合は、パスワードの定期的変更がそれなりに有効、ということですよ。言い換えると、パスワードの定期的変更に頼る状態というのは、けっして好ましいことではありません。

高橋: 分かりました。ここまでのお話を一旦整理すると、昔の/etc/passwdにパスワード自体が入っていた時代はパスワードを定期的に変更する理由があったが、今はそうではないこと、同じパスワードを複数人で使う場合にパスワードの定期的変更がそれなりに意味があるが、本来は一人1IDにするか、異動・退職のタイミングでパスワードを変更するべき、ということでした。社内ネットワーク等のパスワードは、環境にもよるが、パスワードの定期的変更がそれなりに有効、ということですね。

徳丸: その通りです。

高橋: 徳丸さんのご専門のWebシステムの話題があまり出てきませんでしたが、Webの場合はどうなんですか?

徳丸: Webの場合でも、パスワード漏洩の影響が長期間にわたって出て、かつ漏洩に気づけないというケースは、パスワードを定期的に変更した方がよい、ということになりますね。

高橋: あれ、パスワードの定期的変更を認めちゃうんですか?

徳丸: 残念ながら…でも、パスワードの定期的変更よりも良い方法がある場合はそちらを使うべきです。

高橋: 例えばどういうものでしょうか?

徳丸: 例えば2段階認証ですね。2段階認証が使える場合は、パスワードを定期的に変更する必要性はほとんどないでしょう。

高橋: 2段階認証に使うトークンを一種のパスワードだとすると、一分毎にパスワードが変わっているようなものですからね。

徳丸: そうです。パスワード漏洩の影響が長期に及ぶアプリケーションというのは、メールとかストレージサービスですね。その種のサービスは2段階認証が普及しつつあります。一方、被害が個人情報漏洩だけというサイトは、パスワードを変更しても被害は軽減できないので、定期的変更にあまり意味がありません。次に、ログイン通知やログイン履歴の機能があれば、不正ログインに利用者が気づくことができるので、パスワードの定期的変更の必要性は薄くなります。

高橋: 定期的にチェックするのも面倒だし、忘れそうですね。

徳丸: そうですね。この点、Yahoo!やLINEがやっているログイン通知は便利ですよ。自分ではなにもしていないのに、「ログインしました」と通知されるので、気づきやすいです。

高橋: でも、ログイン通知を実装しているサイトは少ない感じですね。

徳丸: そうなんです。だから、サイト運営者は、利用者にパスワードの定期的変更を促す前に、2段階認証やログイン通知の機能を実装していただきたいです。

高橋: そうですね。それでは、そろそろまとめていただきますか?

徳丸: はい。結局のところ、パスワードの定期的変更の位置づけとしては下記のパターンがあると考えます。

  1. もっとよい方法がある(例: 二段階認証)
  2. 元々の管理レベルが低い場合の簡便法(例: パスワードを勝手に知っている人がいる)
  3. 社内ネットワークなど不正に気づくことが難しく被害が長期に及ぶ場合の対策

    1と2については、もっとよい管理方法に移行するべきです。3については、パスワードの定期的変更の動機としてはもっともありそうですが、もし本当に高いセキュリティを目指すのであれば、スマートカードログオンに移行するなど、パスワードの定期的変更に頼らないセキュリティ施策を採用するべきです。私が一つお勧めしたいのは、パスワードを三ヶ月おきに変更するような機械的な定期的変更ではなく、パスワード管理のあり方を定期的に見直すことです。たとえば、フィッシングが問題になってきて二段階認証の普及が進んできたら、自分でも二段階認証を使うかどうかを検討する、パスワードリスト攻撃が問題になってきたら、自分でもパスワードの使い回しをしていないかチェックして、是正する、などです。機械的にパスワードを変更することは、意味が無い場合が多いですよ。

高橋: ありがとうございました。パスワード管理を定期的に見直すというのはいいですね。これで、パスワード定期的変更の効能についての徳丸さんへのインタビューは終わりです。みなさま、ごきげんよう。

※注: このエントリはインタビュー仕立ての記事であり、文責はすべて徳丸にあります。高橋は架空の人物です。

フォロワー