Hatena::ブログ(Diary)

ockeghem(徳丸浩)の日記 このページをアンテナに追加 RSSフィード

[PR]WAFの導入はHASHコンサルティング
 |  

2010-05-07

“完璧なWAF”に学ぶWAFの防御戦略 “完璧なWAF”に学ぶWAFの防御戦略 - ockeghem(徳丸浩)の日記 を含むブックマーク

セキュリティExpert 2010に大河内智秀氏が「現状の課題と“完璧なWAF”」と題して寄稿されている。大変興味深い内容であるので、この寄稿をなぞりながら、WAFの防御戦略について検討してみたい。

クロスサイト・スクリプティング(XSS)に対する防御

大河内氏の寄稿の前半は、現状のWAFの課題として、Webアプリケーションに対する攻撃の多く(大半)がWAFのデフォルト設定では防御できないと指摘する。例えばクロスサイト・スクリプティング(XSS)に関しては、以下のような指摘がある。

仮にscriptブラックリストに指定したとしましょう。それでもまだ不十分です。<IMG>タグでXSSが発動することをご存じでしょうか?プログラムなどでは<IMG>タグは画像添付に必須であり、WAFで禁止することは難しいのが実情で、ブラックリスト方式の課題となっています。

「現状の課題と“完璧なWAF”」より引用

「<IMG>タグでXSSが発動」が具体的にどのようなパターンを指すのか曖昧だが、XSS Cheat SheetにはIMG要素によるXSSがいくつか紹介されているので、このようなパターンを指しているのかもしれない。

<IMG SRC="javascript:alert('XSS');">
<IMG SRC=javascript:alert('XSS')>
<IMG SRC=JaVaScRiPt:alert('XSS')>
<IMG SRC=javascript:alert(&quot;XSS&quot;)>
<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">
<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>
<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>
<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>
<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>
<IMG SRC="jav ascript:alert('XSS');">
<IMG SRC="jav&#x09;ascript:alert('XSS');">
<IMG SRC="jav&#x0A;ascript:alert('XSS');">
<IMG SRC="jav&#x0D;ascript:alert('XSS');">
【略】

一見複雑に見えるが、ほとんどのパターンはjavascript:という擬似スキームそのものや、これに難読化を施したものであるので、そのような手がかりを元にシグネチャを構成できる。しかも、XSS Cheat Sheet自体が業界で有名なドキュメントなので、WAF制作者たるもの、ここに載っているパターンくらいは対策すると考えるのが自然だろう。私がかつてWAFを評価した際にも、ここに出てくるパターンを試したところ期待通りにブロックしてくれていた。

大河内氏の文章には前提条件が書かれていないのでコメントしにくい。例えば、安全なウェブサイトの作り方には、「HTMLテキストの入力を許可する場合の対策」という項があるが、そのようなケースではWAFによる防御は難しい。大河内氏の意図が、IMG要素そのものをユーザが入力するケースを想定しているのであれば、「HTMLテキストの入力を許可する場合」に該当する。しかし、HTMLテキストを許可するアプリケーションは一部のブログや掲示板など特殊なアプリケーションなので、WAFがこれらに対応できないからと言って、WAFがダメダメだと主張するのも酷だろう。それに、IMG要素を使ったXSSにWAFは対応しているわけなので、「<IMG>タグは画像添付に必須であり、WAFで禁止することは難しい」という書き方はおおざっぱに過ぎ、適切な表現とはいえないだろう*1

SQLインジェクションに対する防御

次にSQLインジェクションに関する説明を読んでみよう。

SQLインジェクション対応の駄目な例として挙げられるのは、複文などの直接攻撃しか考慮していないケースです。簡単に言うと、master..execなどの直接攻撃のみ禁止しているWAFは甘く、union selectなどのunionインジェクションを考慮したWAFはまだマシであると言えます(攻撃者の観点が分かっているという意図でマシと記載しました)。

このブラックリストを通り抜けるにはコメントを使用します。たとえば、union/**/selectではどうなるでしょうか? 残念ながらほとんどのWAFは通り抜けてしまいます。

「現状の課題と“完璧なWAF”」より引用

「master..execなどの直接攻撃のみ禁止しているWAFは甘く」という興味深い記述があるが、私は寡聞にしてそのようなWAFを知らない。ぜひどこの何というWAFが該当するのか教えていただきたい。

また、unionインジェクションに関する記述も興味深い。私がかつて評価したWAFは全てunionインジェクションをブロックしたが、「union select」という並びだけでブロックするWAFは、F5 BIG-IPとImperva SecureSphereだけだった*2。残りの

Citrix Application Firewall、Barracuda Web Application Firewall、JP-Secure SiteGuardの三製品は、「union select」という並びが入力にあってもブロックしない。

一方、「union/**/select」という並びでは、BarracudaおよびSiteGuardはブロックするのだ*3

「union select」だけだとブロックしないが、「union/**/select」だとブロックする。これは、過剰検知(False Positive)対策だろうと思う。unionやselectは非常に基本的な英単語なので、これを単純にブロックしてしまうと、ユーザの正常な入力もブロックする恐れがある。例えば以下のような文はどうか。

G.M. union select the choice.

selectではなくて(三単現の)selectsじゃないのかというツッコミはご容赦いただくとして、上記のような英文を攻撃と誤検知すると運用に支障が出る。そこで、現代的なWAFは単純なunion selectの並びだけではなく、前後の文脈を考慮してSQLインジェクションとして成立するかどうかを判定するように進化しているのだ。このようなWAFは、「union select」だけならブロックしないが、「union select hoge, fuga, null, 1 from usermaster」ならブロックする*4。そして、どこまでならスルーし、どこからならブロックするかの境界は過剰検知との兼ね合いのさじ加減で決まってくるので、WAFベンダーシグネチャ作成者が苦心して決めていると推測する。

一方、union/**/selectの方は、一般の英文として出てくることはまずないだろうし、SQLインジェクションの難読化とみなすことができるので、多くのWAFがブロックする。

つまり、「union selectならブロックするが、union/**/selectはほとんどのWAFは通り抜ける」という大河内氏の主張はWAFの現状を反映しておらず、むしろ「union selectは(わざと)通すがunion/**/selectはブロックする」がWAFの現状としては正しい。大河内氏は「ほとんどのWAFは通り抜ける」と断定するのであれば、具体的にどのWAFがそうなのかを示す責任があるだろう。

クロスサイト・リクエスト・フォージェリ(CSRF)に対する防御

CSRFに関して大河内氏は以下のように記述しておられる。

ではWAFでは何が防げないでしょうか。まずクロスサイトリクエストフォージェリは、ユーザが意図しない行動を起こしてしまうものであり、リクエスト自体は正常なリクエストとして扱われ処理されます。つまり、ユーザが意図しないだけであって、リクエスト自体には問題はありません。こういった攻撃はWAFでは防御できないのです。

「現状の課題と“完璧なWAF”」より引用

こういう大ざっぱな議論をしてしまうと、XSSSQLインジェクションだって「リクエスト自体には問題は」なく、WAFで防御できないことになってしまう。それはともかく、CSRFがWAFで防御できないという状況は、以下のどの状況を指しているのだろうか。

  1. 原理的にWAFで防御できないことが証明されている
  2. まだ有効な防御アルゴリズムが発見されていない
  3. 有効なアルゴリズムがあり一部のWAFでは実装されているが設定・運用が大変なので普及していない
  4. その他

大河内氏の書き方だと、CSRFは原理的にWAFでは防御できない、すなわち(a)パターンのように読めるが、それは誤りだ。

金床氏の著書「ウェブアプリケーションセキュリティ」のP119には以下のような記述がある。

クロスサイトスクリプティングSQLインジェクションと異なり、CSRFはWAFで確実な対策を行うことが可能だ。ここでは WAF(Guardian@JUMPERZ.NET)を用いたCSRF対策について具体的に説明する。

この記述に続いて、Guardian@JUMPERZ.NETのCSRF防御戦略と設定方法が説明されている。それは、Webページにトークンを自動的に埋め込んで、フォームの受け取り側でチェックするというものだ。トークンをチェックするページは明示する必要があり、設定は少々面倒となる。このため、上記の分類では(c)となる。また、トークン利用の他、Refererをチェックする方式でも実装は可能だろうが、私はそのようなWAFを見たことはない*5

