進・日進月歩

IT, Jazz, study, engineering, すべての真実とクリエイティビティのために

Archive for November, 2010

XSS対策手法のひとつの提案

  • Filed under: 雑記
Friday
Nov 19,2010

ドラフト。ドラフトじゃなくなることはあるんだろうか。コメント求む。

XSS対策手法のひとつの提案

概要

XSS (cross site scripting)というのは、特にwebプログラミングの文脈でよくつかわれる、セキュリティホールの一種である。webプログラミングが一般的になった現在でもこのセキュリティホールは頻繁に発生しており、必ずしも素人だけがしてしまう失敗とも限らない。近年で有名な例としてtwitterでの事件がある。

>本投稿では、XSSの事例と背景について説明するとともに、その対策として「コンテキストを減らす」「コンテキストごとのエスケープ」「安全なプログラミングの手法」を示しひとつのXSS対策の指針を示す。

また最後に、もともこもない対策方法として「base64で対策したらこうなった」という話をする。

本投稿は、「安全な」対策方法としてはまだ開発段階にあり、鵜呑みにするのは危険であることをご了承いただきたい。自己責任。

目次

  • XSSとは
  • XSSの原理と背景: コンテキストとエスケープの関係
  • XSS対策が困難な理由と、その対策
  • コンテキストの削減
  • コンテキストごとのエスケープ
  • 安全なプログラミング指針
  • すべてをhtmlエスケープしてしまうfail safeの欠点
  • もともこもない対策: base64
  • まとめ

XSSとは

XSSとはCross Site Scriptingの略であり、掲示板などのユーザ投稿型のwebサービスに対して「悪意のあるスクリプト」を投稿し、開発者の意図しない動作をさせるセキュリティーホールの一種である。

Smartyテンプレートとphpでの具体的な例を挙げる。以下の様なtest.phpとtest.tplがあったとする。ここでは、簡単のため必要なrequireや定数・変数の定義などは省いている。

CODE:
  1. // test.php
  2.  
  3. <?php
  4.  
  5. $smarty = new Smarty();
  6.  
  7. $smarty->assign("value1", $_GET["form1"]);
  8.  
  9. $smarty->display("test.tpl");
  10.  
  11. ?>

CODE:
  1. {* test.tpl *}
  2.  
  3. {$value1}
  4.  
  5. <form action="." method="get">
  6.  
  7. <input type="text" name="form1"><input type="submit" />
  8.  
  9. </form>

この例では、例えばform1に次の値を渡すとjavascriptが実行される。

CODE:
  1. <script>alert("XSS!");</script>

本来であれば、適切に文字列が処理されform1の文字列は、文字としてブラウザに表示されなければならない。しかし、適切な文字列処理が行われていないためブラウザ上では下記の様なHTMLとして解釈され、開発者の意図してないjavascriptが実行されてしまう。

CODE:
  1. <script>alert("XSS!");</script>
  2.  
  3. <form action="." method="get">
  4.  
  5. <input type="text" name="form1" /><input type="submit" />
  6.  
  7. </form>

このように、適切な文字列処理が行われないまま出力された値をブラウザが解釈することで、開発者の意図しない動作を発生させる脆弱性のことをXSSと呼ぶ。上記はその一例である。

なお、本投稿では筆者の主たる開発環境のなかで一番一般的であると考えられるPHPとsmartyテンプレートで考えるが、全ての言語・テンプレートで共通のことがいえる。

XSSの原理と背景: コンテキストとエスケープの関係

XSSを防ぐ一番一般的な方法はエスケープという文字列処理である。例えば先ほどの例では、以下の様なエスケープが行われる。

前:<script>alert("XSS!");</script>

後:&lt;script&gt;alert("XSS!");&lt;/script&gt;

ここでは、HTMLのメタ文字である「<」や「>」を「&lt;」「&gt;」に変換している。ここでいうメタ文字とはあるプログラムの文字列中で特殊な意味を持つ文字のことである。<や>はHTMLではタグを示す特殊な文字であり、これらはHTMLのメタ文字である。これらを文字列として解釈させるための変換をエスケープと呼ぶ。

