0

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

现在,我想将主键 [1, 2, 3, ..] 散列到另一个称为订单号的数字中,以便我们的客户可以在他们的请求中引用它们,例如

1 -> 100192938303
2 -> 293029200002

我想要以下内容:

  • 无法通过查看自动增量 ID 来猜测我每天创建了多少订单
  • 不需要额外的数据库查找,完全由 PHP 哈希(和预定义的盐)
  • 无碰撞

可能吗?

4

4 回答 4

3

我认为您可能可以选择更简单的方法 - 不要使用自动递增 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
于 2014-11-22T16:31:35.997 回答
0

这里有两个想法:

  1. 使用可逆哈希。这是否有效取决于您认为的安全性,因为它本质上只是混淆。但是如果你定制它(也许改变算法中某些步骤的顺序),并且你防止源被泄露,它将阻止除了最坚定的攻击者之外的所有攻击者。(根据您的安全目标,您可能希望结合其他一些技术来降低泄漏风险,例如,员工离开公司。考虑将部分算法保密,就好像它是加密密钥一样,并且对输入进行额外的、可变的、预转换。)

在我的脑海中,一个简单的可逆哈希可能只是位的“横向添加”。对于更复杂的东西,我想不到,流行的“MurmurHash”系列算法据称是可逆的。

我不知道有任何密码学上强大的可逆哈希。但是,关于对称加密主题的其他答案与此想法相似。

  1. 使用流密码,AKA 加密 RNG。如果订单总数将相当小,这是合适的。您需要的是一个唯一的数字序列,它与计数序列一一对应。因此,使用您选择的 RC4 或 HMAC 生成一系列唯一随机数,并在执行过程中消除重复项。(也许让这个过程快速进行的一种创造性方法是布隆过滤器。)

对于从内部 ID 到外部 ID 的映射,您只需生成序列。反过来,您继续前进,直到找到 ID,或达到最大订单 ID。这个算法是 O(n),这显然不是理想的,但如果你愿意妥协一点,增加更多的复杂性,或者聪明一点,你可能会找到一种方法来缓解这种情况。例如,您可以在 RAM 中保留 ID 的缓存。

编辑:

由于线性复杂性,我自己对 #2 持怀疑态度,所以我运行了一些数字。使用来自 Core2 处理器的 Crypto++ 基准数字,如果您将 10 毫秒的预算用于数字转换,并且您使用 40 位 ID(假设您获得了 1 万亿次订单),您将获得最大约 2,500,000 的订单 ID。而且我认为您可以使用较小的密钥将其翻倍。

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

于 2015-01-15T06:38:59.960 回答
0

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

  • 遍历合理的订单值,获取每个值的加盐哈希,直到您识别匹配的哈希或耗尽允许的订单 ID 池。
  • 将合理的订单值缓存一次(例如,在应用程序启动时),并存储在哈希表中。一旦创建了缓存,这种方法会快得多,但需要额外的查找。

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

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

  1. 使用您的密钥加密订单 ID。
  2. Base64 对订单 ID 进行编码并作为令牌返回给客户端。
  3. 从客户端收到加密令牌后,解码 Base64 字符串
  4. 使用您的密钥解密解码的字符串以生成原始订单号。

例如,对于订单 42 和 DES 密钥E0EC4E44EF2C6CEE以及零 IV,您将dmTt0kbIlcA=作为订单 ID 发送给客户端(如果您将 42 编码为 little-endian 32 位整数)。(此处使用零 IV 是合适的,因为在您的场景中不需要考虑唯一的密文。)

于 2014-11-22T15:36:17.503 回答
-2

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

您甚至可以添加盐,例如 base64_encode($id."salt")

于 2014-11-22T14:12:31.430 回答