ikepyonのだめ人間日記

セキュリティに関することを書いていく予定。

サニタイズ脳?

某所にて以下のようなコードを見つけてしまった。

<?php

$name = htmlspecialchars($_post["name"], ent_quotes);
$mail = htmlspecialchars($_post["mail"], ent_quotes);
$mail = mb_convert_kana($mail, "a");
$sub = htmlspecialchars($_post["sub"], ent_quotes);
$freeans = htmlspecialchars($_post["freeans"], ent_quotes);
$check = htmlspecialchars($_post["check"], ent_quotes);

if(!(($check == "1") || ($check == "0"))){
$error = "変数checkが予測されている値ではありません。";
}

//入力チェック
if(strlen($name) == "0"){
$error = "氏名を入力してください。";
}if(strlen($mail) == "0"){
$error = "メールアドレスを入力してください。";
}if(preg_match("/^[^@]+@([-a-z0-9]+\.)+[a-z]{2,}$/",$mail)){
$error = "メールアドレスの形式が正しくありません。";
}if($check == "0"){
$error = "個人情報の取り扱いについてに同意してください。";
}
if(count($error)){
exit;
}

//入力情報をメールで送信
if (mb_send_mail("hoge@example.com", $sub, $final, $from)){
}else{
$error = "ご記入頂いた情報の転送に失敗しました。";
}
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<body>
<form method="post" action="<?=$_server["php_self"]?>">
<p>氏名:</p>
<input class="form" type="text" name="name" value="<?=$name?>" />
<p>返信用メールアドレス:</p>
<input class="form" type="text" name="mail" value="<?=$mail?>" />
<p>件名:</p>
<input class="form" type="text" name="sub" value="<?=$sub?>" />
<p>お問い合わせ内容:</p>
<textarea class="form" name="freeans" rows="4" cols="40"><?=$freeans?></textarea>
<input name="check" type="checkbox" value="1" <?if($check == "1"){echo"checked";}?> />個人情報の取り扱いについて同意する。<br>
<input type="submit" name="send" value="送信" />
</form> 
</body>

これって、XSSの対策はhtmlspecialchars()使えばいいという知識だけがあって、何のためにやるのかを理解してないんだろうなぁ。いろいろ脆弱性対策の解説はあるけど、実際のコードではどうすればいいのか?ってのが抜けているのかもしれないなぁとこれを見て思った。

コードによる脆弱性が発生する仕組み

コードによる脆弱性が発生する原理は、XSSにしろ、SQLインジェクションにしろおんなじ何だが、理解できてない人には理解できないみたいなようなのでつれづれに書いてみる。
どんなアプリケーションも以下のような構造をしている。

入力データ→モジュールA→データ1→モジュールB→データ2→モジュールC→データ3→・・・・・→モジュールX→出力データ

要するに、どこかしらから入力データを受け取ったモジュールが、そのデータをごちゃごちゃ処理を行い、その結果をデータとして別のモジュールに渡す。そしてデータを受け取ったモジュールが今度はそのデータを元に処理を行って、また新しいデータを出力する。これを延々伝言ゲームのように繰り返すわけだ。
まあ、大きく考えると一つのモジュールはブラウザ、RDBMS、Webアプリケーションといったものになるし、粒度を小さくして関数やメソッド単位にして考えてもいい。どっちにしろ、あらゆるアプリケーションは複数のモジュールを組み合わせて作られている。

さて、伝言ゲームでは、受け取った情報を隣の人に伝えてるだけなのに、最初の人が受け取った内容と、最後の人が受け取った内容がまったく異なることが多々ある。これは、各人が受け取った情報の伝達にミスがあるとか、勝手に解釈するからというのがあるために発生するわけである。コードによる脆弱性もまったく同じようなことが起こっているために起こる。

