如何创建和使用nonce

时间:2010-11-10 14:33:28

标签: php actionscript-3 cryptography nonce

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

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

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

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

5 个答案:

答案 0 :(得分:59)

实际上很容易做到......有一些图书馆可以为你做这些:

  1. PHP Nonce Library
  2. OpenID Nonce Library
  3. 或者如果你想自己编写,那很简​​单。使用WikiPedia page作为跳出点,在伪代码中:

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

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

答案 1 :(得分:19)

Nonces是一群蠕虫。

不,实际上,几个CAESAR条目的动机之一是设计一个经过验证的加密方案,最好是基于流密码,它可以抵抗随机数重用。 (例如,重复使用带有AES-CTR的随机数,会破坏您的信息的机密性,使第一年编程学生可以解密它。)

有三种主要的思想流派:

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

    1. 上述哪种思想与你想解决的问题最为相关?
    2. 你是如何产生现时的?
    3. 你如何验证nonce?
    4. 生成随机数

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

      这两个在道德上是等价的:

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

      随意用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的非标准兼容变体。

答案 2 :(得分:1)

一个选项(我在评论中提到)是录制游戏并在安全的环境中重播。

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

答案 3 :(得分:0)

这种非常简单的随机数每1000秒(16分钟)更改一次 并且可以用于避免在同一应用程序之间来回发布数据的XSS。 (即您可以使用相同的种子和随机数生成器)

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小时

if($nonce==$this->makeNonce($seed,0)||$nonce==$this->makeNonce($seed,1)){
     //handle data here
 } else {
     //reject nonce code   
 }

答案 4 :(得分:-1)

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

如果有人来这里寻找PHP Nonce库:我建议不要使用the first one given by ircmaxwell

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

  

nonce对于某个特定时间窗口是有益的,即越接近   用户到达那个窗口的末尾他或她必须的时间越少   提交表格,可能不到一秒

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

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