为了账号安全,请及时绑定邮箱和手机立即绑定

为类似凭证的行为制作最佳的伪随机唯一代码生成器

为类似凭证的行为制作最佳的伪随机唯一代码生成器

PHP
小怪兽爱吃肉 2022-01-24 10:48:38
我正在为 IT 技术服务开发一个简单的管理系统。该系统有一个网站,客户可以通过输入他们的唯一代码来检查他们的订单的当前状态。当然,这段代码必须是唯一的,并且希望不容易(手动)暴力破解。我正在考虑在“XKF-042”行的某处输出,易于阅读和写下。问题出现在这些值的生成上:我可以使用纯随机数据并生成两段代码,但这迫使我检查代码是否已经存在,这感觉就像是资源的指数浪费。一个简单的答案是从任意数字开始计数,比如“ABC-001”,然后加 1,因此不需要检查该值是否已经存在。问题在于暴力破解的容易程度。任何人都可以检查 ABC-XXX 并检查最后一千个连续订单。数学不是我的强项,但我知道必须有一个更优雅的解决方案来解决这个问题。我正在考虑为代码的每一侧生成每一个可能的排列并将它们打乱,所以我有一个看似随机的配对列表可供读取,并且可能每 1000 个代码移动一次“代码右侧”列表。编辑:无法猜测代码并不重要;除了订单信息和成本外,输出不会有任何个人信息。我可以使用 4x4 代码(如“SJDM-4823”)使其“更强大”。
查看完整描述

2 回答

?
莫回无

TA贡献1865条经验 获得超7个赞

好吧,我建议使用 LCG,Linear Congruential Generator。它有非常好的属性——给定正确的常量集(赫尔-多贝尔定理)输出唯一地覆盖所有 2 32空间(或 64 位空间,但据我记得 PHP 只有 32 位整数)。基本上它是从 [0...2 32 ) 间隔到另一个 [0...2 32 ) 间隔的一对一映射器。它可以以两种方式使用


一、ID next = LCG(ID prev ),就像典型的随机数生成器一样。或者只是从线性递增计数器 ID next = LCG(1, 2, ...) 中输入它。您可以将整数转换为 8 个符号 base-16 字符串,应该足够好了。


没有 PHP 代码,有一个 python 代码


import numpy as np


class LCG:


    UZERO: np.uint32 = np.uint32(0)

    UONE : np.uint32 = np.uint32(1)


    def __init__(self, seed: np.uint32, a: np.uint32, c: np.uint32) -> None:

        self._seed: np.uint32 = np.uint32(seed)

        self._a   : np.uint32 = np.uint32(a)

        self._c   : np.uint32 = np.uint32(c)


    def next(self) -> np.uint32:

        self._seed = self._a * self._seed + self._c

        return self._seed


    def seed(self) -> np.uint32:

        return self._seed


    def set_seed(self, seed: np.uint32) -> np.uint32:

        self._seed = seed


    def skip(self, ns: np.int32) -> None:

        """

        Signed argument - skip forward as well as backward


        The algorithm here to determine the parameters used to skip ahead is

        described in the paper F. Brown, "Random Number Generation with Arbitrary Stride,"

        Trans. Am. Nucl. Soc. (Nov. 1994). This algorithm is able to skip ahead in

        O(log2(N)) operations instead of O(N). It computes parameters

        A and C which can then be used to find x_N = A*x_0 + C mod 2^M.

        """


        nskip: np.uint32 = np.uint32(ns)


        a: np.uint32 = self._a

        c: np.uint32 = self._c


        a_next: np.uint32 = LCG.UONE

        c_next: np.uint32 = LCG.UZERO


        while nskip > LCG.UZERO:

            if (nskip & LCG.UONE) != LCG.UZERO:

                a_next = a_next * a

                c_next = c_next * a + c


            c = (a + LCG.UONE) * c

            a = a * a


            nskip = nskip >> LCG.UONE


        self._seed = a_next * self._seed + c_next



#%%

np.seterr(over='ignore')


a = np.uint32(1664525)

c = np.uint32(1013904223)

seed = np.uint32(1)


rng = LCG(seed, a, c)


print(rng.next())

print(rng.next())

print(rng.next())


查看完整回答
反对 回复 2022-01-24
?
慕妹3146593

TA贡献1820条经验 获得超9个赞

我写的一篇文章包含有关唯一随机标识符的建议。从您的问题来看,您似乎面临以下困境:生成随机唯一标识符——

  • 足够长以至于难以猜测,但是

  • 足够短,便于最终用户输入。

那篇文章中的建议解释了如何生成唯一的随机 ID(128 位或更长,使用加密 RNG,例如random_bytesbin2hexPHP 中)。但出于您的目的,生成的 ID 可能太长而不适合。有一些方法可以处理如此长的 ID,包括——

  1. 将 ID 划分为可记忆的块(例如:“374528294473”变为“374-538-294-473”),

  2. 将 ID 转换为一系列容易记住的单词(如比特币的 BIP39),或

  3. 在 ID 的末尾添加一个所谓的“校验和数字”以防止输入错误。

在决定使用比该文章中的建议更短的 ID 之前,您应该尝试 (1) 或 (2)。

此外,您的应用程序通常必须根据已生成的 ID 数据库检查 ID 的唯一性;但是,这种唯一性检查可能会或可能不会成为您的应用程序的性能瓶颈,唯一的方法是尝试并查看。或者,您可以将 ID 表的记录号(对于每条记录应该是唯一的)包含在 ID 本身中。


如果订单 ID 是唯一授予访问该订单信息的权限,则也可能存在严重的安全问题。理想情况下,应该有其他形式的授权,例如只允许登录用户或某些登录用户访问与订单 ID 关联的订单信息。另请参阅此问题


查看完整回答
反对 回复 2022-01-24
  • 2 回答
  • 0 关注
  • 185 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信