跨站請求偽造

問題

跨站點請求偽造或 CSRF 可以強制終端使用者在不知不覺中生成對 Web 伺服器的惡意請求。可以在 POST 和 GET 請求中利用此攻擊媒介。例如,url 端點/delete.php?accnt=12 刪除從 GET 請求的 accnt 引數傳遞的帳戶。現在,如果經過身份驗證的使用者將在任何其他應用程式中遇到以下指令碼

<img src="http://domain.com/delete.php?accnt=12" width="0" height="0" border="0">

該帳戶將被刪除。

該問題的常見解決方案是使用 CSRF 令牌。CSRF 令牌嵌入到請求中,以便 Web 應用程式可以信任請求來自預期源,作為應用程式正常工作流的一部分。首先,使用者執行一些操作,例如檢視錶單,觸發建立唯一令牌。實現此功能的示例表單可能如下所示

<form method="get" action="/delete.php">
  <input type="text" name="accnt" placeholder="accnt number" />
  <input type="hidden" name="csrf_token" value="<randomToken>" />
  <input type="submit" />
</form>

然後,在表單提交之後,伺服器可以針對使用者會話驗證令牌以消除惡意請求。

示例程式碼

以下是基本實現的示例程式碼:

/* Code to generate a CSRF token and store the same */
...
<?php
  session_start();
  function generate_token() {
    // Check if a token is present for the current session
    if(!isset($_SESSION["csrf_token"])) {
        // No token present, generate a new one
        $token = random_bytes(64);
        $_SESSION["csrf_token"] = $token;
    } else {
        // Reuse the token
        $token = $_SESSION["csrf_token"];
    }
    return $token;
  }
?>
<body>
  <form method="get" action="/delete.php">
    <input type="text" name="accnt" placeholder="accnt number" />
    <input type="hidden" name="csrf_token" value="<?php echo generate_token();?>" />
    <input type="submit" />
  </form>
</body>
...

/* Code to validate token and drop malicious requests */
...
<?php
  session_start();
  if ($_GET["csrf_token"] != $_SESSION["csrf_token"]) {
    // Reset token
    unset($_SESSION["csrf_token"]);
    die("CSRF token validation failed");
  }
?>
...

現有許多庫和框架都有自己的 CSRF 驗證實現。雖然這是 CSRF 的簡單實現,但你需要編寫一些程式碼來動態重新生成 CSRF 令牌,以防止 CSRF 令牌竊取和修復。