つまり、モジュール内では問題なく解釈し処理を行っているのだけれど、他のモジュールに渡すデータが受け取るモジュールからするとあいまい(正確に言うと違うのだけれど)というか別の意味に解釈できるようなデータを渡してしまう。この結果、開発者が意図しない処理を行ってしまう。これが、脆弱性として表面化するのである。受け取るデータが明確におかしなもの(伝言ゲームの例で言えば、出題内容が日本語とされているにもかかわらず日本語の文法に沿ってないものとか)であれば、受け取る側で「コレはおかしい!」と判断して、エラーなり、なんなり処理ができるんだけれども、たまたまデータはおかしくないけれど、別の意味に取れるようなものであれば、別の意味として処理を行ってしまう。

日本語で言えば、例えば次のような文章があったとする。

ここではきものをぬいでください

コレを書いた人(開発者)は「ここで履物を脱いでください」という意味でこの文章を書いたにもかかわらず、受け取った人(RDBMSとか他のモジュール)は「ここでは着物を脱いでください」ととって服を脱ぎだすということが起こっているかもしれない。
元の文は「は」を「HA」と読むか、「WA」と読むかで大きく意味が違ってくる。
日本語では「は」という文字は特別な意味を持っており、「WA」と読むことで「助詞?」になったり、「HA」と読むことでただの「は」という文字になったりする。先の文を一意の意味を持つようにするためには以下のようにすればいい。

ここで、はきものをぬいでください

こうすることで「は」は「HA」としか取れなくなる。もちろん「はきもの」を「くつ」に変えてしまって、「ここでくつをぬいでください」としても日本語としてはほぼ同じ意味を持つので問題ないと言うかもしれない。しかし、文章が改変されてしまい、もとの文章とは違うものになってしまっている。「くつ」をぬげとあるが、では「げた」は脱がなくてもいいのか?とか「さんだる」は?という具合に厳密に考えると不具合が起こる。もっとも、人間はいい加減なので「くつ」=「はきもの」と解釈するので現実社会では問題は起こらないだろう。


これと同じこと(「は」を「WA」でなく「HA」と読んでしまうこと)がコードの脆弱性でも起こっている。例えばSQLインジェクションであれば「'」は文字列リテラルの区切りを意味するので「Tully's」といった文字列リテラルをそのまま検索しようとすると「'」が文字列リテラルの終了とみなされて、SQL文法エラーが発生してしまう。
これを防ぐには「は」を「HA」と確実に読ませるように「、は」としたように、「'」を文字列リテラルの区切りをいう意味を持たないただの文字データ「'」として表す必要がある。そのためには「'」を「''」にするという処理を行う。こういった特別な意味を持つデータを特別な意味を持たないデータにする処理をエスケープ処理と呼ぶ。この処理を行うことで、RDBMSは、開発者の意図どおりの意味を解釈することができる。

こう理解するとコードの脆弱性対策って当たり前のことと思えないかなぁ?

iモードのセキュリティガイドライン

http://www.nttdocomo.co.jp/service/imode/make/content/browser/
ガイドラインといっておきながら、内容が無いようw
せめてリンクもIPAのトップページじゃなくて、安全なWebサイトの作り方とか、セキュアプログラミング講座とかへのリンクにしとこうよ。
IPAのトップページのリンクなんかじゃきっと見つけられない人出てくるよw
まあ、auとかソフトバンクはこういったガイドラインを出そうという気がないようなので、それよりましだけどさ

ほんとに安全なアプリ開発に高いコストが必要か? そのに

開発側でやるべきことを書く前に、アプリケーションの脆弱性についてどこの工程が原因で脆弱性が発生するのかちょっと分類してみる。
あらゆる脆弱性は、以下のどれかといっていいと思う。

  1. 仕様の問題による脆弱性
  2. 設計の問題による脆弱性
  3. コードの問題による脆弱性
  4. 運用の問題による脆弱性

現状これらがすべてごっちゃになって論じられているのではないだろうか?

仕様の問題による脆弱性

