如何创建和使用随机数

2022-08-30 08:18:36

我正在运行一个网站,并且有一个评分系统,可以为您提供玩游戏次数的分数。

它使用散列来证明http请求的完整性,以便用户无法更改任何内容,但是正如我担心可能发生的那样,有人发现他们不需要更改它,他们只需要获得高分,并复制http请求,标头和所有内容。

以前,我被禁止防范这种攻击,因为它被认为不太可能。但是,既然它已经发生了,我可以。http请求起源于Flash游戏,然后由php验证,php将其输入数据库。

我很确定nonces会解决这个问题,但我并不完全确定如何实现它们。设置随机数系统的常见且安全的方法是什么?


答案 1

这实际上很容易做到...有一些库可以为您做到这一点:

  1. PHP 随机函数库
  2. OpenID Nonce Library

或者如果你想写你自己的,这很简单。使用维基百科页面作为起点,在伪代码中:

在服务器端,您需要两个客户端可调用函数

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);
}

该函数实际上只需要返回一个随机数或字符串。随机性越好,安全性越好...另请注意,由于它被直接馈送到哈希函数中,因此实现细节在请求之间无关紧要。客户端的版本和服务器的版本不需要匹配。实际上,唯一需要100%匹配的位是...下面是一个相当安全的函数的示例...makeRandomStringhash('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条目的动机之一是设计一个经过身份验证的加密方案,最好是基于流密码,该方案可以抵抗随机重复使用。(例如,使用AES-CTR重用nonce会破坏消息的机密性,以至于一年级编程学生可以解密它。

有三种主要的随机数思想流派:

  1. 在对称密钥加密中:使用递增计数器,同时注意切勿重用它。(这也意味着为发送方和接收方使用单独的计数器。这需要有状态编程(即将nonce存储在某个地方,以便每个请求不会从 开始)。1
  2. 有状态随机随机数。生成一个随机随机数,然后记住它以便稍后验证。这是用于击败CSRF攻击的策略,这听起来更接近于这里所要求的。
  3. 大型无状态随机随机数。给定一个安全的随机数生成器,你几乎可以保证在你的一生中永远不会重复一次随机数两次。这是NaCl用于加密的策略。

因此,考虑到这一点,要问的主要问题是:

  1. 上述思想流派中哪一个与你试图解决的问题最相关?
  2. 你是如何生成随机数的?
  3. 你如何验证随机数?

生成随机数

对于任何随机随机数,问题 2 的答案都是使用 CSPRNG。对于 PHP 项目,这意味着:

  • random_bytes() 用于 PHP 7+ 项目
  • paragonie/random_compat, 一个 PHP 5 polyfill forrandom_bytes()
  • ircmaxell/RandomLib,这是一把瑞士军刀,具有随机性实用程序,大多数处理随机性的项目(例如fir密码重置)应该考虑使用而不是滚动自己的实用程序

这两者在道德上是等同的:

$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);

$_SESSION['nonce'] []= random_bytes(32);

验证随机数

状态

有状态随机数很容易,建议使用:

$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]);

随意用数据库或memcached查找等替换。array_search()

无状态(这里是龙)

这是一个很难解决的问题:您需要一些方法来防止重放攻击,但是您的服务器在每次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令牌的非标准兼容变体。


推荐