PHP CSRF Токен-код.Достаточная безопасность? - PullRequest
1 голос
/ 23 октября 2011

Недавно защитив свой сайт как можно лучше от XSS, я сейчас нахожусь в процессе защиты от CSRF, просмотрев и прочитав несколько статей по этому вопросу, я создал приведенный ниже код.

Я хотел бы знать, правильна ли моя реализация при использовании строки в базе данных для обеспечения безопасности. Что-то, что я должен делать по-другому? Должен ли я проверять базу данных с обеих сторон ???

Код:

if(!isset($_SESSION['register_token'])){

  $keytype = 'register';

  $getregisterkey = mysql_query("SELECT key FROM tokenkeys WHERE type='".$keytype."' ") or die(mysql_error()); 
  while ($row = mysql_fetch_array($getregisterkey))
  {
$registerkey = mysql_real_escape_string($row['key']);
  }; 


  $_SESSION['register_token']=sha1(uniqid(rand(), TRUE).$registerkey);
  $_SESSION['register_token_time']=time();
}

<input type="hidden" name="token" value="<?php echo $_SESSION['register_token'];?>" />



if($_POST['register_token']==$_SESSION['register_token']){
  $register_token_age=time()-$_SESSION['register_token_time'];
  if($register_token_age=>300){

    //process form
  } else{
    //valid token but expired
  }
} else{
  die('Access Forbidden')

}

1 Ответ

1 голос
/ 23 октября 2011

Токены XSRF безопасны только как канал, по которому отправляется страница. Используйте https и только https в этой форме и отправляйте ее только на конечную точку https. В противном случае MITM может получить ваш токен XSRF из формы в том виде, в котором он был обслужен или отправлен.

while ($row = mysql_fetch_array($getregisterkey))
{
    $registerkey = mysql_real_escape_string($row['key']);
}; 

никогда не будет выполняться при нулевых строках, и в этом случае вы не получаете большой энтропии от $getregisterkey, когда позже делаете

$_SESSION['register_token']=sha1(uniqid(rand(), TRUE).$registerkey);

поэтому я бы позаботился о том, чтобы ваша реализация работала быстро, если возвращено ноль строк. Возможно, измените значение на if ($row = mysql_fetch_array(...)) { ... } else { /* abort */ }, поскольку дополнительные строки не приносят пользы.

rand() должен быть либо действительно случайным, либо криптографически стойким PRNG.

Я не знаком со стандартными библиотеками PHP, но [wikipedia] предполагает, что rand() не является криптографически сильным. Википедия говорит

Есть предложения по добавлению генерации случайных чисел в PHP.

Сильная криптография в PHP предлагает использовать openssl_random_pseudo_bytes()

Не используйте rand () или mt_rand ()

Чтобы сгенерировать криптографически сильное случайное число в PHP, вы должны использовать функцию openssl_random_pseudo_bytes() библиотеки OpenSSL.

Если вы используете слабую случайность, то злоумышленник может наблюдать за сгенерированными вами числами (запрашивая несколько версий формы и анализируя скрытый ввод) и использовать это, чтобы выяснить, какими могут быть следующие числа, и подделать токены CSRF.

Если злоумышленник изменит свойство сеанса 'register_token_time', он сможет избежать проверок XSRF.

Например, если у вас есть страница, которая делает

$_SESSION[$_POST['x']] = $_POST['y'];

тогда злоумышленник может POST

x=register_token&y=pwnd

, чтобы заменить register_token, хранящийся в сеансе, а затем отправить сообщение с

token=pwnd

и обойдите защиту XSRF.

...