Django、Python 和链接加密
我需要设置一种加密方式,用来生成用户特定的链接。用户点击这个链接后,在另一个页面上,相关的链接会用加密的字符串解密,然后返回结果。
为此,我需要一个加密函数,这个函数需要输入一个数字(或者字符串),这个数字是我选择的项目的主键,和用户账户绑定。同时,它还需要一个种子(seed),然后生成一个加密代码,这个代码会在另一个页面解密。
所以大概是这样的:
my_items_pk = 36 #primary key of an item
seed = "rsdjk324j23423j4j2" #some string for crypting
encrypted_string = encrypt(my_items_pk,seed)
#generates some crypted string such as "dsaj2j213jasas452k41k"
and at another page:
decrypt_input = encrypt(decypt,seed)
print decrypt_input
#gives 36
我希望我的“种子”是某种主要变量(而不是某个类),可以是一个数字或者字符串。
我该如何在Python和Django中实现这个呢?
2 个回答
Django现在有一些功能可以做到这一点。你可以查看这个链接:https://docs.djangoproject.com/en/dev/topics/signing/
引用一下那页内容:
“Django提供了一个低级别的API来对值进行签名,还有一个高级别的API来设置和读取签名的cookie,这是在网络应用中最常见的签名用途之一。”
你可能还会发现签名在以下方面很有用:
- 生成“找回我的账户”链接,发送给那些忘记密码的用户。
- 确保存储在隐藏表单字段中的数据没有被篡改。
- 生成一次性秘密链接,让用户临时访问受保护的资源,比如他们已经支付的可下载文件。
Python本身并没有内置的加密算法。不过,你可以看看Python加密工具包(PyCrypt)。我只是稍微试过一下,但在Python的文档中提到过加密服务。下面是一个使用PyCrypt通过AES加密字符串的例子:
from Crypto.Cipher import AES
from urllib import quote
# Note that for AES the key length must be either 16, 24, or 32 bytes
encryption_obj = AES.new('abcdefghijklmnop')
plain = "Testing"
# The plaintext must be a multiple of 16 bytes (for AES), so here we pad it
# with spaces if necessary.
mismatch = len(plain) % 16
if mismatch != 0:
padding = (16 - mismatch) * ' '
plain += padding
ciph = encryption_obj.encrypt(plain)
# Finally, to make the encrypted string safe to use in a URL we quote it
quoted_ciph = quote(ciph)
然后你可以把这个加密后的部分放到你的URL里,可能作为GET请求的一部分。
要解密,只需反向操作;假设encryption_obj
是像上面那样创建的,并且你已经获取了URL中相关的部分,下面的代码可以做到:
from urllib import unquote
# We've already created encryption_object as shown above
ciph = unquote(quoted_ciph)
plain = encryption_obj.decrypt(ciph)
你也可以考虑另一种方法:一个简单的办法是对主键进行哈希(如果你愿意,可以加点盐),然后把哈希值和主键存储在数据库里。给用户提供哈希值作为他们链接的一部分,当他们返回并提供哈希值时,查找对应的主键并返回相应的对象。(如果你想走这条路,可以看看内置库hashlib。)
举个例子,你可以在models.py中定义类似这样的内容:
class Pk_lookup(models.Model):
# since we're using sha256, set the max_length of this field to 32
hashed_pk = models.CharField(primary_key=True, max_length=32)
key = models.IntegerField()
然后你可以在视图中生成哈希,使用类似下面的代码:
import hashlib
import Pk_lookup
hash = hashlib.sha256()
hash.update(str(pk)) # pk has been defined previously
pk_digest = hash.digest()
lookup = Pk_lookup(hashed_pk=pk_digest,key=pk)
lookup.save()
注意,你也需要对这个版本进行引用;如果你愿意,可以使用hexdigest()
代替digest
(这样生成的字符串就不需要引用了),但你需要把字段的长度调整为64。