大河内氏はおそらく金床本を読んでおられないのだろう。日本でWAFビジネスをされるのであれば、金床本くらいは一度目を通しておかれることをお勧めしたい。

アクセス制御不備に対する防御

アクセス制御(認可)の問題については、大河内氏は以下のように記述しておられる。

アクセス制限の不備もWAFには判別不可能です。たとえば管理者ページを認証機構なし設置し、単にそのほかのページからリンクしていないだけ、というケースを考えてみましょう。この場合、URLを知っている者のみがアクセス可能ですが、それが容易に思いつくものか思いつかないものか、いずれであってもWAFには通常のWebページと区別できないため防御できません。

「現状の課題と“完璧なWAF”」より引用

この内容について検討してみよう。

アクセス制御(認可)の不備について、ウェブ健康診断仕様では以下の3種類の診断パターンを用意している。

  1. URL操作により、現在のユーザでは実行権限のない機能が実行可能
  2. 文書ID、注文番号、顧客番号等がパラメータにより指定されている場合、そのID 類を変更して、元々権限のない情報を閲覧できるか
  3. hidden, Cookie に現在権限が指定されており、その変更により現在のユーザでは実行権限のない機能が実行可能

大河内氏の指摘は、上記分類では(1)に該当する。しかし、現実のWAFには、ウェブ健康診断仕様で規定された認可不備のパターンを全て防御できるものが多い。当然ながら、大河内氏の指摘したパターンも防御できる。

この種の防御がもっとも得意なWAFは、Citrix Application Firewallであろう。Citrixの場合は、セッション毎に「次に遷移して良いURLの集合」を管理していて、A要素やフォームのアクション属性などにより参照されていないURLに遷移するとエラーにする(ブロックする)ことができる。これは(1)に対する防御として有効だ。また、CookieやHiddenフィールドに対する保護機能があるので、(2)や(3)に対する攻撃を検知し、ブロックすることができる。Citrixの他、JP-Secure SiteGuardなどもこれらの機能をもつ。下図に、SiteGuardの設定画面を引用する。下図で、「URL遷移検査」というのが上記(1)に対する防御、「フォーム変数検査」が(2)および(3)に対する防御となる。ただし、Cookieに関してはこの設定では保護されないので、「Cookie暗号化」という機能で防御することになる*6

f:id:ockeghem:20100505225854p:image:left

このように、WAFには典型的な認可不備に対する防御機構が用意されている。したがって、「WAFには通常のWebページと区別できないため防御できません」という指摘は誤りだ。

もっとも、認可処理はセキュリティ機能の根幹といえる部分であるため、アプリケーションの認可機能に欠陥を残したままWAFだけの防御で運用することは不安だという議論はあるだろう。また、認可不備は上記3パターンだけでなく色々なバリエーションがあるので、WAFで常に防御できるわけではない。しかし、それならそのように表現すれば良いだけの話で、WAFが原理的に認可不備を防御できないような表現は誤りだ。

チューニングについて

大河内氏の寄稿の後半は、“完璧なWAF”を実現するチューニングの話題に移るのだが、このチューニングの部分もかなりユニークだ。氏は、初期状態のWAFは非常に不完全な状態で、チューニングにより完全な状態にする必要があると指摘する。

WAFの初期状態(いわゆる最低限のブラックリストのみ)ではおおよそ50%程度の数値であると表現できます。チューニングをすることで60〜70%まで上げることができるでしょう。チューニングをきつくすると、120%となりサイトの機能が一部動作しなくなります。では、残りの30〜40%のみ上げるにはどうすればいいのでしょうか。

筆者は検知システムと同様、WAF+チューニング+サービスで100%に限りなく近づけることができると考えています(図3)。

f:id:ockeghem:20100505225853p:image:left

図3●適切なシグネチャチューニングとサービスの連携により100%を目指す

「現状の課題と“完璧なWAF”」より引用

短い文章の中にユニークな視点がちりばめられている。以下検証する。

WAFの初期設定に対する考え方

図3によると、“完璧なWAF”は、購入当初ではFalse Negative(検知漏れ)気味に設定されていて、チューニングより検知率を高めるようになっている。一方、現在市販されているWAFには、購入当初はFalse Positive(過剰検知)になっていて、導入後のチューニングで過剰検知しているシグネチャを外すことにより適正な状態に設定できるようになっているものがある。F5 BIG-IPやImperva SecureSphereがそのような製品の例である。さらに、JP-Secure SiteGuardやBarracuda Web Application Firewallは、極力初期設定のままチューニングなしで使えるというコンセプトの製品である。先に紹介した「union select」の例がわかりやすい。F5とImpervaは「union select」をブロックするので、導入後の検証中に過剰検知が判明すれば、該当のシグネチャを外すわけである。一方、SiteGuardとBarracudaは、はじめから「union select」のように過剰検知しやすいシグネチャが設定されていないのだ。

私は、SiteGuardやBarracudaのようにチューニングの労力が少ない製品が好ましいと考えている。一方、“完璧なWAF”のように、初期状態で50%のできで、チューニングとサービスで100%にもっていくというコンセプトは斬新だ。

なぜチューニングシグネチャを初期シグネチャに含めないのか

ここで一つの疑問が生じる。後から追加するシグネチャは、最初からセットしておいては駄目なのか。大河内は、自社で開発したWAFを自社でチューニングするという前提で寄稿している。であれば、「チューニングシグネチャ」を「初期シグネチャ」に含んでしまえば、煩わしいチューニング作業が省略でき、導入コストも低減できることになる。

その理由として私が思いついた仮説を二つ紹介する。一つは、“完璧なWAF”は現在開発中なので当初は完成度が低く、チューニングと称して完成度を高めていくという仮説だ。しかし、この仮説はあまりにも顧客不在の考え方であって、おそらく違うのだろう。

もう一つは、「チューニングシグネチャ」は過剰検知が多く、初期シグネチャには加えられないものだという仮説だ。F5やImpervaは、このようなシグネチャも初期シグネチャとして加えておいて、チューニング時に削除するという考え方であるわけだが、“完璧なWAF”は、初期シグネチャには加えずに、チューニング時に追加するということらしい。だが、どうやって。

False Negativeをどうやって検知するのか

ここで生じる疑問は、False Negativeを検知する方法があるのかということだ。本寄稿のP99には以下のような記述があるのだが、

運用については、まず誤検知(False PositiveとFalse Negative)に対応できる自社監視チームや監視サービスが存在するかどうかがポイントです。

「現状の課題と“完璧なWAF”」より引用

False Positiveの方はWAFが検知しているから監視できるのは分かるが、False NegativeはWAFが検知していないので、監視しようがないのではないか*7。F5やImpervaが初期状態でFalse Positive方向に設定されているのは、WAF自体で誤検知を検知できるようにという配慮だろう。“完璧なWAF”が斬新だと思うのはこのような部分であって、WAFが検知していないFalse Negativeを検知できる特別な監視手法があるのかもしれない。まことに興味深い内容であり、“完璧なWAF”のリリースが待ち遠しい。

サービスで補うとは具体的にはどういう意味か

さらに、チューニングシグネチャでも防御できない30〜40%を補う「サービス」についても興味深い内容だ。しかし、そのサービスが具体的に何を指すのか、本寄稿を読んでもよくわからない。P99には以下のような記述もあるのだが、

WAFで守れない部分については、最大手ネット通販会社などで採用されているセキュリティ教育コンテンツの講師が、独自のノウハウを盛って指導を行う予定です。

「現状の課題と“完璧なWAF”」より引用

ここを読む限り、WAFで守れない部分はアプリケーション側で対策すべく、開発チームを教育してくれるということだろうか。しかし、これではWAFで守ったことにならないので、“完璧なWAF”と銘打つのは誇張が過ぎ、違うような気がする。さらに、先に引用した図3を見ると、第三段階ではサービスによる防御の割合が縮小し、チューニングシグネチャによる防御の割合が大幅に増えている。これは本文中でも、

こうしたポイントに加え、監視チームやアプリ開発社からのフィードバックを受けてチューニングを継続していくうちに、100%に近づくチューニングを実施できるのです。

「現状の課題と“完璧なWAF”」より引用

とあるので、なにか特別な「サービス」があるのかもしれない。仮に、開発者が教育を受けてアプリケーション側で対策ずみなのであれば、わざわざWAF側でチューニングして(当然費用が発生する)対応する必要はまったくないからだ。

