7月に伊原さんからケーキオフの「WebAppへの攻撃は見つけられるのか?」の回への議論参加のお誘いを頂いていたのだが、ずいぶんはるか先の話だなあと思っているうちに忘れてしまい、それが今日だったことを知った。あれからもう2か月が過ぎ去ったのか。突っ込みコメントできることはありそうだったので行きたかったのだが、痛恨。
というわけで、資料とビデオを拝見した上で、以下コメント。
最後の「まとめ」として、「絶対に見つけられない攻撃は存在しないはず」と述べられているが、それは正しくない。
セッションIDを盗むまでもなくパスワードなしにログイン状態に入れてしまうタイプの欠陥を突いた攻撃については、一定時間以内前にパスワード送信のアクセスがあったかどうかをログから調べれば攻撃の有無を調べられるだろう。しかし、正規ユーザがログイン中のタイミングでのセッションハイジャック攻撃は、その方法では攻撃の有無を調べることができない。
国分氏の講演では、セッションハイジャックを見つける方法として、「Set-Cookie: で発行したセッションIDと異なる Cookie: が送られてきた場合を探す」ということが述べられているが、攻撃者は、そもそも Set-Cookie: を発生させるようなアクセス(通常、これはパスワード送信時に起きる)をすることなしに、最初から直接に、他人用のセッションIDを Cookie: としたリクエストを送信するのだから、この方法では検出できないだろう。
また、「同じユーザから何度もアクセスがある中で、セッションIDが途中から違っているものを見つけられるかもしれない」ということも述べられているが、問題は、「同じユーザ」というのをどうやって判別するのかだ。そもそも、同じユーザであることを識別するためにセッションIDを必要としているのであるから、「同じユーザからのセッションIDが変化した」ということを完全に検出することは本質的にできない。
では、「同じユーザ」を、同じIPアドレスからのアクセスかどうかで判別する方法はどうだろうか。
まず、同じIPアドレスから異なるセッションIDが飛んでくることは、ごく日常的な事態のはずだ。企業内ユーザのProxyサーバ経由のアクセスや、規模の大きなNATごしのアクセス(一部のケーブルテレビ接続の場合など)の場合には、当該ドメインから複数のユーザがログインしていれば、そうした事態は普通に起きる。それを攻撃とみなすわけにはいかないだろう。
次に、同じセッションIDが異なるIPアドレスから飛んできた場合を攻撃とみなすという考え方がある。しかし、ダイヤルアップ接続のユーザならば、一旦電話を切ってデータ入力をし、再び接続してボタンを押すという、ケチケチアクセスをするかもしれない。その場合、同じセッションIDが異なるIPアドレスからやってくることになるので、これを攻撃とみなすわけにはいかない。
これを「攻撃の可能性がある」とみなしてアクセスを拒否してしまうという対策はアリかもしれない。しかし、そうした対策をとっているサイトは極めて少ないようだ。
まず、企業内ユーザからのProxy経由のアクセスは、アクセスごとにIPアドレスが微妙に変化することが多い。Proxyが複数台のサーバで負荷分散されている場合に、アクセスごとに異なるIPアドレスのサーバを経由することがあるからだ。(この現象は、普通のWebサーバのアクセスログから、大手企業からのアクセスのログを探して、ざっと眺めてみるだけでも知ることができる。)したがって、単純に完全にIPアドレスが一致するかどうかで判別すると、Webアプリが正常に動かなくなってしまう。そこで、一定のアドレスブロックの範囲内であれば同一とみなすという対策が考えられる。こうした対策をとっているところは、少数派ではあるが、存在はする。しかし、同じダイヤルアップ接続プロバイダ内からの攻撃を判別することができない。より厳密に対策したければ、アクセス元が接続プロバイダなのかProxyなのかといった情報などから、細かく条件別に判別するようにするしかないだろう。
そして、この方法で対策をとったとしても、セッションハイジャック攻撃を完全に防げるわけではない。なぜなら、同一企業内、同一接続プロバイダ内、同一NAT内からの、第三者アクセスを判別することはできないからだ。同様に、事後に攻撃がなかったことを証明することも不可能だ。
では、セッションIDを盗み出す時点で攻撃を見つけるというのはどうだろうか。当該サーバのクロスサイトスクリプティング脆弱性を突いた攻撃は、POSTアクションも含めたログから検出することができるだろう。しかし、cookieを盗まれるのは、サーバ側のクロスサイトスクリプティング脆弱性だけではない。ブラウザのセキュリティホールによって盗まれることもあるし、ユーザ側のProxyのクロスサイトスクリプティング脆弱性によって盗まれることもあるし、secure属性の付いていないcookieをパケット盗聴で盗まれることもある。これらはサーバ側では検出できない。
次に考えられる対策は、セッションIDとは別に、シーケンス番号をcookieなどでブラウザに渡す方法だ。ここで言うシーケンス番号とは、アクセス1ページごとに変化させるもので、同じセッションIDに対して同じシーケンス番号のアクセスが2度以上きたら異常事態とみなして、アクセスを拒否する方法だ。攻撃者がセッションハイジャックするとき、正規ユーザよりも先に「次ページ」をアクセスした場合には、残念ながらアクセス拒否とならないが、正規ユーザがその後に「次ページ」にアクセスした時点で異常は検出できる。
しかしこの対策がとられていたとしても、ユーザがログアウトボタンを押さずにウィンドウを閉じた場合には、攻撃者はその後を見計らってハイジャックアクセスするようにすれば、バレることなく攻撃を成功できてしまう。特に、パケット盗聴が可能な状況では、そうしたタイミングを見計らうこともできるだろうし、ブラウザのセキュリティホールを突いてcookieを盗む場合には、盗んだ後ブラウザを終了させてしまうこともできるだろう。
講演では、「ひとり分の情報だけ吸い出して満足することはないだろうから、次々と連続して複数のアカウントのデータを吸い出すだろうから、そうしたアクセスを見つける方法がある」と述べられていたが、それはその通りだろう。攻撃者が、一人分で満足する場合にはその限りでない。
総合的に言うと次のシナリオが最もバレない攻撃となる。攻撃者が被害者のパケットを盗聴できる状況にいる場合。この場合には、同じIPアドレスブロックから攻撃のアクセスができる場合が多いだろう。仮にシーケンス番号対策がとられていたとしても、ユーザのアクセス行動をウォッチできるのだから、シーケンス番号対策を回避できる。そして、攻撃対象Webアプリに、セッションID用cookieにsecure属性を付けてない欠陥があるならば、それを盗聴してセッションハイジャックされると、攻撃を検出できない。
一方、安全なWebアプリを作ることはできる。cookieでセッション追跡を実装すると、仮にWebアプリに欠陥がなくても、ユーザのブラウザのセキュリティホールを突いてセッションIDを盗まれる可能性があるが、セッションハイジャックされても、重要な情報を盗まれないような画面構成にすればよい。すなわち、重要な画面に入るには再度のパスワード入力が必要なようにし、そこのセッション追跡にcookieを使わないようにする。もちろん、その部分の仕組みに別の欠陥があってはならないのだが。