Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

Organization

CSRFに関して

CSRFに関して

by nouka
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の場合もトークンを生成し検証する方法が最も安全。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?