また、ここでコンテキストという概念を導入する。コンテキストとは、プログラム中の注目している領域の文字が何であるかを示すものである。例えばHTMLでは、以下の様なコンテキストが存在する。

CODE:
  1. <html>
  2.  
  3. ここは「HTMLの文字列」のコンテキスト
  4.  
  5. <script>
  6.  
  7. //ここは「javascript」のコンテキスト
  8.  
  9. var hoge="ここは「javascriptの文字列」のコンテキスト";
  10.  
  11. </script>
  12.  
  13. </html>

他にも、様々なコンテキストが存在する。

XSSの原因は、コンテキストに対して適切なエスケープがされていないことにある。先ほどの例では、「HTMLの文字列」のコンテキストでHTMLのメタ文字をエスケープ出来ていなかったためXSSが発生している。HTMLのメタ文字である「<>」をエスケープしHTMLの文字列のコンテキストに合わせなくてはいけない。

つまり、ひとつのコンテキストの中にメタ文字を含めるとXSSが起き、メタ文字をエスケープして文字列の中をひとつのコンテキストにすることでXSSが防げる。

XSS対策が困難な理由と、その対策

XSS対策を困難にしている理由として、筆者は「コンテキストの多さ」と「コンテキストごとのエスケープが提案されていない」ことに注目する。

コンテキストは先ほどの単純なHTMLの例でも多数存在することがわかる。筆者が思いつく限りでも以下の様なものがある。

  • HTMLの文字列
  • HTMLの属性値
  • HTMLタグ中のURL
  • Javascript
  • Javascriptの文字列
  • CSS
  • Javascriptのdocument.writeでの書き出し時
  • Javascript中でのURL

このコンテキストごとにメタ文字が存在し、それぞれに対して適切なエスケープ処理を行う必要がある。

しかし、PHPやsmartyでは十分なエスケープ関数が提供されていない。むしろ、「HTMLの文字列」に対するエスケープ関数しか無いといえる。そのため、エスケープ関数を通しているにもかかわらずXSSが起こる。

例えば以下の例がある。引用

CODE:
  1. // HTML
  2.  
  3. <script>
  4.  
  5. var hoge="{$fuga|escape}";
  6.  
  7. </script>

上記の様な例では、$fugaに代入されている文字列の末尾にバッククオートを挿入することで、XSSを行うことができる。Smartyにはjavascriptの文字列のコンテキストでのエスケープ処理が存在するが、この例では適切なエスケープ関数を選択しないことでXSSが起きることを示した。

このように、コンテキストが多数存在するため初心者にはそれが判断しにくくなっている。また必ずしも適切なエスケープ関数が用意されているとも限らないため適当なエスケープ関数を選んだり、適当に組み合わせたりする開発者もいるようである。

このような状況がXSSの対応を難しくしていると考えられる。

そこで、筆者は「コンテキストの削減」と「コンテキストごとのエスケープ」を提案する。

コンテキストの削減

XSSを議論するとき、エスケープばかりに目がいき勝ちであるが、今までの議論で述べた通りコンテキストの多さが混乱を招いているという一面がある。そこで、この混乱を回避するために、以下のコンテキストに絞り込むことを検討する。今回は特にPHP、smartyテンプレートでHTMLを出力することを考える。

  • HTMLの文字列 (<html>ここ</html>)
  • javascriptの文字列 (var hoge="ここ";)
  • HTML中のURL (<html><a href="http://hogehoge.com/ここ"></html>)
  • javascript中のURL ($.get("http://hogehoge.com/ここ"))

(ここでこれに絞り込む根拠を書きたい。一言でいうと、これ以外は大体プログラミングの努力でどうにか出来る。)

ここで以下の様な場合に注意したい。

CODE:
  1. <script>
  2.  
  3. var hoge = "<span>{$val}</span>";
  4.  
  5. </script>

この例は、HTMLとして出力される予定の部分にsmartyで値を埋め込む場合である。これは文字列中にのみ注目した場合「HTML」のコンテキストと「HTMLの文字列」のコンテキストが混在してしまっている。つまりこれは単純な「javascript中の文字列」のコンテキストではなく、新たなコンテキストである。

