【发布时间】:2017-02-23 21:33:00
【问题描述】:
在我的 Django 应用程序中,我有分层 URL 结构:
webpage.com/property/PK/sub-property/PK/ 等等...
我不想暴露主键并造成漏洞。因此我是 将所有 PK 加密为所有模板和 URL 中的字符串。这是由this SO 用户编写的精彩库django-encrypted-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 个字符..
【问题讨论】:
-
出于好奇,为什么每次都要加解密呢?您是否只是试图通过切换 URL 中的密钥来防止访问随机属性?在那种情况下,我为每个对象生成了一个 UUID 并将其存储在模型中,然后用它来进行查找……效率更高。
-
是的,这就是我这样做的原因:)。我们曾经有 UUID,但我们没有将它们存储在数据库中。另外,我在这里的目标是获得尽可能短的字符串。为了碰撞安全,我相信 UUID 相当长。而且我读过截断它们是不安全的。
-
无论您使用什么方法(只要它是好的),您的碰撞机会将在很大程度上取决于标识符的长度。这也将是空间/时间的权衡:我个人只会生成一个随机 id 以保存到模型中,(并检查以确保它不在数据库中,如果是,则生成另一个)。我会占用额外的存储空间,而不是每次访问对象 URL 时都必须加密/解密(因为您也有子属性,所以更是如此)。
标签: python django encryption aes