まとめ

大河内氏の“完璧なWAF”の寄稿をなぞる形で、WAFの防御戦略について検討した。前半の「WAFで防御できない攻撃」については若干の誤解もあるようだが、本論の“完璧なWAF”については、他に類を見ない極めてユニークな製品に仕上がる予定のようだ。大河内氏の文章がへたくそなためか、私の理解力が不足しているせいか、“完璧なWAF”の詳細についてはまだ不明な点が多い。製品リリースのあかつきには、是非詳しい内容を紹介していただきたい。

*1:元のタイトルが“完璧なWAF”となっているので、IMG要素も完璧な対応を求めているのかもしれない。だとすると、HTMLテキストの入力を許可するアプリケーションにも対応する“完璧なWAF”は驚くべき性能を持つことになる。はてなダイアリーは度々XSS脆弱性が指摘されているようだが、“完璧なWAF”を導入すれば解決するのだろうか

*2WAFでWebアプリの脆弱性を守れるか参照

*3:Citrixについて未調査

*4:厳密には、SQLインジェクション攻撃が成立するためには、unionの前にシングルクォートか式が必要だ

*5:手動でルールを追加する方法であれば、多くのWAFで対応可能であると思われる

*6デフォルトではいずれもオフになっていて、導入時に設定する必要がある

*7:False Negativeを調べる方法として、WAF越しに脆弱性診断をするという方法があるが、寄稿では触れられていない。このアプローチだと、網羅的に診断しないとFalse Negativeの洗い出しが出来ないので、コストはかなり高くなる。

cdcd 2010/05/25 20:58 一経営者としてあまりに観点の違いがあり指摘したい。結局のところ貴殿はどうしたらよいかの発言がないのはどうしてか?揚げ足だけとっているだけではないか。大河内氏は役職をみたところマネージャでありエンジニアではない。大きな視点での会社のサービスアピール寄稿だと考えるべきではないか。貴殿は育ち盛りの若いエンジニアだと推測されるが経営者とは思えない発言である。今後貴殿は出版元である技術評論社や大河内氏の所属する組織と仕事をすることはないのであろうが、あまりに幼稚すぎる。エンジニアな観点はよくわからないが、確実に誤りと判断されることがないのであれば(少なくとも常識人であれば)公の場での発言はないのではないか。前提条件が不明確なら個人的に問い合わせを行い、結果を載せるのであればまだ理解できるが貴殿の対応はこのような発言を私が行いたくなるほどいただけない。

ockeghemockeghem 2010/05/27 00:40 cdさん、コメントありがとうございます。私の文章を読んで不快の念を覚えられたようで、お詫び致します。
確かに大河内氏はエンジニアではないと理解していますが、そんなことは内容の不正確さに対する言い訳になるのでしょうか。セキュリティExpertは1580円もする専門書です。『大きな視点での会社のサービスアピール寄稿だと考えるべきではないか』ということですが、これは記事広告という意味でしょうか? 記事広告ならば、これは記事広告であるということがわかるように書くのが業界のルールだと思いますが、記事広告だとは読み取れませんでした。記事広告と分かるように書いてあれば、私も大人気ない書き方はしなかったと思います。
『今後貴殿は出版元である技術評論社や大河内氏の所属する組織と仕事をすることはないのであろうが、あまりに幼稚すぎる』というご指摘、『幼稚すぎる』というご批判に対してはまぁそうかなと思いますが、大河内氏の所属する組織とは過去に仕事をご一緒したことがあり、今後もその可能性はあります。そのような状況下で、私は実名を明らかにした上で記事の批判をしているのです。
『エンジニアな観点はよくわからないが、確実に誤りと判断されることがないのであれば』ということですが、私は確実な誤りについての指摘をしています。それがわかりにくかったということであれば、私の文章がへたくそだったのでしょうが、本名を明かしてブログを書き、批判相手の所属する組織に知り合いがいっぱいいる状態では、不確かなことは書けません。そういう状況下で、責任を負った上で書かざるを得ない状況だったわけです。あまりにも技術的に不正確な内容で、所属組織の名声を傷つけるような内容だったからです。
私も一経営者として、本名を公開してリスクをとった上でモノを書いています。大河内さんも実名と組織の看板を背負ってモノを書いています。ですから、対等な立場だと思っています。ですから、cdさんも経営者ということであれば、実名で議論をしませんか?

cdcd 2010/05/28 00:00 意図が伝わっていないのが貴殿の将来にとって残念だと思い、貴殿の為に今一度時間を割いてコメントをする。
貴殿は前提がわからないと言い訳をした上で指摘をしている。確実な誤りというのであれば前提が〜という言い訳は必要ないのではないか。前提が正しいとした推論上での確実な誤りは確実な誤りといえず、記事全体に誤った印象を与え誹謗中傷しているのではないか。前提が貴殿の考察と違っていれば、貴殿が誤った指摘をしても貴殿がせめられることはなく(前提が違ったと言い訳できる)、逆であった場合は貴殿は損はしないとなんともズルい書き方で発言をしている。どうして前提が不明であれば、なぜ確認した上で発言をしないのだろうか。なぜそんなズルい発言をするのであろうか。
仮に貴殿の発言が正しいと仮定して出版社からみて非公式の場で(この場で)貴殿が指摘をするよりも、貴殿が出版社や寄稿者に指摘し、出版社や寄稿者に対応をしていただいたほうが寄稿者や出版社、読者にたいしてよい結果となるとの考えにはならないのだろうか?こういったところを大人気ないと言っているのである。
広告記事について広告記事と書かない広告だから宣伝効果があると考えないのだろうか。専門家である貴殿が見ても判断つかない記事であるからよくできている記事なのではと思う。
貴殿が経営者と名乗るのであれば、寄稿者の立場を考慮して立場にあった発言をするべきである。寄稿内容についても出版社が求める事項と文字数の制限や宣伝などを考えると大河内氏は前提を削ったと考えに至らないのはなぜだろうか。
仕事についてだが、今後も一緒に仕事をする可能性があるといえる貴殿の性格が素晴らしいと思う。
貴殿は、大河内氏だけでなく大河内氏の所属する会社、編集をした出版社も批判していることをお気づきであろうか。大河内氏の組織を調べてみると歴史のある大きな会社だと判断できる。その大きな会社が会社名を公開して寄稿する時に会社組織がする校正、専門誌であれば編集者も専門家であるからその専門の編集者の校正を批判しているのである。
最も一番不快だったのは、貴殿はどうしたらよいかの発言をせず指摘だけしている点であると今一度いっておく。その行為が誹謗中傷目的であると感じられたから深く不快な思いをした。批判だけかいて何が起きるのかもう少し考えられてみてはいかがだろうか。

ockeghemockeghem 2010/05/28 10:31 cdさん、コメントありがとうございます。
『その行為が誹謗中傷目的であると感じられたから深く不快な思いをした』ということですが、私は根拠を明示して批判をしています。技術的に間違っているという批判です。技術誌に載った寄稿に対して、根拠を明示して批判することを「誹謗中傷」とは言わないと思います。
『貴殿は、大河内氏だけでなく大河内氏の所属する会社、編集をした出版社も批判していることをお気づきであろうか。大河内氏の組織を調べてみると歴史のある大きな会社だと判断できる。その大きな会社が会社名を公開して寄稿する時に会社組織がする校正、専門誌であれば編集者も専門家であるからその専門の編集者の校正を批判しているのである。』
これはその通りです。でも、社内ではしかるべき人がチェックしてないんじゃないですかね。大河内氏の所属企業のエンジニアを何人も知っていますが、その人たちは素晴らしい知見の持ち主です。その方々が査読したら、あんな寄稿は通らないと思いますよ。つまり、チェックされていないことに対する批判をしていることになりますね。
それに、私は根拠を明示して批判しているのですから、異論があれば、根拠を明示して反論すればいいだけのことです。今のところ、そのような反論はありませんし、「徳丸さんひどいじゃないですか」というメールなども頂戴していません。
私が一番大切なことは、元寄稿の読者や、これからWAFを導入しようとしている一番の方々に有益な知識を提供することだと思っています。正しい知識を普及させるためには、間違った情報は訂正していかなければならないのです。あの本が出る前に査読を依頼されたのであれば、非公開の形で訂正を依頼することができます。しかし、あの本を買った人は既にたくさんいるわけですから、その方たちに向けて、「あの内容は間違っている。本当はこうだ」という情報を提供していくことがこのエントリの狙いです。だから、公開でやらざるを得ないのです。

