将整数(最多2^48)牢固地加密到最短的URL-SAFE字符串中



在我的django应用程序中,我具有层次URL结构:

webpage.com/property/PK/sub-property/PK/等...

我不想暴露主要键并创建一个漏洞。所以我是将所有PK加密到所有模板和URL中的字符串中。这是由此SO用户编写的精美库Django-recrypted-ID完成的。但是,该库最多支持2^64个长整数,并产生24个字符(22 2个填充)。这会导致我的嵌套结构中的巨大URL。

因此,我想修补加密和解密功能,并尝试缩短输出。这是原始代码(我添加的 填充处理):

# Remove the padding after encode and add it on decode
PADDING = '=='
def encode(the_id):
    assert 0 <= the_id < 2 ** 64
    crc = binascii.crc32(bytes(the_id)) & 0xffffff
    message = struct.pack(b"<IQxxxx", crc, the_id)
    assert len(message) == 16
    cypher = AES.new(
        settings.SECRET_KEY[:24], AES.MODE_CBC,
        settings.SECRET_KEY[-16:]
    )
    return base64.urlsafe_b64encode(cypher.encrypt(message)).rstrip(PADDING)

def decode(e):
    if isinstance(e, basestring):
        e = bytes(e.encode("ascii"))
    try:
        e += str(PADDING)
        e = base64.urlsafe_b64decode(e)
    except (TypeError, AttributeError):
        raise ValueError("Failed to decrypt, invalid input.")
    for skey in getattr(settings, "SECRET_KEYS", [settings.SECRET_KEY]):
        cypher = AES.new(skey[:24], AES.MODE_CBC, skey[-16:])
        msg = cypher.decrypt(e)
        crc, the_id = struct.unpack("<IQxxxx", msg)
        if crc != binascii.crc32(bytes(the_id)) & 0xffffff:
            continue
        return the_id
    raise ValueError("Failed to decrypt, CRC never matched.")
# Lets test with big numbers
for x in range(100000000, 100000003):
    ekey = encode(x)
    pk =  decode(ekey)
    print "Pk: %s Ekey: %s" % (pk, ekey)

输出(我稍微更改了字符串,所以不要试图破解我:p):

Pk: 100000000 Ekey: GNtOHji8rA42qfq3p5gNMI
Pk: 100000001 Ekey: tK6RcAZ2MrWmR3nB5qkQDe
Pk: 100000002 Ekey: a7VXIf8pEB6R7XvqwGQo6W

我试图修改encode()功能中的所有内容,但没有任何成功。产生的弦始终长度为22。

这是我想要的:

  • 保持加密强度接近原始级别,或者至少不会大大降低
  • 支持整数最多2^48(〜281万亿)或2^40,因为现在使用2^64的时间太多了,我认为我们不会在数据库中拥有如此巨大的PK。
  • 我将对14-20之间的弦长度感到满意。如果它的20 ..是的,它仍然少2个字符..

当前您正在使用静态IV使用CBC模式,因此您无论如何您都不安全,并且像您说的那样,产生了相当大的Ciphertexts。

我建议将CBC模式交换为CTR模式,这使您具有可变长度IV。我认为,在CTR模式下IV(或NONCE)的正常建议长度为12,但是您可以根据需要将其降低或向下减少。CTR也是一个流密封器,这意味着您投入的是您在尺寸方面得到的。使用AES,CBC模式将始终在16个字节的块中返回您的密文,因此即使您要加密6个字节,您也会获得16个字节,因此对您而言并不理想。

如果您让静脉输注...长48位,旨在加密不超过48位,则可以产生6 6 = 12个字节的原始输出,或使用Base64(4*)(12/3))= 16个字节。通过进一步降低IV和/或输入尺寸(2^40?),您将能够获得比这更低的输出。您可以随意降低输入的可能值,而不会损害安全性。

请记住,CTR确实有陷阱。产生两个共享相同iv和键的密文,这意味着它们可能会被微不足道的破碎,因此始终随机生成您的iv(并且不要过多地减小它的大小)。

最新更新