Ответ 1
Если вы не знаете эти ссылки, этот должен помочь вам понять некоторые сценарии, а именно расскажут вам ДО и DONT. Надеюсь, что это поможет.
Мне интересно, если это безопасный способ установить токен, если на самом деле не создан токен, я его генерирую и использую во всех приложениях и в этих формах. Один токен за сеанс?
if (!isset($_SESSION['token'])) {
$data['token'] = uniqid(rand(), true);
session_regenerate_id();
$_SESSION['token'] = $data['token'];
}
Было бы необходимо очистить токен от поданной формы? или просто остаться с ним, хотя я представил форму?
Если вы не знаете эти ссылки, этот должен помочь вам понять некоторые сценарии, а именно расскажут вам ДО и DONT. Надеюсь, что это поможет.
Лично я бы сгенерировал новый токен для каждой формы, которую я хочу отобразить. Если вы сделаете это так, кому-то просто нужен куки файл сеанса, чтобы прочитать токен и использовать его, пока сеанс остается активным.
В моих приложениях я генерирую токен для каждого отображения формы следующим образом:
<?php
$token = uniqid(rand(), true);
$_SESSION['csrf_tokens'][$token] = true;
HTML
<form>
<input type="hidden" name="token" value="<?php echo $token ?>" />
</form>
При проверке формы я проверяю этот токен следующим образом:
if (isset($_SESSION['csrf_tokens'][$token]) && $_SESSION['csrf_tokens'][$token] === true) {
unset($_SESSION['csrf_tokens'][$token]);
// additional code here
}
Вместо использования per-session token
я бы предпочел per-form/url token
для дополнительной безопасности, некоторые могут утверждать, что per-request token
наиболее защищен, но влияет на удобство использования.
Я также считаю, что лучше отделить хранилище сеансов от хранилища токенов и использовать что-то вроде Memcache
. Это лучше, когда вам нужна скорость с использованием нескольких серверов приложений и т.д. Я также предпочитаю это, потому что я могу добавить пользовательский expiration to the token
, не затрагивая всего session
Вот типичный пример
HTML
<form method="POST" action="#">
IP:<input type="text" name="IP" /> <input type="hidden" name="token"
value="<?php echo Token::_instance()->generate(); ?>" /> <input
type="Submit" value="Login" />
</form>
Обработка
$id = "id44499900";
Token::_instance()->initialise($id); // initialise with session ID , user ID or IP
try {
Token::_instance()->authenticate();
// Process your form
} catch ( TokenException $e ) {
http_response_code(401); // send HTTP Error 401 Unauthorized
die(sprintf("<h1>%s</h1><i>Thief Thief Thief</i>", $e->getMessage()));
}
Используемый класс
class Token {
private $db;
private $id;
private static $_instance;
function __construct() {
$this->db = new Memcache();
$this->db->connect("localhost");
}
public static function _instance() {
self::$_instance === null and self::$_instance = new Token();
return self::$_instance;
}
public function initialise($id) {
$this->id = $id;
}
public function authenticate(array $source = null, $key = "token") {
$source = $source !== null ? $source : $_POST;
if (empty($this->id)) {
throw new TokenException("Token not Initialised");
}
if (! empty($source)) {
if (! isset($source[$key]))
throw new TokenException("Missing Token");
if (! $this->get($this->id . $source[$key])) {
throw new TokenException("Invalid Token");
}
}
}
public function get($key) {
return $this->db->get($key);
}
public function remove($key) {
return $this->db->delete($key);
}
public function generate($time = 120) {
$key = hash("sha512", mt_rand(0, mt_getrandmax()));
$this->db->set($this->id . $key, 1, 0, $time);
return $key;
}
}
class TokenException extends InvalidArgumentException {
}
Примечание. Обратите внимание, что пример может повлиять на кнопку "Назад" или обновить, поскольку токен будет автоматически удален после
120
секунд, и это может повлиять на удобство использования
Мне интересно, безопасный ли способ установить токен
Это зависит от того, насколько безопасно ваше веб-приложение. Эта строка не криптографически защищена (как указано в документах PHP для uniqid() и rand()):
uniqid(rand(), true);
Для злоумышленника может быть возможно определить/перебор силы, если время генерации токена известно/определено, а семя rand() известно/определено. Однако для ваших целей это может быть хорошо, поскольку оно все равно предотвратит атаки CSRF, если злоумышленник не знает значения маркера.
Один токен за сеанс?
Использование одного токена за сеанс может быть прекрасным для ваших целей. Однако имейте в виду:
Будет ли необходимость вычищать токен в поданной форме? или просто остаться с ним, хотя я представил форму?
Это зависит от того, насколько высокая ценность предназначена для вашего приложения для злоумышленников, и уровень сбоя, который может вызвать атака. Ваша существующая мера затрудняет выполнение атак CSRF, но если она имеет большую ценность и у вас есть очень решительные злоумышленники, тогда вы можете уменьшить риск CSRF больше:
Вы можете обратиться к следующему сайту, это может привести к некоторым идеям.
1.) https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
2.) http://blog.whitehatsec.com/tag/session-token/
Спасибо за ответ.
Я уже ответил на аналогичный вопрос на другом форуме: здесь. Надеюсь, это полезно. В нем объясняется основной процесс предотвращения CSRF и ссылки на некоторый код для структуры CSRF.
Если вам нужна более высокая безопасность, измените токен после каждого запроса для каждого сеанса. Если вы хотите улучшить удобство использования, сохраните один токен за сеанс.