この例では、dom操作で同等の処理ができるためコンテキストを増やすこと無く対処できる。

この例の様に一見して同じコンテキストに見えるものでも、実際は異なる(二種類以上のコンテキストを混ぜている)場合があるため注意が必要である。そしてここが非常に難しいため文字列連結などには注意が必要である。

また、「javascriptでのdocument.writeでの出力」や「innerHTML」などのコンテキストの必要を主張する読者もいるかもしれないが、多くの場合DOM操作での代替が可能でありdocument.writeが必要な場面は限られる。従って、これらは使うべきではないし、使う場合は知識豊富なプログラマが行うべきである。

コンテキストごとのエスケープ

上記で定めたコンテキストごとのエスケープ処理について議論する。筆者は上記のコンテキストごとにひとつのエスケープ関数を用意している。先に議論した通り、コンテキストごとに必要なエスケープ関数はひとつである。この例の様にhtml,jsなどの複数のエスケープ関数をカスケードして処理することも不可能ではないが、次の理由から筆者はおすすめしない。

もしjsの関数にバグがあった場合、jsを利用している全箇所に影響が出る。js関数が複数のコンテキストで使用されていた場合、かならずしも複数のコンテキストで正しく処理できることを保証することはできないため、全箇所で動作確認をし直す必要がある。したがって、エスケープ関数はひとつのコンテキストごとにひとつ用意すべきである。

それぞれの具体的な実装について議論したい。

(したいといいつつ、自分の関数さらしていいいものか判断しづらいので、今度書く)

安全なプログラミングの指針

ここでは、XSSに対して安全でシステムを壊しにくいプログラミング手法について議論する。

筆者がおすすめする方法は以下である。

  • データはできるだけ生の状態で変数に格納する
  • 出力時に必ず適切なコンテキストのエスケープをひとつする
  • どうしても必要な時をのぞき、dom操作以外のjavascriptからの出力をしない

データを生の状態で変数に格納する理由は、先の説で述べたのと同様に、コンテキストごとの適切なエスケープが困難になるからである。これについては次節で例をあげて説明する。

出力時には、必ず適切なコンテキストのエスケープをひとつだけ行う。複数のエスケープを行うと問題が起こる可能生を前説で説明した。エスケープを忘れることは論外であるが、これについてはコミット時やデプロイ時にフックでチェックする方法が多く存在する。またsmartyを改変しエスケープ関数がついていないとエラーを出し知らせることも行われている。

dom操作以外のjavascriptからの出力をしないことについても前説で議論した。多くの場合、javascriptからの出力はdom操作に代替可能であり、textnodeを作りテキストで出力することでXSSを防げる。

すべてをhtmlエスケープしてしまうfail safeの欠点

これはここで提案されている方法である。以下に引用する。

ちなみに自分は、文字列テンプレート+innerHTMLというのを好んで使うので、自社サービスに付いてはサーバー側であらかじめエスケープされているJSONを使うのを好む。これはJavaScriptで余計な文字列処理をしなくていいというメリットがある。

簡単にいうと、javascriptに値を渡した段階でhtmlエスケープされている状態にしておき、javascriptで全くエスケープ処理をしなくても大丈夫な様にする処置である。つまり何も考えずにdocument.writeを実行しても安全である。

この方式の欠点は、document.writeをする場合以外ではhtmlエスケープを元に戻す必要がある点である。例えば、ajaxに値を渡す場合やjavascript上で文字列処理をする場合などである(後者はあまりないかもしれないが)。php上でのエスケープ処理を把握し、逆エスケープ処理をjavascript上で構築する必要があり保守性の低下やバグの温床になりやすい。もちろん、サイト構築時のポリシーや設計次第では有効であるが、汎用的な解決策であるとはいい難い。また、すでに構築済みのサイトである場合ほかのエスケープ処理とバッティングしやすく、導入が困難である場合が多い。また、この方法で設計したサイトを後に変更することも同等に困難であり、設計の柔軟性が損なわれる。

先に議論した通り、多くの出力操作はdom操作に代替可能であり、差し迫った必要がない限りはそもそもエスケープする必要がない。