Mr.SmokeTooMuchMr.SmokeTooMuch 2010/10/04 16:54 素晴らしい内容読ませていただきました。記事の中で、CSRF対策ちして、RefererでチェックしているWAFは存在します。確か、S社のSaaS型WAFと思います。

ockeghemockeghem 2010/10/06 08:00 Mr.SmokeTooMuchさん、コメントありがとうございます。
私もその後、代理店の方から、Citrixの新しいバージョンでは、トークン埋め込み方式でCSRF対策ができるようになったと伺いました。CSRFは原理的に対策可能なので、対応する製品は出てくるでしようね。もちろん、元エントリの全体の趣旨には影響しません(WAFでCSRF対策できないという意見に対する反論なので)。
ありがとうございました。また、遊びに来てください。

pclpcl 2011/12/16 12:19 徳丸さんの指摘された内容についてはおおむね共感できますが、
「WAFの初期設定に対する考え方」については大河内さんが正しいと思います。

過剰検知の少ない WAF は確かに運用しやすいですが、反対に False Negative が
発生しやすくなります。

その False Negative を取り除くために慣らし運用(学習)して、さらにシグネチャを
チューニングすることで、各顧客の環境に適した 完全な WAF が仕上がるのではないでしょうか。

・・・といいましても、WAF 自体それほど完成された製品ではないので、
正解なんてないのかな?とも思います。

なんにせよ、もっと議論する場所がほしいですねえ。

2009-07-14 「劣化するWeb開発技術者」を量産するテキスト

携帯電話向けWebアプリセッション管理はどうなっているか 携帯電話向けWebアプリのセッション管理はどうなっているか - ockeghem(徳丸浩)の日記 を含むブックマーク

 最近購入したPHP×携帯サイト 実践アプリケーション集を読んでいて妙な感じがしたので、この感覚はなんだろうと思っていたら、その理由に気づいた。本書に出てくるアプリケーションは、PHPセッション管理機構を使っていないのだ。そんな馬鹿なと思ったが、目次にも索引にも「セッション」や「session」という語は出てこない。サンプルプログラムCD-ROM上で session を検索しても出てこないので、セッションはどこでも使っていないのだろう。

 そうは言っても、本書にはブログSNSなど認証が必要なアプリケーションも登場する。本書で採用している認証方式はこうだ。

  • 携帯電話の個体識別番号を用いた、いわゆる「かんたんログイン」のみを使う
  • 認証状態をセッション管理機構で維持しない。全てのページで毎回認証する
  • そのため、「iモードID」など、ユーザに確認せずに自動的に送信されるIDを用いる

 つまり、全てのリンクやFORMのアクションに、「guid=ON」を指定しているのだ。

 これは、携帯電話向けWebアプリケーション開発の定石とは言えないだろう。本書を勉強して著しく実力をつけた開発者がいたとして、いざ本格的なサイトを開発しようとすると非常にとまどうのではないか。セッション管理は、Webアプリケーション開発の基本であるのだから、最初からその基本をきちんと教えるべきだろう。

 この手法の限界は多いのだが、たとえば以下のような問題がある

  • iモードIDはSSLの場合送信されないので、SSL対応のサイトは開発できない
  • セッション情報をすべてhiddenなどで引き回さないといけないので、複雑なアプリケーション開発は困難
  • PCサイトとの互換性がない。PCと携帯電話相互乗り入れするサイトも作れない(あるいは困難)
  • 標準的なセキュリテイ対策手法が使用できない

などなど。このように、一般のPC向けサイト開発とはまったく互換性のない手法を敢えて教える必要があるのか。

 このような事態を予見するような意見は既にあった。高木浩光氏のインターネットにおけるセキュリティとプライバシーの両立についてに出てくる「劣化するWeb開発技術者」(スライドP42から下記に引用)がそれだ。

f:id:ockeghem:20090714000348p:image

 まさに上記のような、『「セッションID」という概念を知らない』技術者、『契約者固有IDがないとWebアプリを作れない』技術者を量産するような内容になっているのだ。

 この方法の欠点として、「標準的なセキュリテイ対策手法が使用できない」ことを指摘したが、実際問題として、本書のアプリケーションでは、クロスサイト・リクエストフォージェリ(CSRF)対策をまったく考慮していない。このため、正規ユーザの権限にてSNSの書き込みや削除などが行えるようだ。

 以下は、本書のSNSの日記削除ページから引用だ。日記を削除してよいか確認したのち、「削除実行」のボタンの部分のフォームだ。

<form action="deletediary.php?guid=ON" method="post">
<div style="text-align:right;">
<input type="hidden" name="exec" value="1" />
<input type="hidden" name="id" value="<?php varout($entry['id']) ?>" />
<input type="submit" value="削除実行" />
</div>
</form>

 上記のように、CSRF対策のトークン類はとくに渡されていない。携帯電話の場合、Refererをチェックすることもできない(docomoの大多数の端末がRefererに対応していない)ので、CSRF脆弱性がある。

 通常のCSRFは、正規ユーザが「たまたまログイン状態だった」場合に被害を受けるものだが、本書の方法だと「自動的に勝手にログインされる」わけだから、CSRFの影響を受ける確率は高くなる。

 対策は容易ではない。元々セッション管理をしていないのだから、一般的なCSRF対策に用いられるトークン類を覚えておく場所がない。docomoの場合、Refererが送出されない端末が大多数なのでRefererをチェックするわけにも行かない。だからといって、個体識別番号などをトークンとして用いることもできない。個体識別番号は秘密情報ではなく、どのサイトにもばらまかれるものだからだ。個体識別番号を暗号化する、あるいは適当なSaltをつけてハッシュ値を求めるなどの方法が考えられるが、暗号アルゴリズムの選定や、鍵やSaltの管理などが大変で、そんな苦労をするくらいなら、あっさりPHPセッション管理機構を使う方が楽で安全だ。それに、携帯電話向けWebアプリ開発では、セッションIDをURLに埋め込むことが定石的に行われるが、この方法の場合、原理的にCSRF脆弱性は発生し得ない。最初から秘密情報をURLに埋め込んでいるためだ。このセッションIDは第三者は知り得ないので、CSRF攻撃を掛けようがないのだ。

 一方、本書の姉妹書のような体裁のPHP×携帯サイト デベロッパーズバイブルの方は、かんたんログインによる認証結果をPHPセッション管理機構で保持するという、定石的でまともな作りになっている。これであれば、ログイン部分を取り替えるだけで、別の認証方式(たとえばパスワード認証)に変更することも容易だ。このような一般的・汎用的な方法を広く普及させるべきだと思う。

 ただしこちらの方は、CSRF脆弱性は心配ないが、残念ながらセッションフィクセーションの問題がある。本書P270には以下のような認証プログラムがある(login.php)。

// 前略
// 【徳丸注】ここまでくれば認証OK
// 会員データをセッションに格納する
session_start();
$_SESSION['name'] = $name;

// 会員ページにリダイレクトする【後略】

 認証成功時にセッションIDの再生成をしていないので、セッションフィクセーションの脆弱性が生じる(参考)。session_start();の後に、session_regenerate_id(); を追加する必要がある。携帯向けWebアプリの場合、URLセッションIDを埋め込むので、CSRF脆弱性の余地がない代わりに、セッションフィクセーションの攻撃は受けやすくなる。忘れずに対策しておきたい。

 このようなセッション管理系の脆弱性のチェックリストとしては、ウェブ健康診断仕様の「(K) セッション管理の不備」が利用できる。以下に引用する。

(K)セッション管理の不備

