加密签名

Web应用程序安全的黄金法则是永远不信任来自不受信任来源的数据。 有时将数据通过不可信介质传递是有用的。 密码签名的值可以通过一个不可信的通道安全地传递,知道任何篡改将被检测到。

Django提供了用于签名值的低级API和用于设置和读取签名cookie的高级API,这是登录Web应用程序最常见的用途之一。

您也可能会发现对以下内容有用的签名:

  • 生成“恢复我的帐户”URL以发送给失去密码的用户。
  • 确保存储在隐藏表单域中的数据没有被篡改。
  • 生成一次性秘密URL以允许临时访问受保护的资源,例如用户已付费的可下载文件。

保护SECRET_KEY

使用startproject创建新的Django项目时,会自动生成settings.py文件并获取随机的SECRET_KEY值。 这个值是保护签名数据的关键 - 保持这一安全性至关重要,或者攻击者可以使用它来生成自己的签名值。

使用低级API

Django的签名方法存在于django.core.signing模块中。 要签署一个值,首先实例化一个Signer实例:

>>> from django.core.signing import Signer
>>> signer = Signer()
>>> value = signer.sign('My string')
>>> value
'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'

签名被追加到冒号后面的字符串末尾。 您可以使用unsign方法检索原始值:

>>> original = signer.unsign(value)
>>> original
'My string'

如果签名或者值被以任何方式改变,将引发一个django.core.signing.BadSignature异常:

>>> from django.core import signing
>>> value += 'm'
>>> try:
...    original = signer.unsign(value)
... except signing.BadSignature:
...    print("Tampering detected!")

缺省情况下,Signer类使用SECRET_KEY设置来生成签名。 您可以通过将其传递给Signer构造函数来使用不同的秘密:

>>> signer = Signer('my-other-secret')
>>> value = signer.sign('My string')
>>> value
'My string:EkfQJafvGyiofrdGnuthdxImIJw'
Signer(key=None, sep=':', salt=None)[source]

返回一个使用key生成签名的签名者,sep分隔值。 sep不能在URL安全的base64字母表中。 此字母表包含字母数字字符,连字符和下划线。

使用salt参数

如果不希望每个特定字符串出现相同的签名散列,可以使用Signer类的可选salt参数。 Using a salt will seed the signing hash function with both the salt and your SECRET_KEY:

>>> signer = Signer()
>>> signer.sign('My string')
'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
>>> signer = Signer(salt='extra')
>>> signer.sign('My string')
'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw'
>>> signer.unsign('My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw')
'My string'

以这种方式使用salt将不同的签名放入不同的名称空间。 来自一个命名空间(特定盐值)的签名不能用于验证使用不同salt设置的不同命名空间中的相同纯文本字符串。 其结果是防止攻击者使用在代码中的一个地方生成的签名字符串作为另一个代码片段的输入,该代码片段使用不同的salt生成(并验证)签名。

与你的SECRET_KEY不同,你的salt参数不需要保密。

验证时间戳值

TimestampSigner is a subclass of Signer that appends a signed timestamp to the value. 这允许您确认在指定的时间段内创建了一个有符号的值:

>>> from datetime import timedelta
>>> from django.core.signing import TimestampSigner
>>> signer = TimestampSigner()
>>> value = signer.sign('hello')
>>> value
'hello:1NMg5H:oPVuCqlJWmChm1rA2lyTUtelC-c'
>>> signer.unsign(value)
'hello'
>>> signer.unsign(value, max_age=10)
...
SignatureExpired: Signature age 15.5289158821 > 10 seconds
>>> signer.unsign(value, max_age=20)
'hello'
>>> signer.unsign(value, max_age=timedelta(seconds=20))
'hello'
TimestampSigner(key=None, sep=':', salt=None)[source]
sign(value)[source]

签署value并追加当前时间戳。

unsignvaluemax_age = None[source]

检查value是否小于max_age,否则引发SignatureExpired max_age参数可以接受一个整数或一个datetime.timedelta对象。

保护复杂的数据结构

如果您希望保护列表,元组或字典,可以使用签名模块的dumpsloads函数来完成。 这些模仿Python的pickle模块,但在引擎盖下使用JSON序列化。 JSON ensures that even if your SECRET_KEY is stolen an attacker will not be able to execute arbitrary commands by exploiting the pickle format:

>>> from django.core import signing
>>> value = signing.dumps({"foo": "bar"})
>>> value
'eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI'
>>> signing.loads(value)
{'foo': 'bar'}

由于JSON的性质(在列表和元组之间没有本地区别),如果传入一个元组,您将从signing.loads(object)中获得一个列表:

>>> from django.core import signing
>>> value = signing.dumps(('a','b','c'))
>>> signing.loads(value)
['a', 'b', 'c']
dumps(obj, key=None, salt='django.core.signing', compress=False)[source]

返回URL安全的,sha1签名的base64压缩的JSON字符串。 序列化对象使用TimestampSigner进行签名。

loads(string, key=None, salt='django.core.signing', max_age=None)[source]

如果签名失败,则dumps()的反转将引发BadSignature 如果给定,检查max_age(以秒为单位)。