この手法は初期開発時の設計次第では大変有効ではあるが、dom操作の有用性や既存のシステムへの導入の困難性、システム変更の困難性から、あまりおすすめできないと筆者は考える。

もともこもない対策: base64

これまで、コンテキストとエスケープの関係を議論してきた。XSS対策の困難さはコンテキストにあったエスケープ手法を選択できないところにあることを示した。

ここで、筆者が最近諸事情でとった手法がbase64でエンコードしてしまう手法である。実はbase64で値を埋め込みjavascriptやphpのそれぞれでデコードすれば多くの場合は安全になるのである。もう一度XSSの起きやすいコンテキストについて考えてみよう。

CODE:
  1. var hoge = "{$hoge|base64_encode}";

多くの場合がこれの類型であり、ダブルクオーテーションで囲まれている範囲内に特定のメタ文字を挿入することでXSSを起こすようになっている。一方でbase64の文字種を見てみよう。

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+=

htmlやjavascriptで危険な文字種が一切現れない。これを受け取った側でdecodeすれば、XSSの危険性を減らしながら生データを扱うことができることがわかる。URLセーフでない文字列も存在するが、これには代替があるためURLセーフにすることも可能である。

つまり、コンテキストがわからないならbase64エンコードしてしまえば、少なくとも値の受け渡しで失敗することは激減する。javascriptの出力側でdocument.writeや値の連結について注意すればXSSの問題はほぼ起きないと筆者は考える。

base64は、その変換ルールからデータのサイズは必ず4/3倍になることが知られており、データサイズの管理も簡単である。また、変換ルールは単純であるため効率よく変換もできる。

base64を使えば100%安全ということはあり得ないが、細かな詳細な議論ができない環境では有効である。

まとめ

本投稿では、XSSの対策や包括的理解をすすめることを目指し、その対策に対する考え方や施策のひとつを提案しました。

まず、XSS一般についてふれ、その原因や背景について述べました。コンテキストという概念を示し、それとエスケープ処理の対応について述べました。そして、コンテキストを絞り込みそれぞれに対してひとつずつエスケープ方法を用意するというシンプルな考え方を提案し、それがXSSに対応できることを説明しました。

個人的には「なんで今さらXSSなの?」といわれそうだなとも思ったんですが、直近でこのことで大変もめた経緯があり、また、結局包括的な説明などがあまりない状況をどうにかした方がいいだろうなと思いブログ記事にしました。

長文過ぎるので、「単純なハウツー」を求めている人には役立たないかもしれないですが、みなさまの勉強の役にすこしでも立てればなと思います。

本当は怖い3×5!=5×3

  • Filed under: 雑記
Tuesday
Nov 16,2010

ネタもと404 blog not found 3x5!=5x3

ここ最近よく話題になっていたので、なんとなく見ていたのだけど、ネットのほとんどの人は「交換してもマルでいいじゃないか」「これで誤答扱いはひどい」と思っているようです。
個人的には、これらの指摘こそひどいなぁと思って、久しぶりにブログ書いてみました。

主な論点は、4つ。

  • 「文章題」は、問題文の理解を問うている
  • 書式のなってない報告書・レポートは正当化されない
  • 書式に理由なんかない
  • じゃぁ3x5は5x3でいいのかなぁ?

「文章題」は、問題文の理解を問うている。

じぶんも実はこれにひっかかりました。というか、もっとひどかったかもしれません。
なにしろ私は、式すら書かず答えしか書かなかったのですから

「皿が5皿ある。1つのお皿に3つずつりんごが載っている。全部でいくつか。」

答え:15こ

小学生でもこのくらいの暗算は出来る人はいると思います。しかし、当たり前ですが減点対象です。当時小学生だった私は納得いきません。「答えは合っているのになんで減点されなくちゃいけないわけ?」あぁかわいく無い俺。

「3x5でも5x3でもいいじゃないか!」と言ってる皆さんは、もちろん「式なんか書かなくたっていいじゃないか!」というのにご賛同いただけると信じております。

冗談はともかく。

そのとき先生に言われたのはこんな感じのことだったと思います。

はらだくんが暗算得意なのは先生もよくわかっているよ。けど、この問題は「問題文がきちんと理解できているか」も点数になるの。だから、問題がきちんとわかっていることを書いてくださいね。

