问题
I am looking for a way to add a CSRF token to an application I'm making. The caveat is, the application does not currently use cookies or sessions.
I would love to find a way to introduce a CSRF token without having to:
- Introduce state in my application.
- Use session or cookie (
$_SESSION
/$_COOKIE
) storage
Is this at all a possibility, or am I stuck creating new state in my application.
回答1:
You can, but it's not going to be very secure.
Here's an example BUT DO NOT USE THIS, IT IS PROBABLY A BAD IDEA:
// 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
}
The downside is that these tokens are inherently reusable, so if one leaks an attacker can simply reuse (or recalculate) it. You can also try adding the form action="" value in the hash calculation.
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";
// ...
What's your anathema for sessions anyway?
回答2:
You can use a lightweight db and store a token, along with the ip adres, user agent, expiry time and requested url or you can try using a database and session storage to store a key with which tokens are created. E.g.
$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 . "' />";
回答3:
CSRF protection works by comparing a token embedded in a page with a token associated with the browser that the page was sent to.
It is absolutely impossible to do this without introducing state.
来源:https://stackoverflow.com/questions/28679327/csrf-token-without-cookies-in-php