如何创建和使用随机数

发布于 2024-10-01 05:10:26 字数 301 浏览 0 评论 0原文

我正在运营一个网站,有一个评分系统,可以根据您玩游戏的次数给您积分。

它使用哈希来证明http请求的完整性进行评分,因此用户无法更改任何内容。 然而,正如我担心的那样,有人发现他们不需要改变它;相反,他们只需要获得高分,然后复制 http 请求、标头以及所有其他内容。

以前我被禁止防范这种攻击,因为它被认为不太可能发生。然而,既然事情已经发生了,我可能会这么做。 http请求源自一个flash游戏,然后经过php验证,php将其输入到数据库中。

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

I am running a website, and there is a scoring system that gives you points for the number of times you play a game.

It uses hashing to prove the integrity of http request for scoring, so users cannot change anything.
However, as I feared might happen, someone figured out that they didn't need to change it; instead they just needed to get a high score, and then duplicate the http request, headers, and all the rest.

Previously I'd been prohibited from protecting against this attack because it was considered unlikely. However, now that it has happened, I may. The http request originates from a flash game, and then is validated by php, and php enters it into the database.

I'm pretty sure nonces will solve the issue, but I'm not exactly sure how to implement them. What is a common, and secure way of setting up a nonce system?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

此岸叶落 2024-10-08 05:10:26

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

  1. PHP Nonce Library< /a>
  2. OpenID Nonce 库

或者如果你想编写自己的,它也很漂亮简单的。使用 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;
}

It's actually quite easy to do... There are some libraries out there to do it for you:

  1. PHP Nonce Library
  2. OpenID Nonce Library

Or if you want to write your own, it's pretty simple. Using the WikiPedia page as a jumping off point, In pseudo-code:

On the server side, you need two client callable functions

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

And on the client side:

sendData($data) {
    $nonce = getNonceFromServer();
    $cnonce = hash('sha512', makeRandomString());
    $hash = hash('sha512', $nonce . $cnonce . $data);
    $args = array('data' => $data, 'cnonce' => $cnonce, 'hash' => $hash);
    sendDataToClient($args);
}

The function makeRandomString really just needs to return a random number or string. The better the randomness, the better the security... Also note that since it's fed right into a hash function, the implementation details don't matter from request to request. The client's version and the server's version don't need to match. In fact, the only bit that needs to match 100% is the hash function used in hash('sha512', $nonce . $cnonce . $data);... Here's an example of a reasonably secure makeRandomString function...

function makeRandomString($bits = 256) {
    $bytes = ceil($bits / 8);
    $return = '';
    for ($i = 0; $i < $bytes; $i++) {
        $return .= chr(mt_rand(0, 255));
    }
    return $return;
}
像极了他 2024-10-08 05:10:26

随机数是一罐蠕虫。

不,确实,几个 CAESAR 条目的动机之一是设计一种经过身份验证的加密方案,最好基于流密码,可抵抗随机数重用。 (例如,在 AES-CTR 中重复使用随机数会破坏消息的机密性,达到一年级编程学生可以解密的程度。)

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

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

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

  1. 上述哪些思想流派与您要解决的问题最相关?
  2. 您如何生成随机数?
  3. 您如何验证随机数?

生成随机数

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

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

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

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

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

验证 Nonce

有状态

有状态的 nonce 很简单,建议使用:

$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 Tokens 的不符合标准的变体。

Nonces are a can of worms.

No, really, one of the motivations for several CAESAR entries was to design an authenticated encryption scheme, preferably based on a stream cipher, that is resistant to nonce reuse. (Reusing a nonce with AES-CTR, for example, destroys the confidentiality of your message to the degree a first year programming student could decrypt it.)