検出パターン 脆弱性有無の判定基準 備考 (脆弱性有無の判定基準詳細、その他)
1ログインの前後でセッションID が変化するかセッションID が変わらない場合は指摘
2言語・ミドルウェアの備えるセッション管理機構を使用せず手作りのセッション管理機構を使っていないか手作りのセッション管理機構を使用している場合は指摘セッションID のパラメータ名等で、言語・ミドルウェアセッション管理機構を使用しているかを判断します。判断がつかない場合には、"手作りの疑いあり"として報告します。
3SSL を使用するサイトの場合、セッションID を保持するCookie にセキュア属性が付与されているかCookie のセキュア属性が付与されていない場合は指摘-
4Cookie をオフにしてアクセスした場合、セッションIDがURL 埋め込みにならないかセッションID がURL 埋め込みの場合は指摘リファラから漏洩するおそれがある場合にのみ、脆弱と判定します。(5 を参照)
5携帯電話向けサイト等でセッションID をURL 埋め込みにしている場合、外部リンクからReferer 経由でセッションID が漏洩しないかReferer からセッションID が漏洩する場合は指摘PC/携帯サイト両方が対象。外部へのリンク(検査対象とは異なるホスト上のページへのリンク)が存在する場合にのみ脆弱と判定します。セッションID の漏洩が問題とならない場合(認証等の機能が無いケースや、ワンタイムなセッションID を使用しているケース)は報告から除外します。

 携帯電話向けアプリケーションの場合、上記3項と4項は該当しない(Cookieを使っている場合は該当する)が、それ以外(1、2、5)は対策する必要がある。このあたりも含めて、携帯電話向けWebアプリケーションセッション管理をどのように書けばよいかは、近くまとめようと思っている。

追記(2009/11/02)

KCCSにて11月12日(木曜)、11月19日(木曜)、12月10日(木曜)の3回にわたり、ケータイWebセキュリティのセミナーを実施することになりました。ケータイWebのセキュリティですので、とくにセッション管理やかんたんログイン機能の問題を中心に説明します。興味のある方は、こちらのリンクから内容確認の上お申し込みください。

lilylily 2009/07/15 00:33 携帯web開発者のセキュリティ意識の皆無さに改めて気づかされます。
こういう基本的な情報をもっと広めていきたいですね。
脆弱性山盛りの携帯サイトは沢山ありますからね(笑)いや、笑えないか。

ただ、あなたはモバイルの常識を知らない。
あなたにはモバイルサイトの構築はできないでしょう。出来たとしても、セキュリティ意識が皆無の技術者が作る物にも遠く及ばないでしょう。
それぐらい、モバイルは一般のwebアプリの常識が通用しないものです。

逆も然りで、モバイルオンリーの開発者は一般の常識的なwebアプリは作れません。これは断言できます。
まとめ記事、期待しています!

なまえ なまえ  2009/07/17 22:53 >>lily
相手の経歴をよく知りも知らないくせに「あなたはモバイルの常識を知らない」だなんて、
よく言えるね。失礼じゃないか。経歴を調べればどういう人だかわかること。
いいかげんなコメントを書きちらした恥を知りたまえ。

2009-07-11 数値型に対するSQLインジェクション対策

PHP逆引きレシピは概ね良いが、SQLインジェクションに関しては残念なことに PHP逆引きレシピは概ね良いが、SQLインジェクションに関しては残念なことに - ockeghem(徳丸浩)の日記 を含むブックマーク

 404方面でも絶賛されていたPHP逆引きレシピを購入した。本書はとても丁寧な仕事で素晴らしいと思ったが、セキュリティに関しては若干残念な思いをしたので、それを書こうと思う。

 目次は以下のようになっている。

第1章 準備

第2章 PHPの基本構文

第3章 PHPの基本テクニック

第4章 ファイルとディレクトリ

第5章 PEARとSmarty

第6章 Webプログラミング

第7章 クラスとオブジェクト

第8章 セキュリティ

8.1 セキュリティ対策の基本

8.2 PHPの設定

8.3 セキュリティ対策

第9章 トラブルシューティング

第10章 アプリケーション

PHP逆引きレシピ オフィシャルサポート

 本書は、タイトルの示すように、コレコレしたいという目的ごとにPHPでの書き方が書かれている。よくある逆引き辞典タイプの本だが、類書に比べて丁寧に書かれている印象を受けた。私が感心したのは、PHP4.4.9、PHP5.2.9、PHP5.3(RC2-dev)での対応を書いてあるだけでなく、『本書では、レンタルサーバー「さくらインターネット」「XREA」「ロリポップ」でサンプルスクリプトの動作確認を行い、その結果を以下のマークで表しています』というところで、丁寧な仕事ぶりだと思った。

 第8章の「セキュリテイ」についても概ねよく書けている。先行する仕事をよく調べつつ、その内容をしっかり咀嚼した上で書いてあるという印象だ。書かれている内容すべてに同意するわけではないが、少なくとも、本書で書かれている方法で書いてセキュリティホールが混入するということは、ほとんどないのではないか。本書で省略された内容についても、HTTPヘッダインジェクションくらいで*1PHPで問題になりやすいヌルバイト攻撃や、文字エンコーディングの問題、インクルード攻撃などもしっかり記述してある。筆者たちの努力に、まずは敬意を払いたい。

 しかし、SQLアクセスに関しては、残念なところがある。まず、本書のP614には以下のように正しい指摘がある。

