LoginSignup
5
8

More than 5 years have passed since last update.

CSRFに関して

Last updated at Posted at 2018-07-23
1 / 15

CSRFとは

  • Webアプリケーションの「重要な処理(パスワードやメールアドレスの変更、決済、口座振込など)」は、ユーザが意図したリクエストか確認が必要。
  • 確認が抜けていると、罠サイトを閲覧しただけでユーザのブラウザから勝手に「重要な処理」が実行させられてしまう。
  • リクエスト強要とも。

以下のようなHTMLがあったとします。

<!-- // 正規のサイト -->
<form action="http://hoge.com/password_change.php" method="post">
  <input type="text" name="password">
  <input type="submit" value="変更する">
</form>

それに対して以下のようなHTMLを用意します。

<!-- // 罠サイト -->
<body onload="document.forms[0].submit()">
  <form action="http://hoge.com/password_change.php" method="post">
    <input type="hidden" name="password" value="cracked">
  </form>
</body>

password_change.php で確認の処理が抜けていると、パスワードが勝手に変更させられてしまう。


CSRFの対策


inputのhiddenにトークンを埋め込みます。

<?php
session_start();
if (empty($_SESSION['token'])) {
  $token = bin2hex(openssl_random_pseudo_bytes(24));
  $_SESSION['token'] = $token;
} else {
  $token = $_SESSION['token'];
}
?>
<form action="http://hoge.com/password_change.php" method="post">
  <input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_COMPAT, 'UTF-8'); ?>">
  <input type="text" name="password">
  <input type="submit" value="変更する">
</form>

パスワードの変更受付処理でトークンを検証します。

session_start();
$token = filter_input(INPUT_POST, 'token');
if (empty($_SESSION['token']) || $token !== $_SESSION['token']) {
  die('エラー');
}

  • 他にも「パスワードの再入力を求める」や「リファラをチェックする」といった対策がある。
  • トークンの埋め込みが最も一般的。

CSRF(Web APIの場合)


こんなAPIがあったとします。

session_start();
if (empty($_SESSION['uid'])) {
  header('HTTP/1.1 403 Forbidden');
  die('ログインしてください');
}
$req = json_decode(file_get_contents('php://input'));
// DBにpasswordをセットする疑似コード
$this->db->set('password', ['uid' => $_SESSION['uid'], 'password' => $req->password]);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['result' => 'OK']);

それに対して以下のようなJavaScriptを実行させます。

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://api.hoge.com/passward");
xhr.withCredentials = true;
xhr.send('{"password": "cracked"}');

APIで確認の処理が漏れていると、パスワードが勝手に変更させられてしまいます。


CSRFの対策(Web APIの場合)


ログイン(uidを取得)処理でトークンも生成。

if ($_SESSION['token']) {
  $token = bin2hex(openssl_random_pseudo_bytes(24));
  $_SESSION['token'] = $token;
}
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['uid' => $_SESSION['uid'], 'token' => $_SESSION['token']]);

APIでトークンを検証する。

session_start();
if (empty($_SESSION['uid'])) {
  header('HTTP/1.1 403 Forbidden');
  die('ログインしてください');
}
$token = $_SERVER['HTTP_X_CSRF_TOKEN'];
if (empty($token) || $token !== $_SESSION['token']) {
  header('HTTP/1.1 403 Forbidden');
  die('エラー');
}
...

まとめ

  • CSRFは攻撃者が罠サイトを作成し、ユーザーに罠サイトを閲覧させることで発動する。
  • HTMLフォームの場合もWeb APIの場合もトークンを生成し検証する方法が最も安全。
5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8