公開日:2014/10/26
クロスサイトスクリプティング(XSS)攻撃とクロスサイトリクエストフォージェリ(CSRF)攻撃。
SQLインジェクション攻撃やタイミング攻撃と違って攻撃者は罠を仕掛けて待つだけ。
罠にかかったユーザを本物サイトに誘導して本物サイトの機能を利用して攻撃する。
まとめてメモする。
テスト用サイト
入力ページのソース。
<form action="confirm.php" method="post">
<table>
<tr><td>お名前</td></tr>
<tr><td><input type="text" name="name"></td></tr>
<tr><td>メールアドレス</td></tr>
<tr><td><input type="email" name="mailaddress"></td></tr>
<tr><td>お問い合わせ件名</td></tr>
<tr><td><input type="text" name="title"></td></tr>
<tr><td>お問い合わせ本文</td></tr>
<tr><td><textarea rows="10" cols="30" name="comment"></textarea></td></tr>
</table>
<button type="submit">確認</button>
</form>
確認ページ。
最近ドメインキングにほんとに問い合わせした内容(笑)
確認ページのソース。エラーチェックなし(`・ω・´)シャキーン
<?php
$name = $_POST['name'];
$mailaddress = $_POST['mailaddress'];
$title = $_POST['title'];
$comment = $_POST['comment'];
?>
<h2>お名前</h2>
<p><?php echo $name;?></p>
<h2>メールアドレス</h2>
<p><?php echo $mailaddress;?></p>
<h2>お問い合わせ件名</h2>
<p><?php echo $title;?></p>
<h2>お問い合わせ本文</h2>
<p><?php echo $comment;?></p>
<form action="send.php" method="post">
<input type="hidden" name="name" value="<?php echo $name;?>">
<input type="hidden" name="mailaddress" value="<?php echo $mailaddress;?>">
<input type="hidden" name="title" value="<?php echo $title;?>">
<input type="hidden" name="comment" value="<?php echo $comment;?>">
<button type="submit">送信</button>
</form>
DBに登録された内容。
お名前:合同会社スマート
メールアドレス:iku@smarllc.jp
件名:セキュリティ設定について
本文:SSL 3.0の脆弱性が発表されました。(省略)
罠サイト
クロスサイトスクリプティング(XSS)攻撃
クリックしたら100万円ボタンを押してみる。
くれるんじゃないんかい( ゚д゚)5分て短いなおい
クリックしたら100万円ボタンのソース。
<form action="confirm.php" method="post">
<input type="hidden" name="name" value="<script>alert('100万円払って下さい。\n5分以内に下記に振り込まないとヤバいです。\nスマート銀行\n鴻巣支店\n普通9999999');</script>">
<input type="hidden" name="mailaddress" value="">
<input type="hidden" name="title" value="">
<input type="hidden" name="comment" value="">
<button type="submit">クリックしたら100万円</button>
</form>
これがクロスサイトスクリプティング。
Cross Site ScriptingだけどCSSだとCascading Style SheetsとかぶるからXSSらしい。
本物サイトに移動した上で本物サイトの機能を利用してスクリプトを実行する攻撃手法。
確認ページのようなユーザの入力した内容を表示するページが対象になる。
今回は偽ダイアログを表示してみたけど偽サイトにジャンプさせることもできるしクッキー情報を攻撃者に送信させることもできる。
そのクッキーの中にセッションIDがあればセッションハイジャック(なりすまし)ができるしクレジットカード情報があれば買い物ができる。
スクリプト以外にも文章や画像を埋め込んで改竄したページを表示させることもできる。
罠にかかったユーザはURLを見ても本物ページのため改竄に気づかない。
恐ろしい_(:3 」∠)_
XSS攻撃対策
確認ページで入力値をエスケープしてやる。
<?php
$name = htmlspecialchars($_POST['name'], ENT_QUOTES);
$mailaddress = htmlspecialchars($_POST['mailaddress'], ENT_QUOTES);
$title = htmlspecialchars($_POST['title'], ENT_QUOTES);
$comment = htmlspecialchars($_POST['comment'], ENT_QUOTES);
?>
<h2>お名前</h2>
<p><?php echo $name;?></p>
<h2>メールアドレス</h2>
<p><?php echo $mailaddress;?></p>
<h2>お問い合わせ件名</h2>
<p><?php echo $title;?></p>
<h2>お問い合わせ本文</h2>
<p><?php echo $comment;?></p>
<form action="send.php" method="post">
<input type="hidden" name="name" value="<?php echo $name;?>">
<input type="hidden" name="mailaddress" value="<?php echo $mailaddress;?>">
<input type="hidden" name="title" value="<?php echo $title;?>">
<input type="hidden" name="comment" value="<?php echo $comment;?>">
<button type="submit">送信</button>
</form>
再度クリックしたら100万円ボタンを押してみる。
htmlspecialchars()関数でスクリプトをエスケープしただけ(σ・∀・)σ
禁止文字を決めといてそれが含まれてたらエラーページに飛ばす方法もあるけど悪意のないユーザが普通にその文字を入力したい場合に困る。
入力フォーマットが決まってる項目に関しては可能な限りチェックしてエラーページに飛ばしてあげたらいいと思う。
まあ最悪エラーにできなくてもこんな確認ページが表示されたらそのまま送信とかしないでしょう。
クロスサイトリクエストフォージェリ(CSRF)攻撃
クリックしたら1000万円ボタンを押してみる。
なんか送信された( ゚д゚)何これ怖い
DBにこんな内容で登録される。
お名前:あほ
メールアドレス:あほ
件名:あほ
本文:あほ
クリックしたら1000万円ボタンのソース。
<form action="send.php" method="post">
<input type="hidden" name="name" value="あほ">
<input type="hidden" name="mailaddress" value="あほ">
<input type="hidden" name="title" value="あほ">
<input type="hidden" name="comment" value="あほ">
<button type="submit">クリックしたら1000万円</button>
</form>
これがクロスサイトリクエストフォージェリ。
Cross Site Request ForgeryだからCSRF。
不正にサーバにデータを送信する攻撃方法。
ユーザの入力内容を元にデータを更新するようなページが対象になる。
今回はあほなデータを登録してみたけど掲示板に書き込ませることもできるし買い物させることもできるしパスワードを変更させることもできる。
攻撃者の決めたパスワードに変更させればアカウントハックができる。
恐ろしい_(:3 」∠)_
CSRF攻撃対策
まず確認ページでトークンを生成してセッション変数と送信ボタンに埋め込む。
<?php
session_start();
$token = bin2hex(openssl_random_pseudo_bytes(15));
$_SESSION['token']= $token;
$name = htmlspecialchars($_POST['name'], ENT_QUOTES);
$mailaddress = htmlspecialchars($_POST['mailaddress'], ENT_QUOTES);
$title = htmlspecialchars($_POST['title'], ENT_QUOTES);
$comment = htmlspecialchars($_POST['comment'], ENT_QUOTES);
?>
<h2>お名前</h2>
<p><?php echo $name;?></p>
<h2>メールアドレス</h2>
<p><?php echo $mailaddress;?></p>
<h2>お問い合わせ件名</h2>
<p><?php echo $title;?></p>
<h2>お問い合わせ本文</h2>
<p><?php echo $comment;?></p>
<form action="send.php" method="post">
<input type="hidden" name="token" value="<?php echo $token;?>">
<input type="hidden" name="name" value="<?php echo $name;?>">
<input type="hidden" name="mailaddress" value="<?php echo $mailaddress;?>">
<input type="hidden" name="title" value="<?php echo $title;?>">
<input type="hidden" name="comment" value="<?php echo $comment;?>">
<button type="submit">送信</button>
</form>
DB登録時にトークンが一致しない場合はエラーとする。
session_start();
if($_SESSION['token']=='' or $_POST['token']!=$_SESSION['token']){
echo '無効なリクエストです。';
exit();
}
再度クリックしたら1000万円ボタンを押してみる。
あほあほデータの登録をブロック(σ・∀・)σ
確認ページでランダムなトークンを発行してやることで確認→完了と正規のルートを通らないとエラーになるようにできた。
トークンの生成にはもちろん暗号論的擬似乱数生成器(CSPRNG)を使用。
あとは一度使ったトークンは削除したり期限を設定したり複数タブでの入力も考慮するならセッション変数にトークンを配列で持たせたりするといいと思う。
一度使ったトークンを削除することでF5キーとか再読込による二重登録も防げたり(*´з`)
CSRFトークンでXSS攻撃も対策
CSRF対策として確認→完了のルートをトークンで保障したけど、同じように入力→確認のルートにも使えるんじゃなかろうか。
というわけで入力ページでもトークンを発行して確認ページでもトークンの比較をするようにした。
これでXSS攻撃の場合もエラーが表示されるようになってXSS対策の意味がなくなった(∩´∀`)∩
最初からCSRFトークンだけでよかったのか?
どんなに調べてもXSS攻撃対策は入力値チェックとスクリプトのエスケープであってトークンはあくまでCSRF対策なんだよな。
入力ページでトークンを生成したりセッションを開始したりするのがまずいのか万が一トークンを知られた場合にもスクリプトが実行されなければ安心よてことなのか。
うーんまだ調べる必要がありそうだ_(:3 」∠)_