SQLインジェクションへの対策は、プリペアドステートメントやプレースホルダの仕組みを持つ関数を使うことです。

 そして、その後のサンプル06-2.phpも、以下のようにバインド機構を用いたスクリプトが示されている。

  if ($stmt = mysqli_prepare($link, $sql)) {
# 準備したクエリに変数をバインドします。第2引数は、変数の型が2つとも
# 文字列(s)であることを指定しています。
    mysqli_stmt_bind_param($stmt, 'ss', $_POST['id'], $_POST['password']);
# クエリを実行します。
    mysqli_stmt_execute($stmt);

 しかし、その前の第6章のP524に書かれているサンプルスクリプトは以下のようにエスケープが使われている。

# SQL文を作成します。
  $sql = sprintf("SELECT * FROM example WHERE id = '%d' OR language = '%s'",
                 mysql_real_escape_string('1'), mysql_real_escape_string('Ruby')
                );

 エスケープを使うこと自体は間違いではないが、第8章のセキュリティの説明では第一にプリペアドステートメントを推奨しているのであれば、サンプルもそちらで統一できるとよかった。

 そして、上記のサンプルには気になるところがある。リテラルをわざわざエスケープしていること自体はよいことだろう。ユーザ入力であろうとなかろうと、常にエスケープするという正しい習慣を身につけることに役立つからだ。

 問題は、列idに関してだ。この列は整数型である。テーブルexampleの定義は以下のようになっている(本書P520)。

CREATE TABLE IF NOT EXISTS `example` (
  `id` int(2) NOT NULL auto_increment,
  `language` varchar(10),
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

 そして、エスケープ後のSQLは以下のようになる。

SELECT * FROM example WHERE id = '1' OR language = 'Ruby'

 このように、数値をクォートしてエスケープする方法は、従来から大垣靖男氏が主張している方法だ。たとえば以下の説明。

文字列,整数などデータ型に関わらず変数すべてを文字列としてエスケープすることにより,SQLインジェクションを100%防ぐことが可能となります。

http://gihyo.jp/dev/serial/01/php-security/0005?page=2

 今まで追随者を見かけなかったが、最近になって大垣氏以外の方がこの方法を紹介する例が増えてきているように思う。しかし、このパターンを以下のように一般化すると、どうだろうか。

# SQL文を作成します。
  $sql = sprintf("SELECT * FROM example WHERE id = '%d' OR language = '%s'",
                 mysql_real_escape_string($id), mysql_real_escape_string($lang)
                );

 $idに元々整数型の値が入っていた場合に、このスクリプトは整数と文字列の間で、何度も型変換が行われる。

$id整数
mysql_real_escape_stringの引数整数→文字列
sprintf(..%d ..)文字列→整数→文字列
where id='1'文字列→整数

 右向き矢印「→」の数だけ型変換が行われるので、上記の場合は4回型変換が行われ、整数と文字列の間を行ったり来たりすることになるのだ。これはいささか筋の悪いプログラムと言えるだろう。

 また、SQLの文字列→数値の暗黙の型変換は色々問題がある。この暗黙の型変換の結果がどうなるかは処理系依存であって、この点は以前数値リテラルをシングルクォートで囲むことの是非 - ockeghem(徳丸浩)の日記で紹介した。さらに、id:teracc氏が2009-06-05 - T.Teradaの日記にて補足いただいた内容によると、DB2ではそもそも文字列→数値の「暗黙の型変換」はエラーになるとのことである。

 MySQLの場合、実行効率も悪くなることは、奥 一穂氏が、Kazuho@Cybozu Labs: MySQL の高速化プチBKで紹介されている。私もあらためてMySQL5.1にて追試したところ、数値をクォートしない場合が0.23秒、クォートする場合が0.41秒と、倍近い差となっている。

 これら書法上、効率上の問題は以前変数に型のない言語におけるSQLインジェクション対策に対する考察(5): 数値項目に対するSQLインジェクション対策のまとめ - 徳丸浩の日記(2007-09-24)にてまとめている。

 思うに、上記は以下のように書けばよいのではないか。

  $sql = sprintf("SELECT * FROM example WHERE id = %d OR language = '%s'",
                 (int)$id, mysql_real_escape_string($lang)
                );

 このようにすれば、何度も型変換することはないし、非常に読みやすい。効率も良い(悪くならない)。

 この書き方の良いところとして、ソースコードのチェックがやりやすいこともある。sprintfの書式文字列は、%dはクォートしない、%sはクォートする、この二点を確認する。また、パラメータのところでは、(int)のキャストか、mysql_real_escape_stringによるエスケープのどちらかを実行していることを確認する。これで、SQLインジェクション対策については問題ないのだ。心配なのは、数値を'%s'で受けたり、文字列を%dで受けることだが、下表のようにSQLインジェクションにはならない。ただし、文字列を%dを受けると当然ながら処理はおかしくなるが、これはこの方法固有の問題ではなくて単なるバグだ。その場合でもSQLインジェクション脆弱性が混入せず、きわめてミクロのチェックですむところがミソといえる。

書式数値文字列
%d数値に変換されるがSQLインジェクションにはならない
'%s'大垣氏の方法でありSQLインジェクションにはならない

 本書では、これ以降のところでも、概ね似たようなサンプルが並ぶのだが、とくにおかしいのがP548の「テーブルを作成したい」に出てくるサンプルだ。これは以下のようになっている。

# 作成するテーブルの中身を変数に代入します。
  $newTable = 'id INT(2) NOT NULL AUTO_INCREMENT PRIMARY KEY, ' .
              'data VARCHAR(100) NOT NULL';

# テーブルを追加するためのSQL文を作成します。
  $sql = sprintf("CREATE TABLE example2 (%s) ENGINE=MyISAM " .
                 "DEFAULT CHARSET=utf8",
                 mysql_real_escape_string($newTable));

// ロリポップの場合はこちらを有効にする
//  $sql = sprintf("CREATE TABLE example2 (%s) ENGINE=MyISAM ",
//                 mysql_real_escape_string($newTable));

 SQLのCREATE TABLE文について、列定義の部分のみ$newTableとして切り出しているが、それをSQLとして組み立てる際に、mysql_real_escape_string関数でエスケープしている。これはおかしい。SQLのエスケープは、SQLの文字列リテラル内をエスケープすべきなのであって、このようなSQL断片に対してmysql_real_escape_stringを通してしまうと、処理がおかしくなる。このサンプルの場合は正常に動作するが、それは偶然に過ぎないと考えた方がよい。

 うまく行かない例として、MySQLの場合、列定義にコメントがつけられる。たとえば以下のように。

CREATE TABLE example3 (id INT(2) COMMENT 'this is a comment');

 このような場合に括弧の中身を無理にエスケープすると、次のように文法違反となる。

CREATE TABLE example3 (id INT(2) COMMENT ''this is a comment'');

 COMMENTなんて使わないかもしれないが、そもそも列定義の部分をエスケープをする必要などまったくないのだ。

 本書は750ページ近いボリュームで、\2,600というお値打ち価格であるにも関わらずとても手の掛かった丁寧な仕事でお買い得なのだが、それだけに、上記はとても残念な気がした。本書の読者の参考になれば幸いである。

参考:

WASForum Conference 2008講演資料「SQLインジェクション対策再考」

数値項目に対するSQLインジェクション対策のまとめ

SQLの暗黙の型変換はワナがいっぱい

quoteメソッドの数値データ対応を検証する

追記(2010/3/19)

IPA非常勤研究員として「安全なSQLの呼び出し方」執筆に参画いたしました。数値に対するSQLインジェクション対策も説明していますので、あわせて参考にいただければと思います。

*1:これはPHPのheader関数がHTTPヘッダインジェクション対策されているから、アプリケーション側で対策する必要はないという趣旨なのだと想像する

testtest 2009/07/12 16:27 これってタイトルと内容はマッチしてると言えるんですか?
お題が釣り目的としか思えないんですが。

ockeghemockeghem 2009/07/13 14:06 testさん、コメントありがとうございます。
そうですね、客観的に読み返すと、釣りみたいに見えると思います。申し訳ありません。
でも、ここで書いているテーマは、私がずっと追っかけているテーマなのです。だから、私は文字通りに「残念な」思いをしたわけでして、その時点では「釣り」としての意識はありません。
ただ、ネットの世界では「残念な」にもっと別の特別なニュアンスがあることは承知しています。それをもっと意識して用いるべきでした。
今後、気をつけたいと思います。ありがとうございました。

2009-03-07 どう書くorz

正規表現で「制御文字以外」のチェック 正規表現で「制御文字以外」のチェック - ockeghem(徳丸浩)の日記 を含むブックマーク

 一般に、セキュアコーディングの基本として入力値の検証(Validation)をせよということになっていますが、これが変な方向に行くといわゆる「サニタイズ」のような手法になってしまいます。以前も指摘したようにアプリケーションとしてのValidationは仕様に従って行うべきものです。

 ですが、概ねどの場合でも行うべき検証として以下があると思います。

  • 文字エンコーディングの妥当姓
  • 制御文字(\x00〜\x1f, \x7f)のチェック
  • 文字列長のチェック

 このうち後ろ二つを正規表現として書くにはどうすればいいかを考えていました。つまり、「制御文字以外の文字でm文字以上n文字以下」というようなチェックです。m文字以上、n文字以下は、{m,n}で書けるので、問題は「制御文字以外の文字」です。これはtextタイプのinput要素で、かつアプリケーション仕様としては文字種の制限をしない場合を想定しています。プログのタイトル欄や、住所欄などが典型例です。

 私は正規表現に特別詳しいわけではないので、試行錯誤的に調査して、POSIX正規表現の[:print:]を使って書けばいいのかと思いました。以下のような感じです。

  if ("this is a pen" =~ /^[[:print:]]+$/) {
    # ... これは成立する

 ところが、[:print:]は、use utf8;しないと日本語対応していないことと、utf8フラグが立った状態では、タブ、改行などにもマッチすることが分かりました。

 PHPにも同様の問題があり、文字エンコーディングがUTF-8の場合は、タブ、改行などに[:print:]がマッチします。これは鬼車の仕様だそうです。wassr.jpにて、moriyoshitさんから教えていただきました。さらに、この件についての調査結果をブログにまとめてくださいました。ありがとうございます。

 さらに、Javaの場合ですと、[:print:]に相当する\p{Print}がUS-ASCIIのみに対応しているということで使えない。

 というわけで、[:print:]の使用をあきらめ、別の方法を考えることにしました。私に思いついたのは以下の二つですが、

  • 16進で指定する
  • 制御文字*以外*という指定

 16進で指定する方法は、ミスが生じやすいことと、可読性が悪いことが気になりました。うっかり、DEL(\x7f)を忘れそうだし、制御文字のうち改行やタブを許したい場合にややこしくなりそうです。Javaの場合は、サロゲートペアを含む正規表現を16進で指定する方法が分からない。

 そこで、制御文字*以外*を指定する方法として、[:^cntrl:]に着目しました。Javaの場合は\P{Cntrl}です。Perlの場合だと、次のように書けます。

  use utf8;
  #...
  if ("日本語にも対応" =~ /^[[:^cntrl:]]+$/) {
    # ... これは成立する

 テキストエリアの場合は改行を(場合によっては水平タブも)許可しないといけませんが、これも簡単に追加できます。

  use utf8;
  #...
  if ("日本語\r\n対応" =~ /^[\r\n\t[:^cntrl:]]+$/) {
    # ... これは成立する

 と、なんとかここまで漕ぎ着けたのですが、残る問題は、

  • もっといい方法はないのか?
  • Perlの場合末尾の\nがうまくチェックできない

 後者についてですが、以下のサンプルプログラムは *match* を表示します。正規表現のオプションs, m, msのいずれを追加しても同じ結果でした。

my $a = "1234\n";
if ($a =~ /^[0-9]+$/) {
	print "*match*\n";
}

 Perlにとって行末の\nは空気みたいな存在なんでしょうか。Webアプリ場合、通常改行は\r\nですが、その場合はチェックできます。問題は\n(ラインフィード)だけがついている場合です。パーセントエンコードで書くと、1234%0a ですね。

 今のところ上手い方法を思いついてないので、一行用と複数行用で処理を分けることを考えていますが、もしよい方法をご存じの方があれば教えて下さい。

2009/9/24追記

ITproに、このあたりをまとめていますので参考になさってください。

no title

えむけいえむけい 2009/03/13 07:46 /^[[:^cntrl:]]+\z/

ockeghemockeghem 2009/03/15 18:35 えむけいさん、アドバイスをありがとうございます。\zについてはid:nihenさんからももご教授いだきました。まだ、小飼弾氏からは、^$ではなく、\A\zを使うように教えていただきました。\A\zが正解のようですね。これについては別途まとめたいと思います。

2009-03-02 攻撃手法を説明する意味はなにか

SQLインジェクション攻撃はDB上の任意データを盗み出す SQLインジェクション攻撃はDB上の任意データを盗み出す - ockeghem(徳丸浩)の日記 を含むブックマーク

 前回に引き続き、Think IT上の連載「SQLインジェクション大全」の第4回:ケース別、攻撃の手口を読んで感じたことを書きたい。

 まず、この記事は以下のような書き出しから始まっている。

本記事は、システムを防御するにはまず敵を知らなければならない、という意図の下に、攻撃手法を紹介する。

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 この趣旨に異論があるわけではないが、しかしこの表現は誤解を生みやすいものだと思う。

 というのは、「敵」(攻撃方法)を知ったからと言って、防御の方法が導き出せる訳ではないからだ。例えば、開発者が「最近のSQLインジェクションの自動化攻撃にはT-SQLのDECLARE文が用いられる」という情報を得て独自に対策を考えた場合、DECLAREという単語をチェックしてエラーにしたり、単語を削除することを考えがちだと思う。現に「セキュリティ専門家」の多くがその種の対策手法を推奨してきた歴史があるわけだが、現在ではこの種の手法はサニタイズと呼ばれ、推奨されない対策となっている。本質的な対策方法を検討するには、脆弱性が混入する根本原因をつきとめなければならないが、攻撃手法を見せられただけで、その攻撃が成立してしまう根源的な原因を考えられる人は少数派だと思うのだ。

 では、攻撃手法を紹介する意味はなにか。私は三つあると考えている。

(1)攻撃がもたらすリスクを説明する

 セキュリティ対策には多少なりとも費用や時間が掛かるわけで、その費用や時間を掛けてもよいと判断をする人(経営者やマネージャ)に対して、なぜそうするべきかを納得してもらう必要がある。セキュリティ対策の場合、それを怠った場合のリスクと可能性が問題となる。「ほら、こんなに簡単に個人情報がとれるんですよ」と手法紹介やデモを見せることは、リスクや可能性を素早く納得してもらうのに有効な手段だ。

 しかし、手法はあくまで攻撃の「例」に過ぎないわけであるから、それを一般化し、発生しうるリスク全般について簡潔に説明することを忘れてはならない。

(2)開発者が対策を行うモチベーションを高める

 セキュリティ対策は開発者にとっては面倒な作業であることが多い。開発者は機能の実現にはモチベーションが高まるが、機能に直結しないチェック処理などは「面倒」で「できればやらずにすませたい」仕事だろう。ましてや、セキュリティ対策の中身が理解できていない場合には、いっそうネガティブな感情が高まる。

 開発者に攻撃手法を説明することは二つの効果がある。まず、前項と同じでセキュリティリスクを理解することで、セキュリティに対する正しい恐怖心を身につけること。もう一つは、対策の有効性を開発者として納得してもらうことだ。これらにより、セキュリティ対策を実装するモチベーションを高めることが期待される。

(3)エンターテイメントとしての攻撃手法

 攻撃手法の多くは巧妙で知的好奇心を刺激するものだ。従って,攻撃手法の説明やデモは、やり方次第では楽しいエンターテイメントになる。

 もちろん、攻撃手法を面白おかしく紹介するだけでは、手法の悪用など、良くない副作用が考えられるわけだ。しかし、一方で正しいセキュリティ知識を身につけるべき開発者がセキュリティに対して興味を持っていない現状では、「セキュリティって面白いんだよ」という啓蒙も必要だろう。

 このため「面白くてためになる」セキュリティ講座の出発点として、「エンターテイメントとしての攻撃手法」というやり方もあり得ると考えている。

 このように,攻撃手法を説明する理由を考えていくと、つきつめれば「リスクに対する正しい理解」が重要であることになる。そこで、SQLインジェクションがもつリスクとは何かをもう一度考えてみよう。私の考えるところ下記の3種類に分類される。

  • 情報漏洩のリスク
  • データ改ざんのリスク
  • その他のリスク

 以下、順に説明しよう。

情報漏洩のリスク

 SQLインジェクションの代表的なリスクが情報漏洩であるが、しかしまだ正しく理解されているわけではない。SQLインジェクションによる情報漏洩が恐ろしい理由は、データベース内の情報ならどれでも取り出しが可能な手法が分かっていることである。

 よくある誤解としては、「この画面は大した情報を扱ってないから対策しなくても大丈夫ですよ」という反応だ。たとえば、その画面はエラー番号を受け取って、エラメッセージをDBから検索して表示するだけの「つまらない」ページかもしれない。しかし、その「つまらない」ページであっても、DB内に個人情報があれば,漏洩させることが可能というのがSQLインジェクションの怖さだ。代表的な手法としては、以下の3種の方法が知られている。

 それぞれ、簡単に説明する。

(1)エラーメッセージを利用する方法

 冒頭で触れたdfltweb1.onamae.com ? このドメインはお名前.comで取得されています。で、エラーメッセージから取得できる情報として、テーブル名/カラム名/カラムの型を紹介しているが、現実にはDB内の内容も取得できる手法がある。

 具体例を示そう。まず、エラーメッセージを取得するSQLが以下のようだったとする。

select message from errmsg where errnum = $n

ここで、$nに対してSQLインジェクションを掛け、以下のようにSQLを改変する。

select message from errmsg where errnum = 1 and 1 < (select top 1 card_name + '!' + card_number + '!' + card_date from t_card)

 追加したSQLでは以下の処理を行う。

  • クレジットカード情報を保持するテーブルt_cardから、氏名(card_name)、カード番号(card_number)、有効日付(card_date)を取り出す
  • 取り出した情報を、記号「!」をセパレータとして連結する
  • 連結後の文字列を数値1と比較する

 すると、連結後の文字列は数値と比較できないので、以下のようなエラーメッセージが表示される(SQL Server 2005にて検証)。

varchar の値 'yamada!1234-5678-9012-3456!07/11' をデータ型 int に変換できませんでした。

 この例では、先頭の1レコードが得られるだけだが、SQLのハックにより、任意のレコードを得ることができる。

(2)UNIONを利用する方法

 先ほどの例と同様にSQLインジェクションにより、SQLを以下のように改変する。

select message from errmsg where errnum = -1 union select card_name + '!' + card_number + '!' + card_date from t_card

 ここでerrnum = -1としているのは、存在しないエラー番号としてである。UNIONはSQLの和集合を求める演算で、UNIONの左辺(元のSQL)と右辺(追加したSQL)をマージした結果を返す。左辺は1件もヒットしないので、結果として,新たに追加された問い合わせ結果が、このSQL全体の結果となる。この結果、以下のような値を返すことになる。

yamada!1234-5678-9012-3456!07/11

 やはり先頭一件のみの表示だが、前項と同様、任意のレコードを得る方法があり、繰り返し問い合わせることにより全てのクレジットカード情報を抜き取ることができる。

 また、UNIONを使う場合は、一覧表形式の表示箇所に用いるとより有効で、一度に複数件のレコードを得ることが可能だ。

(3)ブラインドSQLインジェクション

 今回の例ではブラインドSQLインジェクションを用いる理由はないのだが、SQLインジェクション脆弱性箇所によっては、WHERE句の真偽値のみ、あるいはSQLがエラーになったか正常終了したかのみが判別できる場合がある。このような代表例としては、ログイン画面がある。ログインが成功したか否か、あるいはログイン機能が正常終了したか異常終了したかによって、1ビットの情報が得られる。

 この1ビットの情報を積み重ねることによって、データベース内の任意の情報を取得する手法がブラインドSQLインジェクションである。詳しい手法の紹介は省略する。ブラインドSQLインジェクションは,一回のHTTPリクエストで得られる情報は1ビットなので、非常に多数のHTTPリクエストが必要となる。このため、手動での攻撃は困難で,専用のツールやスクリプトを用いることになる。

 dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。の2ページ目末尾には「スリープ機能」によるSQLインジェクションが紹介されている。

5つ目がスリープ機能を利用したレスポンス時間の差異だ。SQLインジェクションの攻撃を実施する際に、レスポンスに攻撃結果が表示されない場合、指定した攻撃が実行されたか否かを判断するために、攻撃の内容にレスポンス時間を遅くさせるための仕組みを組み入れる。レスポンス時間が遅れれば、攻撃が実行されたことがわかる。

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 この手法は、WHERE句の真偽値もSQLのエラーも画面には表示されない場合の攻撃手段であり、いわば「究極のブラインドSQLインジェクション」である。画面からはなにも得られないので、問い合わせが帰ってくるまでの時間の差で1ビットの情報を得ようというものだ。ただでさえブラインドSQLインジェクションは時間がかかるところを、スリープ機能まで使うので、情報を得るのには膨大な時間がかかる。この手法は検査手法への応用としては興味深いが、現実の攻撃に用いるのは難しいかもしれない。

 そして、引用部分については、この攻撃を理解する上で必要となるブラインドSQLインジェクションの説明がないので、この説明だけでは読者はなんのことやら分からないだろう。

データ改ざんのリスク

 かつて複文が利用できるデータベースの調査: SQL Serverが狙われるには理由がある - 徳丸浩の日記(2008-05-02)SQLインジェクションのサンプルが動かない - ockeghem(徳丸浩)の日記で紹介したように、一部のDBMSでは、一回の問い合わせの際に複数のSQL文を受け付ける。これを複文(multiple statement)という。攻撃手法としては、SELECT文やUPDATE文などの末尾に新たなUPDATE文を続けることによって、データの改変を行うことができる。DBMSの種類でいうと、MS SQL Serverが一番この攻撃がやりやすく、条件付きでPostgreSQLとSQLiteでも可能な場合がある。さらに、MySQLでも、複文を受け付ける機能がある(PHPのmysqli_multi_queryなど)ので、元のアプリケーションがこれを使っている場合は複文による攻撃が可能だが、開発者がわざわざそのようなプログラミングをしている可能性は低そうだ。

 通常SQLの区切りにはセミコロン「;」を用いるが、SQL Serverの場合はセミコロンなしでも複文の作成が可能だ。これについては複文が利用できるデータベースの調査(2): SQL Serverが狙われるにはまだまだ理由がある - 徳丸浩の日記(2008-06-27)にて紹介した。

 データ改変の場合は、元SQLとはまったく別のSQLを追加するので、任意テーブルの任意列を書き換え可能である。最近の攻撃例では、表示内容にIFRAME要素やSCRIPT要素を埋め込むことによって、改変されたサイトを訪問したユーザにマルウェアを感染させるという攻撃が行われている。

その他のリスク

 SQL Serverには、xp_cmdshellというストアドプロシジャが用意されており、SQLを利用して、DBサーバー上でWindowsのコマンドを実行することができる。xp_cmdshellはSQL Server 2000ではデフォルトで有効、SQL Server 2005以降ではデフォルトで無効である。SQL Serverを使った古いサイトでは特に注意を要する。xp_cmdshellが利用できる場合、任意コマンドの実行が可能と言うことから、DB内にとどまらない多様な攻撃が可能となる。

 また、SQL Server以外のDBMSでもファイルの読み書きが可能な場合が多い。これにより、DBサーバー上の機密ファイルをSQL経由で読み出したり、攻撃結果をファイル経由で効率よく持ち出すことが可能となるなど、ファイルを用いた攻撃が可能になる場合がある。

SQLインジェクション大全はどうか

 攻撃手法を紹介する意義について考察した後に、SQLインジェクション攻撃のリスクと一部の攻撃手法を紹介した。

 私がこのエントリを書こうと思った動機は、dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。には、一番重要となるリスクが明確に説明されていないと思ったからだ。その説明は断片的で不正確だ。その典型が2ページ目のクレジットカード情報の取り出しの説明だ。

 例えば、ECサイトのデータベースにクレジットカード情報の含まれる会員情報が保管されているとする。その会員向けのサービスとして、クレジットカード以外の会員情報を表示するWebページがあるとする。

 その際に使用するSQL文に、クレジットカード情報の格納されたテーブルにアクセスするSQL文を挿入して、会員名などを表示する個所に、クレジットカード番号を表示させる方法がある。

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 こう書いてあるので、引用箇所に続いて「本来画面に表示されないクレジットカード番号を表示させる」テクニックが説明されるのだろうと思うのだが、その説明はされていない。説明されているのはJOINを用いてユーザ名やクレジットカード番号を表示するSQLだ。

select u.user_name,c.card_number, c.card_date from t_users u join t_card c on u.id = c.user_id where id=’$userid’ ;

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 しかし、改変前のSQLが示されていないので、どのような攻撃の結果このSQLが得られたのかが分からない。さらに、SQLの末尾に$useridとあるので、このSQLは元々この形であったと考えるしかない。これでは、「会員名などを表示する個所に、クレジットカード番号を表示させる方法」ではない。元々クレジットカード番号を表示する画面ではないのか。

 おかしな部分はそれだけではない。$useridはその名前の示すようにユーザIDであろうが、以下のような説明があるのだ。

その時、検索のキーワードとなる「$userid」の値がブラウザから送られるPOSTやGET、Cookieの値を参照している場合、「$userid」の値を以下のように改ざんして攻撃する。

「’ or 1=1 --」

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 近藤氏は、よほど「' or 1=1 --」がお好きらしい。しかし、$useridの書き換えができるという前提であれば、SQLインジェクションを用いなくとも任意のユーザの個人情報を閲覧できるということである。だから、SQLインジェクション以前の話ではないのか。ユーザIDの内容を外部から書き換えられる状態は、セッション管理上のごく初歩的な脆弱性である。

 近藤氏の所属する神戸デジタル・ラボでは、Webアプリケーションのソースコード診断も提供しているらしい。

また、「ソースコード診断」は、対象サイトのプログラムセキュリティチームが、手作業でチェックし、顕在化していなかった問題点を含めて明らかにしていきます。「ソースコード診断」の結果を擬似攻撃診断に反映して行うことで、相乗効果が期待でき、より精度の高い診断を実施することができます。

最高の品質|サービスの強み|プロアクティブ・ディフェンス

 先のサンプルプログラムも、公開前に自社の「ソースコード診断」を受けておけばよかった。

 なお、図2-1、図2-2、図2-3は前回(第3回)の図(文字コードの問題などの説明で本文との関連はない)がそのまま載せられているので説明が余計に分かりにくくなっている。これは編集部のミスだろうが、関係者は公開後も誰も閲覧しなかったのだろうか。

 今回の結論はこうだ。

  • 攻撃手法を紹介する目的は主にリスクを理解してもらうことである
  • 攻撃手法を説明する時は,余計な脆弱性が混入しないよう気をつけよう
  • SQLインジェクションのリスクには任意データの漏洩、任意テーブルの書き換えなどがある

追記(2009/3/3 23:22)

 ようやく図2-1と図2-2が差し替えられた。

 この図によると、やはり元々から「ただのJOIN」である。(クレジットカード番号がマスクされているが、意図が不明なので追求しない)。また、SQLインジェクションの結果得られる神戸太郎さんの情報は、ユーザIDをA002からA001に変更するだけで可能だ。同様に、A003、A004と変更していくだけで、他の人の個人情報も得られる。SQLインジェクションよりも簡単だ。図が差し替えられても私の主張はなんら変更する必要はなく、より理解が容易になったと思う。

 |  
Google