PHP 中没有 cookie 的 CSRF 令牌

CSRF token without cookies in PHP

我正在寻找一种方法来将 CSRF 令牌添加到我正在制作的应用程序中。需要注意的是,该应用程序当前不使用 cookie 或会话。

我很想找到一种引入 CSRF 令牌的方法,而不必:

  1. 在我的应用程序中引入状态。
  2. 使用会话或 cookie ($_SESSION / $_COOKIE) 存储

这是否有可能,或者我是否一直在我的应用程序中创建新状态。

CSRF 保护的工作原理是将页面中嵌入的令牌与页面发送到的浏览器关联的令牌进行比较。

不引入state是绝对做不到的

可以,但不会很安全。

这是一个例子但不要使用它,它可能是个坏主意:

// Do this somewhere
define('CSRF_SALT', '4dedMj4CWgBMNhRsoTpJlokwB5wTz7UsmF8Mq4uzFIbv');

$token = base64_encode(
    hash_hmac(
        'sha256', 
        date('Ymd') . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'],
        CSRF_SALT,
        true
    )
);

if (\hash_equals($_POST['security_token'], $token)) {
    // Form passes validation
}

缺点是这些令牌本质上是可重用的,因此如果有人泄漏,攻击者可以简单地重用(或重新计算)它。您也可以尝试在哈希计算中添加表单 action="" 值。

function getToken($action)
{
    return base64_encode(
        hash_hmac(
            'sha256', 
            hash_hmac(
                'sha256',
                date('Ymd') . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'],
                hash('sha256', $action, true),
                true
            ),
            CSRF_SALT,
            true
        )
    );
}

echo "<form action='register.php' method='post'>\n";
echo "<input type='hidden' name='security_token' value='".getToken('register.php')."' />\n";
// ...

无论如何,你对会议的厌恶是什么?

您可以使用轻量级数据库并存储令牌以及 ip 地址、用户代理、到期时间和请求 url 或者您可以尝试使用数据库和会话存储来存储密钥令牌被创建。例如

$post = array();
If (isset($_POST) && !empty($_POST)) {
    If (isset($_POST['csrftoken']) && isset($_SESSION['csrfkey'])) {
        // check if csrftoken exists in db and fetch the row
        $hash = hash('sha256', $row['String'] . $_SESSION['csrfKey']);
        // delete the db record
        If ($hash === $_POST['csrfToken']) {
           $post = (array) $_POST;
           // check if expiry time has not passed
           // check requested url 
           // check user agent and ip adres
        }
    }
    $_REQUEST = null;
    $_POST = null;
}

If (!isset($_SESSION['csrfKey'])) {
    $_SESSION['csrfKey'] = uniqid('key_', true);
}
$string = uniqid('token_', true);
$token = hash('sha256', $string . $_SESSION['csrfKey']);
// save token and string along with expirytime in db
echo "<input type='hidden' name='csrftoken' value='" . $token . "' />";