まぁこんなので納得する私ではないので(自分のことながら恥ずかしい・・・)、この後「かける数かけられる数の順番が違う」だとか「15この『こ』が抜けてるので減点」とか、ことあるごとにひっかかり、その度ごとに先生につっかかり偉い迷惑かけました・・・・いや、まじですみません・・・・

けど、今から思ってみれば、「問題の意図をつかみ」「相手に正確に伝える」ことの重要性から納得できるなと思います。

書式のなってない報告書・レポートは正当化されない

今時は、大学のレポートなんかろくに返却されないし、返却されても見ない人がほとんどかもしれません。まぁ私は取りにすら行きません。(死
そんなある日、某教授から「最近の若者の国語教育がひどい」という話を聞かされました。

だいたいこんな感じの内容でした。

言葉というのは時代とともに変わるので、言葉遣いがおかしいのはまだ我慢できなくも無い。しかし、論理展開や書式があまりにも稚拙でそれが我慢ならない。

国語教育は情操教育という理念の元に、感想文ばかりかかせる。そのせいかわからないが、感想文のような文章ばかり提出される。

えぇっと死にたくなりました私・・・・・・

もちろん良識ある社会人の皆様なら、報告書や論文の書式の重要さは十分に理解されてることでしょう。けど、参考までにいくつかURLを列挙しましょう。

あまり思い出せなかった・・・・けど、この2つをみれば「内容の重要性」と同じくらい「書式」が重要であることがわかるでしょう。
むしろ、「書式が整ってないと重要であることが伝わらない」ことすらあり得ます。

書式というのは「読む側」になるとその重要性がわかります。不真面目ながら院生をしている私もたくさんの論文に目を通すので必然的に似通ったフォーマットに慣れてきます。
そうすると、なんとなく効率的に読む方法が見えてきます。
しかし逆に書式に従ってない論文というのはものすごく読みにくいことがわかります。

プログラマの人にわかりやすい例を考えてみました。JavaとかPHPのドキュメントは優れてると思いませんか?なんででしょう。
それは、各メソッドが全ページにわたって同じ書式で書かれているからな気がしませんか?

書式に理由なんかない

あるのかもしれませんが、生まれてから27年納得のいく理由を聞いたことがありません。
もちろん内容に合わせて、項目が設定され、項目ごとにそれなりに合理的なスペースが割かれているのですが、 詳細なレベルでは学会やドキュメントなどによってまちまちであると思います。

じゃぁ3x5は5x3でいいのかなぁ?

もちろん数式としては等価なわけですね。けど、あの文章題の答えとしてはどうでしょうか?
当時小学生だった私は、先生に「私は、リンゴと皿の関係を理解し、正しく式に直し、そこから答えを導きだせました」ということを示さないといけなかったわけです。

それにはまず、「3x5」という共通の書式で式を立てる必要があるわけです。この書式が守れてないと「この人は数字を適当に拾ってきてかけただけなんじゃないか?」と解釈されても文句は言えないと思いませんか?

まとめ

文章題というのは、まず「問題の理解」を問うていることをお話ししました。
そして、それを伝えるためには「書式」が重要であること、そして「書式」に理由をもとめることは難しいことをお話ししました。

正直な感想を言えば「3x5とかそんなのどっちでもいいよなぁ」とも思うわけです。しかし、昨今の諸先輩方の「若者の文章が崩壊しすぎててヤバい」というのを聞いたり、たくさんの書類/文章を見ているうちに、この「3x5」に潜む本質的な問題というのに恐怖を覚えずに入られませんでした。

終わりに

この文章は感想文です。

あと、教育指導要領とかもよく知りません。ただ、「どっちで書いても同じ」というのは「適当な書式でレポート書いてもいい」というのと同じな気がしますし、それは一般社会では受け入れられないということを言いたかっただけです。

  • Comments Off
  • about me

    原田 惇

    応用数学や統計、ITなどを利用していろんなことをしています。
    自己紹介はこちら

    adsense

    amazonお勧め

    Recent Posts


    Recent Comments


    Twitter



    Archives


    Meta