订单号的安全整数散列

时间:2014-11-22 13:33:41

标签: php algorithm security hash consistent-hashing

假设我有一个表Orders,其中包含自动递增的ID,例如1,2,3,4 ......,它们当前被查询为http://www.example.com/order?id= {1,2,3 ..}

现在,我想将主键[1,2,3,...]哈希到另一个名为Order Number的号码中,以便我们的客户可以在他们的请求中引用它们,例如

1 -> 100192938303
2 -> 293029200002

我想要以下内容:

  • 无法通过查看自动增量ID
  • 来猜测我每天创建的订单数量
  • 没有DB额外查找,纯粹由PHP(和预定义的盐)哈希
  • 无碰撞

有可能吗?

4 个答案:

答案 0 :(得分:2)

我认为您可以选择更简单的方法 - 不使用自动递增ID,使用随机整数作为ID。例如:

while (true) {
    $id = get_random_integer();
    $stmt = $db->prepare("INSERT INTO Orders (id, foo, bar) VALUES (:id, 'foo', 'bar')");
    try {
        $stmt->execute(array(':id' => $id));
        //OK
        break;
    } catch (Exception $ex) {
        if (is_duplicate_id_exception) {
            //generate new id and try again
            continue;
        }
        //Some other problem
        throw $ex;
    }
}

这样你:

  • 避免碰撞
  • 不需要散列函数和{hash - > ID}映射
  • 包含不包含订单金额信息的ID

答案 1 :(得分:0)

您提议使用盐渍哈希。由于散列是单向函数,并且您需要将散列转换为原始值,因此您需要以下其中一项来将散列转换为原始顺序值:

  • 循环使用合理的订单值,获取每个订单的咸味哈希值,直到您确定匹配的哈希值或耗尽允许的订单ID池。
  • 缓存可合理的订单值一次(例如,在应用启动时),并存储在哈希表中。创建缓存后,此方法会快得多,但需要额外查找。

您还注意到原始订单标识符是保密的,因为可以获取多个订单ID的攻击者可以推断订单量。订单标识符的机密性与订单本身的机密性是一个单独的问题,该问题没有解决,可以通过单独的访问控制机制来处理。

我认为您示例中的首选方法是使用加密而不是哈希。加密订单ID将满足机密性和往返要求,而不会产生哈希或数据库查找缓存的开销。方法可能如下所示:

  1. 使用密钥加密订单ID。
  2. Base64编码订单ID并作为令牌返回客户端。
  3. 从客户端收到加密令牌后,解码Base64字符串
  4. 使用您的密钥解密已解码的字符串以生成原始订单号。
  5. 例如,对于订单42和DES密钥E0EC4E44EF2C6CEE和零IV,您将dmTt0kbIlcA=作为订单ID发送到客户端(如果您将42编码为小端32位整数)。 (零IV适用于此,因为在您的方案中不需要考虑唯一的密文。)

答案 2 :(得分:0)

以下是两个想法:

  1. 使用可逆散列。这是否有效取决于您认为的安全性,因为它基本上只是混淆。但是如果你自定义它(可能改变了算法中某些步骤的顺序),并且你防止了源被泄露,它将阻止所有但最坚决的攻击者。 (根据您的安全目标,您可能希望结合其他一些技术来降低泄漏风险,例如员工离开公司。考虑保留部分算法的秘密,就好像它是加密密钥一样,并为输入提供额外的,可变的预转换。)
  2. 在我的头顶,一个简单的可逆哈希可能只是"横向添加"比特。对于更复杂的东西,在我的头顶,流行的" MurmurHash"据称,算法族是可逆的。

    我不知道任何加密强烈的可逆哈希值。但是,关于对称加密主题的其他答案与此想法类似。

    1. 使用流密码,AKA加密RNG。如果订单总数相当小,这是合适的。您需要的是一个独特的数字序列,它与计数数字序列一一对应。因此,使用您选择的RC4或HMAC生成一系列独特的随机数,随时消除重复。 (也许一种创造性的方法可以让它快速过滤。)
    2. 对于从内部ID到外部ID的映射,您只需生成序列。反过来说,你一直走到找到ID,或者点击最大订单ID。这个算法是O(n),这显然不是理想的,但如果你愿意稍微妥协,增加更多的复杂性,或者更聪明,你可能会找到一种方法来缓解这个问题。例如,您可以在RAM中保留ID的缓存。

      编辑:

      由于线性复杂性,我自己对#2持怀疑态度,因此我运行了一些数字。使用来自Core2处理器的Crypto ++基准测试数字,如果你预算10毫秒到数字转换,并且你使用40位ID(假设你得到一个千万亿的订单),你会得到一个订单ID最多约2,500,000。而且我认为你可以通过使用更小的密钥来加倍。

      所以这种方法可以采用任何一种方式。对于小规模的东西,它很好。 (上面的假设是保守的。)但对于大型的东西,这可能是一个烦恼。它足以让您完成产品发布;在您开始讨论如何将软件构建为分布式系统时,您想要重新访问它,这也有助于解决此问题。但在那时,你可能最好不要质疑初始假设,只是将这个东西存放在某个数据库中。

答案 3 :(得分:-2)

您可以使用base64_encode()编码订单ID,然后在GET表单中提交,然后在捕获表单发送的变量时使用base64_decode()

你甚至可以添加盐,例如base64_encode($ id。“salt”)