CakePHPとかLaravelとか、Ruby on Railsとかを最近始めてみたけど、
セキュリティについては全然わからなくて、ちゃんとできているか怖いという人のための、
少なくともこれだけは気を付けてコードを書いた方がいいよというお話。
この記事ではコードを書き始めた初心者が、
気づかぬ内に脆弱性を作りこみやすい場所をピックアップして、
勉強のとっかかりになることを目標にしています。
それとセキュリティに詳しい方々、
もしこれも書いた方がいいよというのがあれば、どうかご教示ください。
アプリケーションの話
Viewの変数は全てエスケープする
どの変数がエスケープ必要で、どれが必要じゃないかは判断するのがとても難しいので、
片っ端からエスケープしまくりましょう。
// PHPのコード内でエイリアスのメソッドを用意して
function h($str){
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
// Viewでは簡単に書けるようにすると良い
<?= h($hoge) ?>
# Rails
# デフォルトで何もしなくてもエスケープされています
<%= @hoge %>
// Javascript(jQuery)
// html()ではエスケープされません!
$('.js-hoge').html('<script>alert("XSS")</script>');
// こっちが安全
$('.js-hoge').text('<script>alert("XSS")</script>');
今書いているコードでは、エスケープしなくても安全でも、
Viewをコピペした先の変数が危なくて、エスケープ漏れするみたいなこともあります。
チェック漏れを無くすためにも、面倒でも必ず全部エスケープしましょう。
「XSS」で検索するとより詳しい情報が手に入ります。
小技
ユーザが入力できるフォームとか、Get, Postのパラメータに、
片っ端から<marquee>
というタグを突っ込んでみます。
ページが動きだしたらエスケープ漏れしています。
この方法では全てのエスケープ漏れを検出することはもちろんできないですが、
この簡単な方法でも引っかかってしまったら、危機感を持った方がいいかもです。
SQLはプレースホルダーを利用する
CakePHPや、Laravel、Ruby on Railsなどを利用していると、
普通の処理では自分でSQLを書かないかと思いますが、
少し難しいSQLを実行したくて、
インターネットで見つけたものを使う時は要注意です。
// これはだめ
$sql = 'SELECT * FROM users WHERE id = ' + $user_id;
// こっちが正しい
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bindValue(1, $user_id, PDO::PARAM_INT);
# Railsでこんなことしないと思うがダメなコード例
User.where("name = #{user_name}")
# こう書こう
User.where(name: user_name)
User.where("name = ?", user_name)
フレームワークで、$user->find_by_id($user_id)
みたいなコードを書いていると、
意識しないですが、ちゃんとエスケープしましょう。
「SQLインジェクション」と呼ばれています。
インラインJSを動的に生成しない
たまにこんなコードを書きたくなくかもしれません。
このコードはエスケープしてあるから安心と思うかもしれませんが、危険です。
<script>
// ダメなコード(PHP)
var hoge = "<?=h($hoge)?>";
// ダメなコード(Ruby)
var fuga = "<%=@fuga%>";
</script>
htmlspecialcharsはHTMLのエスケープのための関数なので、
JSでのエスケープには対応していません。
JSのエスケープは人間には複雑すぎるので
おとなしくdata属性で受け渡しをしましょう。
<div class="js-param-hoge" data-param="<?=h($hoge)?>"></div>
<script>
// jQueryが読み込まれているとする
var hoge = $('.js-param-hoge').attr('data-param');
</script>
ちなみにdata属性ではなく、
styleとかonclickとかにユーザ入力を入れると
エスケープしていても脆弱性が生まれます。
http://qiita.com/tomochan154/items/a93c56536c78d1faff0f
それから僕らの想像をはるかに上回る、
ありとあらゆるXSSの方法があるそうです。
http://unageanu.hatenablog.com/entry/20100619/1276921005
ファイルアップロードを自分で実装しない
実装方法を知ったり、実装できるようになる事はとても有意義ですが、
ファイルアップロードのセキュリティなどは一般人には難しすぎるので、
有名でかつ、メンテナンスされているライブラリを使った方がいいです。
拡張子は.jpg
なのに中身がjavascriptで、
HTMLで読み込むとjavascriptが実行されてしまうファイルとか、
意味不明な名前のファイルとか、えげつないサイズのファイルなど、
対策しなければいけない事は多岐にわたります。
ユーザ入力値でファイル名を生成しない
例えばユーザ入力値で、ファイル名を想定していて、
以下のコードを書いたとします。
<?php
// ダメなコード
$path = '/var/www/html/public/' + $_POST['path'];
この$_POST['path']
に相対パスを入れられて即死です。
その他にも気を付けることは多岐にわたるので、
パスは動的に生成しない方がいいです。
「ディレクトリトラバーサル」などで検索すると、より詳しく勉強できます。
ユーザ入力値をコマンドの引数にしない
よくOSコマンドインジェクションに絡んだ、
情報流出を引き起こしているのがこれです。
PHPならsystem()
やexec()
などの
コマンドを実行する関数を含むコードは自分で書かずに、
ライブラリを使用しましょう。
exec()
の引数が文字列だけとかならまだしも、
少しでもユーザ入力を含んでいると、
ヌルバイトが入ったり、複数のコマンドを同時に実行されたりして、すぐに死にます。
ログイン機能を作るのは恐ろしく難しい
正確に言うと
ログイン機能を正しく作るのは恐ろしく難しいです。
ログイン機能を作ろうとすると最低限以下の機能を実装することになります。
- データベースにユーザIDとパスワードを保存する
- Postで送られてきたユーザ入力をデータベースに照会し正しければログインしたことをセッションに保存する
これを自分で実装すると、本当に驚くほどたくさんの、脆弱性が入る可能性があります。
HTTPS化することや、
パスワードのハッシュ化しての保存と照会、
CSRF(Cross Site Request Forgery)対策や、
セッション固定攻撃への対策、
キャッシュの正しい設定など
本当に大量に注意すべきことがあります。
HTTPSにするのは自分でするとして、
それ以外の事はフレームワークの機能を利用しましょう。
Railsなら最も有名なDeviceや、軽量なSorcery、
CakePHPやLaravelにはフレームワーク固有のログイン実装方法があるので、
それを使用するのが無難です。
忘れがちですが
PHPで直接出力するHTMLには認証がかかっていても、
JSファイルには認証を書けていないことがあって、
そこから情報が流出する可能性もあります。
ログイン後のJSやCSS、画像ファイル等にも注意しましょう。
その他もろもろ
VPSを使わない
セキュリティについての知識が浅いうちは、
自分が管理しなければいけない範囲をできるだけ狭めた方がいいです。
VPSを使用すると、
死活管理やセキュリティアップデート、
ポートの管理からDoS攻撃への対策など、
様々に心配することがでてきます。
万が一、サーバを動くように構築できたとしても、
サーバの管理は、初心者には絶対に無理なので、
PHPを公開するなら、レンタルサーバを、
Railsを公開するなら、Herokuなどを使った方が安心です。
マイナーなライブラリを使わない
たとえばGitHubのスターが1桁しか付いていないようなものや、
数年前からメンテナンスされていないライブラリを使うのは、結構危険です。
あなたが完璧にコードを書いていたとしても、
そのライブラリに脆弱性があればそれまでです。
ユーザ入力は信用しない
ここでのユーザ入力とは、
URLのパラメータや、Postに乗ってきたデータなどを指します。
もう全てのユーザ入力はウィルスの侵入経路ぐらいの勢いで対策しましょう。
あなたが数字だと思って処理していたとしても、文字列を送り付けられるかもしれません。
文字列だと思っていたら、配列で送られてくるかもしれません。
もしかしたらバイナリの可能性だってあります。
あとヌルバイトや、SQLの文字列、コマンドラインで実行できる文字列かもしれません。
とにかく、めっちゃ気を付けて扱いましょう。
ちなみに数年前に投稿フォームのある掲示板をこっそり公開していたら、
一日に少なくとも100件以上のスパムや攻撃のようなコードが送信されてきていたので、
誰にも見られていないから安心と思っていても、
それはたぶんあなたが気づいていないだけです。
小ネタ
URLのクエリストリングで、
http://example.com/index.html?page=3
というURLがあるとすると、
あなたはコードで$_GET['page']
またはparams['get']
には、
数字の形式の文字列が入ってくることを期待していると思いますが、
http://example.com/index.html?page[]=3&page[]=4
というURLを
ブラウザのロケーションバーに直接打たれると、
$_GET['page']
またはparams['get']
は配列になってしまいます。
これらは割と忘れがちなので注意してコーディングしましょう。
攻撃方法を知る
防御するためには、相手がどこから責めてくるかを知らなくてはいけません。
体系的に学びたければ、「徳丸本」と呼ばれている本があっておすすめです。
「体系的に学ぶ 安全なWebアプリケーションの作り方」徳丸浩著
その他、オススメのページや、書籍があれば教えてください。