仕様の問題による脆弱性を実装段階で何とかしようとすると確かに非常に莫大なコストがかかる。しかし、仕様策定の段階でこれを対策しておけばそれに比べるとコストはかからない。もちろん、そういったことを洗い出さずに仕様策定するよりはコストがかかる。ただしこの仕様策定段階でのセキュリティ対策というのは発注側が本来コストを払うべきものではないだろうか?
開発側からすれば、発注側の会社がどのようなセキュリティポリシーを持っていて、どういう脅威を問題視するか?というのはわからないと思う。これは通常の機能についても同様だ。発注側は思いつく限り自分たちがほしいと思う機能を上げて、それが実現可能かどうかも考えずに、全部作れという。その結果莫大なコストがかかるというのはよく聞く話だ。発注側は想定したコスト内で実現するために、どの機能が必要で、どの機能はいらないといった重要度を考えなければならないが、同じことをセキュリティ面でも考えなければいけないと思う。
こういったことをやらずに何でもかんでも機能を追加しているのではないか?

設計による脆弱性

仕様の問題による脆弱性と同様の議論が成り立つと思われる。加えて、この段階でコードによる脆弱性が発生するのを少なくする仕組みを作ることで、コードによる脆弱性を減らすことができる。例えば、SQLインジェクションが発生しやすいというのであれば、SQL文を一切書かなくてもいいフレームワークなり、デザインパターンを使用するようにすればいい。また、安全なコーディング規約を決めて、それにしたがってコードを書くように教育しておくということも有用だと思う。もちろん、アプリケーションの仕様によってはフレームワークなどが使えない場合もあるだろう。その場合は、黙っていても安全なコードを書ける技術者にその部分の実装をお願いすることになるかもしれない。
設計でつぶせる脆弱性というのをまったく考えずに設計していることで、余計なコストがコーディング時に増えてないだろうか?

コードによる脆弱性

よく聞くSQLインジェクションXSSはこの部類になる。これらの脆弱性は当たり前のことをしていればほぼ防ぐことができる。しかし、現状その当たり前のことがなされていないことが多い。これはプログラミングの教育においてセキュリティは難しいからということや、当たり前にしなければならないことを説明が難しいからという理由できちんと教えていないことが原因ではないだろうか?教育には確かにコストがかかるが、それは安全なアプリを作るには必要なことだろう。この部分をケチって余計なコストをかけているということはないだろうか?
安全なコードを書くためのコーディング規約をつくり、それを遵守させるというのも対策としてはいいだろう。
また、脆弱性を発見するためにセキュリティテストをする工数がかかるというのもあるだろう。しかしこのテスト工数は自動化ツールを使うことで削減できるだろう。もちろん、自動化ツールで検出できる脆弱性は限られているが、多くの攻撃というのは自動化ツールで見つけられるものだったりするので、ある程度役には立つ。
もっとも、テストというのは実装したものが、仕様どおりに動いているかどうかを確認するためのものと思うので、テストで脆弱性を発見するというのはちょっと違う気もするけど・・・
確かにテスト工数は大きくかかるが、現状の開発ではテストがおざなりになっていることは否めない。設計が終わった段階で、自動テストツールを並行して作るチームというのもあってもいいかもしれない。そうすることで、納期が短くてもテストが十分できるようになるのではないか?もっとも、テストチーム事態がコスト増加の原因となるだろうけど、複数プロジェクトで似たようなものが作れると思うので、量産化できるのではないか?Webアプリに関していえば、そういった汎用ツールって結構できるんじゃないかな?

運用による脆弱性

日々の運用は重要であるにもかかわらず軽視されることが多い。まあ、正常に動いて当たり前というのがあるからなぁorzまた、実装の段階で実装が困難なものを運用でカバーするという無茶も結構あるのではないだろうか?これは開発側が運用のことを考えずに設計しているということが大きい気がする。結果ミスをしやすい運用になったり、そもそも運用条件が現実的にありえないとかありそうである。こういったものも設計の段階からきちんと考慮していれば防げるだろう。

というわけで、(何がというわけかわからんがw)きちんと各工程でセキュリティについて考えていれば大きくコストが跳ね上がることはないのではないだろうか?現状それができていないor何をやればいいのかわからないというのが問題で、やるべきこと、考えることというのはそれほど難しいものではないと思う。また、業務アプリなんてそうそう大きな違いがないのだからテンプレート化、ツール化ってできるのでは?
そういったものを作るには確かにそれなりの知識や技術が必要だろうけどねぇ。