Как создавать и использовать nonces
Я запускаю веб-сайт, и есть система подсчета очков, которая дает вам очки за количество раз, когда вы играете в игру.
Он использует хэширование, чтобы доказать целостность HTTP-запроса для подсчета очков, чтобы пользователи ничего не могли изменить, однако, как я боялся, может случиться, кто-то понял, что им не нужно его менять, им просто нужно было получить высокий балл, и дублировать http-запрос, заголовки и все.
Раньше мне было запрещено защищать от этой атаки, потому что это считалось маловероятным. Однако теперь, когда это произошло, я могу. HTTP-запрос берется из флеш-игры, а затем проверяется php и php вносит его в базу данных.
Я уверен, что nonces решит проблему, но я не совсем уверен, как ее реализовать. Что такое обычный и безопасный способ настройки системы nonce?
Ответы
Ответ 1
Это на самом деле довольно легко сделать... Есть несколько библиотек, чтобы сделать это для вас:
- PHP Nonce Library
- OpenID Nonce Library
Или, если вы хотите написать свой собственный, это довольно просто. Использование страницы WikiPedia в качестве отправной точки, в псевдокоде:
На стороне сервера вам нужны две вызываемые клиентом функции
getNonce() {
$id = Identify Request //(either by username, session, or something)
$nonce = hash('sha512', makeRandomString());
storeNonce($id, $nonce);
return $nonce to client;
}
verifyNonce($data, $cnonce, $hash) {
$id = Identify Request
$nonce = getNonce($id); // Fetch the nonce from the last request
removeNonce($id, $nonce); //Remove the nonce from being used again!
$testHash = hash('sha512',$nonce . $cnonce . $data);
return $testHash == $hash;
}
И на стороне клиента:
sendData($data) {
$nonce = getNonceFromServer();
$cnonce = hash('sha512', makeRandomString());
$hash = hash('sha512', $nonce . $cnonce . $data);
$args = array('data' => $data, 'cnonce' => $cnonce, 'hash' => $hash);
sendDataToClient($args);
}
Функция makeRandomString
действительно просто должна возвращать случайное число или строку. Чем лучше случайность, тем лучше защита... Также обратите внимание, что, поскольку она передается прямо в хэш-функцию, детали реализации не имеют значения от запроса к запросу. Версия клиента и версия сервера не должны совпадать. Фактически, единственный бит, который должен соответствовать 100%, - это хеш-функция, используемая в hash('sha512', $nonce. $cnonce. $data);
... Вот пример довольно безопасной функции makeRandomString
...
function makeRandomString($bits = 256) {
$bytes = ceil($bits / 8);
$return = '';
for ($i = 0; $i < $bytes; $i++) {
$return .= chr(mt_rand(0, 255));
}
return $return;
}
Ответ 2
Nonces - это банда червей.
Нет, действительно, одной из мотиваций для нескольких CAESAR было создание схемы аутентифицированного шифрования, предпочтительно основанной на поточном шифре, которая устойчива к nonce повторное использование. (Повторное использование nonce с AES-CTR, например, уничтожает конфиденциальность вашего сообщения до степени, когда первый год студент может расшифровать его.)
Существуют три основные школы мысли с несами:
- В криптографии с симметричным ключом: используйте увеличивающий счетчик, не обращая внимания на его повторное использование. (Это также означает использование отдельного счетчика для отправителя и получателя.) Для этого требуется программирование с сохранением состояния (т.е. Где-то сохранено значение nonce, поэтому каждый запрос не начинается с
1
).
- Состояния случайных случайных. Создание случайного nonce, а затем запоминание его для подтверждения позже. Это стратегия, используемая для поражения атак CSRF,, которая звучит ближе к тому, что предлагается здесь.
- Большие случайные символы без атак. Учитывая надежный генератор случайных чисел, вы можете почти гарантировать, что никогда не повторяйте nonce дважды в своей жизни. Это стратегия, используемая NaCl для шифрования.
Итак, имея в виду, основные вопросы, которые нужно задать, следующие:
- Какая из вышеупомянутых школ мысли наиболее важна для проблемы, которую вы пытаетесь решить?
- Как вы генерируете nonce?
- Как вы проверяете nonce?
Создание Nonce
Ответ на вопрос 2 для любого случайного nonce заключается в использовании CSPRNG. Для проектов PHP это означает одно из:
-
random_bytes()
для проектов с PHP 7+
- paragonie/random_compat, PHP 5 polyfill для
random_bytes()
- ircmaxell/RandomLib, который является швейцарским армейским ножом утилит случайности, который в большинстве проектов, которые имеют дело с случайностью (например, сбрасывание пароля), должен рассмотреть возможность использования вместо того, чтобы собственный
Эти два являются морально эквивалентными:
$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);
и
$_SESSION['nonce'] []= random_bytes(32);
Проверка несовместимости
Stateful
Простые и рекомендуемые высказывания:
$found = array_search($nonce, $_SESSION['nonces']);
if (!$found) {
throw new Exception("Nonce not found! Handle this or the app crashes");
}
// Yay, now delete it.
unset($_SESSION['nonce'][$found]);
Не забудьте заменить array_search()
на поиск базы данных или memcached и т.д.
Без гражданства (здесь будут драконы)
Это сложная проблема: вам нужно каким-то образом предотвратить повторные атаки, но ваш сервер имеет общую амнезию после каждого HTTP-запроса.
Единственным разумным решением будет аутентификация даты/времени истечения срока действия, чтобы свести к минимуму эффективность повторных атак. Например:
// Generating a message bearing a nonce
$nonce = random_bytes(32);
$expires = new DateTime('now')
->add(new DateInterval('PT01H'));
$message = json_encode([
'nonce' => base64_encode($nonce),
'expires' => $expires->format('Y-m-d\TH:i:s')
]);
$publishThis = base64_encode(
hash_hmac('sha256', $message, $authenticationKey, true) . $message
);
// Validating a message and retrieving the nonce
$decoded = base64_decode($input);
if ($decoded === false) {
throw new Exception("Encoding error");
}
$mac = mb_substr($decoded, 0, 32, '8bit'); // stored
$message = mb_substr($decoded, 32, null, '8bit');
$calc = hash_hmac('sha256', $message, $authenticationKey, true); // calcuated
if (!hash_equals($calc, $mac)) {
throw new Exception("Invalid MAC");
}
$message = json_decode($message);
$currTime = new DateTime('NOW');
$expireTime = new DateTime($message->expires);
if ($currTime > $expireTime) {
throw new Exception("Expired token");
}
$nonce = $message->nonce; // Valid (for one hour)
Тщательный наблюдатель заметит, что это в основном нестандартный вариант JSON Web Tokens.
Ответ 3
Один из вариантов (который я упомянул в комментарии) - это запись игрового процесса и воспроизведение его в защищенной среде.
Другое дело - случайным образом или в определенное время записывать некоторые, казалось бы, невинные данные, которые позже могут быть использованы для проверки его на сервере (например, внезапно живое идет от 1% до 100% или оценивается от 1 до 1000 которые указывают на чит). При наличии достаточного количества данных мошеннику может быть нецелесообразно пытаться подделать его. И тогда, конечно, реализуйте тяжелое запрещение:).
Ответ 4
Этот очень простой одноразовый номер меняется каждые 1000 секунд (16 минут) и может использоваться для предотвращения XSS, когда вы публикуете данные в одном приложении и из него. (Например, если вы находитесь в одностраничном приложении, в котором вы публикуете данные с помощью javascript. Обратите внимание, что вы должны иметь доступ к одному и тому же генератору семени и одноразовых номеров с поста и получателя)
function makeNonce($seed,$i=0){
$timestamp = time();
$q=-3;
//The epoch time stamp is truncated by $q chars,
//making the algorthim to change evry 1000 seconds
//using q=-4; will give 10000 seconds= 2 hours 46 minutes usable time
$TimeReduced=substr($timestamp,0,$q)-$i;
//the $seed is a constant string added to the string before hashing.
$string=$seed.$TimeReduced;
$hash=hash('sha1', $string, false);
return $hash;
}
Но проверяя предыдущий одноразовый номер, пользователь будет обеспокоен, только если он подождет более 16,6 минут в худшем случае и 33 минуты в лучшем случае. Установка $ q = -4 даст пользователю минимум 2,7 часа
function checkNonce($nonce,$seed){
//Note that the previous nonce is also checked giving between
// useful interval $t: 1*$qInterval < $t < 2* $qInterval where qInterval is the time deterimined by $q:
//$q=-2: 100 seconds, $q=-3 1000 seconds, $q=-4 10000 seconds, etc.
if($nonce==$this->makeNonce($seed,0)||$nonce==$this->makeNonce($seed,1)) {
//handle data here
return true;
} else {
//reject nonce code
return false;
}
}
$ Seed может быть любым вызовом функции или именем пользователя и т.д., Используемыми в процессе.
Ответ 5
Невозможно предотвратить обман. Вы можете только сделать это более сложным.
Если кто-то пришел сюда в поисках библиотеки Nonce PHP: я не рекомендую использовать первую, предоставленную ircmaxwell.
Первый комментарий на сайте описывает недостаток дизайна:
Одноразовый номер подходит для одного определенного временного окна, т.е. Чем ближе пользователь подходит к концу этих окон, тем меньше времени ему или ей нужно отправить форму, возможно, менее одной секунды
Если вы ищете способ генерировать одноразовые номера с четко определенным временем жизни, взгляните на NonceUtil-PHP.
Отказ от ответственности: я являюсь автором NonceUtil-PHP