CSRF防御はサーバー側とクライアント側の両方から開始できます。現在では、一般的なCSRF防御もサーバー側で実行されるため、防御効果が高くなります。
1.サーバー側でのCSRF防御
サーバー側でCSRFを実装する方法はたくさんありますが、一般的な考え方は同じで、クライアントページに擬似乱数を追加することです。
(1).Cookie ハッシュ (すべてのフォームに同じ疑似ランダム値が含まれています):
攻撃者はサードパーティ Cookie を (理論上) 取得できないため、これが最も簡単な解決策になる可能性があり、データ構築は失敗しました:>
<?php //构造加密的Cookie信息 $value = “DefenseSCRF”; setcookie(”cookie”, $value, time()+3600); ?>
ハッシュ値をフォームに追加して、これが実際にユーザーによって送信されたリクエストであることを確認します。
<?php $hash = md5($_COOKIE['cookie']); ?> <form method=”POST” action=”transfer.php”> <input type=”text” name=”toBankId”> <input type=”text” name=”money”> <input type=”hidden” name=”hash” value=”<?=$hash;?>”> <input type=”submit” name=”submit” value=”Submit”> </form>
次にサーバー側でハッシュ値検証を行います
<?php if(isset($_POST['check'])) { $hash = md5($_COOKIE['cookie']); if($_POST['check'] == $hash) { doJob(); } else { //... } } else { //... } ?>
個人的にはこの方法で99%のCSRF攻撃を排除できると思っていますが、まだ1%も残っています… XSSの脆弱性によりユーザーのCookieが簡単に侵害されてしまうためWeb サイトが盗まれた場合は、さらに 1% です。一部の攻撃者を除いて、ほとんどの攻撃者はハッシュ値の計算が必要になると基本的に諦めるので、100% 排除する必要がある場合、これは最良の方法ではありません。
(2).認証コード
このソリューションのアイデアは次のとおりです: ユーザーが送信するたびに、ユーザーはフォーム内の画像にランダムな文字列を記入する必要があります。 CSRFですが、個人的には使い勝手があまり良くないと感じていて、認証コード画像の使用にはMHTMLというバグがあり、Microsoft IEの一部のバージョンでは影響を受ける可能性があると聞きました。
(3).ワンタイムトークン(異なる形式には異なる疑似ランダム値が含まれます)
ワンタイムトークンを実装する際には、「並列セッションの互換性」という点に注意する必要があります。ユーザーがサイト上で 2 つの異なるフォームを同時に開いた場合、CSRF 保護対策はフォームの送信に影響を与えません。フォームがロードされるたびにサイトが擬似ランダム値を生成し、以前の擬似ランダム値を上書きした場合に何が起こるかを考えてみましょう。他のすべてのフォームには不正な擬似ランダム値が含まれるため、ユーザーは最後に開いたフォームのみを正常に送信できます。 CSRF 保護がタブ ブラウズやサイトを閲覧するための複数のブラウザ ウィンドウの使用に影響を与えないよう注意する必要があります。
以下は私の実装です:
1) まずトークン生成関数(gen_token()):
<?php function gen_token() { //这里我是贪方便,实际上单使用Rand()得出的随机数作为令牌,也是不安全的。 //这个可以参考我写的Findbugs笔记中的《Random object created and used only once》 $token = md5(uniqid(rand(), true)); return $token; }
2) 次にセッショントークン生成関数(gen_stoken()):
<?php function gen_stoken() { $pToken = ""; if($_SESSION[STOKEN_NAME] == $pToken){ //没有值,赋新值 $_SESSION[STOKEN_NAME] = gen_token(); } else{ //继续使用旧的值 } } ?>
3)。隠し入力フィールドを生成する関数:
<?php function gen_input() { gen_stoken(); echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\” value=\”" . $_SESSION[STOKEN_NAME] . “\”> “; } ?>
4). WEBフォーム構造:
<?php session_start(); include(”functions.php”); ?> <form method=”POST” action=”transfer.php”> <input type=”text” name=”toBankId”> <input type=”text” name=”money”> <? gen_input(); ?> <input type=”submit” name=”submit” value=”Submit”> </FORM>
5). サーバー側検証トークン:
これは非常に簡単なので、ここでは詳しく説明しません。
実際、上記は「並列セッションの互換性」ルールに完全には準拠していません。これに基づいて変更できます。