There are three main schools of thought with nonces:

  1. In symmetric-key cryptography: Use an increasing counter, while taking care to never reuse it. (This also means using a separate counter for the sender and receiver.) This requires stateful programming (i.e. storing the nonce somewhere so each request doesn't start at 1).
  2. Stateful random nonces. Generating a random nonce and then remembering it to validate later. This is the strategy used to defeat CSRF attacks, which sounds closer to what is being asked for here.
  3. Large stateless random nonces. Given a secure random number generator, you can almost guarantee to never repeat a nonce twice in your lifetime. This is the strategy used by NaCl for encryption.

So with that in mind, the main questions to ask are:

  1. Which of the above schools of thought are most relevant to the problem you are trying to solve?
  2. How are you generating the nonce?
  3. How are you validating the nonce?

Generating a Nonce

The answer to question 2 for any random nonce is to use a CSPRNG. For PHP projects, this means one of:

  • random_bytes() for PHP 7+ projects
  • paragonie/random_compat, a PHP 5 polyfill for random_bytes()
  • ircmaxell/RandomLib, which is a swiss army knife of randomness utilities that most projects that deal with randomness (e.g. fir password resets) should consider using instead of rolling their own

These two are morally equivalent:

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

and

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

Validating a Nonce

Stateful

Stateful nonces are easy and recommended:

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

Feel free to substitute the array_search() with a database or memcached lookup, etc.

Stateless (here be dragons)

This is a hard problem to solve: You need some way to prevent replay attacks, but your server has total amnesia after each HTTP request.

The only sane solution would be to authenticate an expiration date/time to minimize the usefulness of replay attacks. For example:

// 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)

A careful observer will note that this is basically a non-standards-compliant variant of JSON Web Tokens.

鸵鸟症 2024-10-08 05:10:26

一种选择(我在评论中提到)是记录游戏玩法并在安全环境中重播。

另一件事是随机地,或者在某些特定的时间,记录一些看似无辜的数据,这些数据稍后可以用来在服务器上验证它(比如突然直播从 1% 到 100%,或者分数从 1 到 1000 表明作弊) )。有了足够的数据,作弊者就不可能试图伪造它。然后当然要实施严厉的禁令:)。

One option (which I mentioned in comment) is recording gameplay and replay it in secure environment.

The other thing is to randomly, or at some specified times, record some seemingly innocent data, which later can be used to validate it on server (like suddenly live goes from 1% to 100%, or score from 1 to 1000 which indicate cheat). With enough data it might just not be feasible for cheater to try to fake it. And then of course implement heavy banning :).

皓月长歌 2024-10-08 05:10:26

这个非常简单的随机数每 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,可以是进程中使用的任何函数调用或用户名等。

This very simple nonce changes every 1000 seconds (16 minutes)
and can be used for avoiding XSS where you are posting data to and from the same application. (For example if you are in a single page application where you are posting data via javascript. Note that you must have access to the same seed and nonce generator from the post and the receiving side)

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

But by checking for the previous nonce, the user will only be bothered if he waited more than 16.6 minutes in worst case and 33 minutes in best case. Setting $q=-4 will give the user at least 2.7 hours

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

The $seed, could be the any function call or user name, etc. used in the process.

旧城烟雨 2024-10-08 05:10:26

不可能防止作弊。你只能让事情变得更加困难。

如果有人来这里寻找 PHP Nonce 库:我建议不要使用 ircmaxwell 给出的第一个

该网站的第一条评论描述了一个设计缺陷:

随机数适用于某个特定的时间窗口,即越接近
用户到达该窗口末尾的时间越少
提交表单,可能不到一秒

如果您正在寻找一种生成具有明确定义的生命周期的 Nonce 的方法,请查看 NonceUtil-PHP

免责声明:我是 NonceUtil-PHP 的作者

It is not possible to prevent cheating. You can only make it more difficult.

If someone came here looking for a PHP Nonce Library: I recommend not using the first one given by ircmaxwell.

The first comment on the website describes a design flaw:

The nonce is good for one certain time window, i.e. the nearer the
user gets to the end of that windows the less time he or she has to
submit the form, possibly less than one second

If you are looking for a way to generate Nonces with a well-defined lifetime, have a look at NonceUtil-PHP.

Disclaimer: I am the author of NonceUtil-PHP

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文