将整数(最多2 ^ 48)安全地加密到最短的URL安全字符串中 [英] Securely encrypt integers (up to 2^48) into the shortest possible URL-safe string

查看:227
本文介绍了将整数(最多2 ^ 48)安全地加密到最短的URL安全字符串中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的Django应用程序中,我有分层的URL结构:

In my Django application I have hierarchical URL structure:

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

我不想公开主键并创建一个漏洞。因此,我将
将所有PK加密到所有模板和URL中的字符串中。这是由 django-encrypted-id 这个用户。
但是,库最多支持2 ^ 64个长整数,并产生24个字符输出(22 + 2个填充)。这导致我的嵌套结构中的巨大的URL。

I do not want to expose primary keys and create a vulnerability. Therefore I am encrypting all PKs into strings in all templates and URLs. This is done by the wonderful library django-encrypted-id written by this SO user. However, the library supports up to 2^64 long integers and produces 24 characters output (22 + 2 padding). This results in huge URLs in my nested structure.

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

Therefore, I would like to patch the encrypting and decrypting functions and try to shorten the output. Here is the original code (+ padding handling which I added):

# 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):

Output (I changed the strings a bit, so don't try to hack me :P):

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

我尝试修改 encode() 功能,但没有任何成功。生成的字符串总是长度为22。

I have tried to modify everything in the encode() function but without any success. The produced string has always the length of 22.

这是我想要的:


  • 保持加密强度接近原始级别,或至少不会显着降低。

  • 支持最多2 ^ 48(〜281万亿)或2 ^ 40的整数,因为因为现在有2 ^ 64是太多了,我不认为我们会在数据库中有这么大的PK。

  • 我将很高兴与14-20之间的字符串长度。如果它的20 ..那么是的,它的2个字符少了..

推荐答案

正在使用具有静态IV的CBC模式,所以您所拥有的代码并不安全,就像您所说,生成相当大的密文。

Currently you are using CBC mode with a static IV, so the code you have isn't secure anyway and, like you say, produces rather large ciphertexts.

我建议从CBC模式为CTR模式,可让您具有可变长度IV。我认为,按CTR模式的IV(或nonce)的正常推荐长度是12,但是您可以根据需要减少上升或下降。 CTR也是一个流密码,这意味着你所提供的是你在大小方面得到的。使用AES,CBC模式将始终以16字节的方式返回密文,即使您正在加密6个字节,您将获得16个字节,因此不适合您。

I would recommend swapping from CBC mode to CTR mode, which lets you have a variable length IV. The normal recommended length for the IV (or nonce) in CTR mode, I think, is 12, but you can reduce this up or down as needed. CTR is also a stream cipher which means what you put in is what you get out in terms of size. With AES, CBC mode will always return you ciphertexts in blocks of 16 bytes so even if you are encrypting 6 bytes, you get 16 bytes out, so isn't ideal for you.

如果您使用IV表示48位长,目的是加密不超过48位,则可以产生6 + 6 = 12字节的原始输出,或者使用base64(4 * (12/3))= 16个字节。通过进一步降低IV和/或输入大小(2 ^ 40?),您将能够获得比此更低的输出。您可以尽可能多地降低输入值,而不会损害安全性。

If you make your IV say... 48 bits long and aim to encrypt no larger than 48 bits, you'll be able to produce a raw output of 6 + 6 = 12 bytes, or with base64, (4*(12/3)) = 16 bytes. You will be able to get a lower output than this by further reducing your IV and/or input size (2^40?). You can lower possible values of your input as much as you want without damaging the security.

请记住,点击率确实存在陷阱。生成共享相同IV和密钥的两个密文意味着它们可以被轻易地破坏,所以总是随机生成你的IV(并且不要将其大小减少太多)。

Keep in mind that CTR does have pitfalls. Producing two ciphertexts that share the same IV and key means that they can be trivially broken, so always randomly generate your IV (and don't reduce it in size too much).

这篇关于将整数(最多2 ^ 48)安全地加密到最短的URL安全字符串中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