The golden rule of web application security is to never trust data from untrusted sources. Sometimes it can be useful to pass data through an untrusted medium. Cryptographically signed values can be passed through an untrusted channel safe in the knowledge that any tampering will be detected.
Django provides both a low-level API for signing values and a high-level API for setting and reading signed cookies, one of the most common uses of signing in web applications.
你可能还发现签名对以下方面很有用:
SECRET_KEY and SECRET_KEY_FALLBACKS¶当你使用 startproject 创建一个新的Django项目时,settings.py 文件会自动生成,并随机得到一个 SECRET_KEY 值。这个值是保证签名数据安全的关键——你必须保证这个值的安全,否则攻击者可以用它来生成自己的签名值。
SECRET_KEY_FALLBACKS can be used to rotate secret keys. The
values will not be used to sign data, but if specified, they will be used to
validate signed data and must be kept secure.
The SECRET_KEY_FALLBACKS setting was added.
Django's signing methods live in the django.core.signing module.
To sign a value, first instantiate a Signer instance:
>>> from django.core.signing import Signer
>>> signer = Signer()
>>> value = signer.sign("My string")
>>> value
'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
The signature is appended to the end of the string, following the colon.
You can retrieve the original value using the unsign method:
>>> original = signer.unsign(value)
>>> original
'My string'
If you pass a non-string value to sign, the value will be forced to string
before being signed, and the unsign result will give you that string
value:
>>> signed = signer.sign(2.5)
>>> original = signer.unsign(signed)
>>> original
'2.5'
If you wish to protect a list, tuple, or dictionary you can do so using the
sign_object() and unsign_object() methods:
>>> signed_obj = signer.sign_object({"message": "Hello!"})
>>> signed_obj
'eyJtZXNzYWdlIjoiSGVsbG8hIn0:Xdc-mOFDjs22KsQAqfVfi8PQSPdo3ckWJxPWwQOFhR4'
>>> obj = signer.unsign_object(signed_obj)
>>> obj
{'message': 'Hello!'}
详见:ref:signing-complex-data
If the signature or value have been altered in any way, a
django.core.signing.BadSignature exception will be raised:
>>> from django.core import signing
>>> value += "m"
>>> try:
... original = signer.unsign(value)
... except signing.BadSignature:
... print("Tampering detected!")
...
By default, the Signer class uses the SECRET_KEY setting to
generate signatures. You can use a different secret by passing it to the
Signer constructor:
>>> signer = Signer(key="my-other-secret")
>>> value = signer.sign("My string")
>>> value
'My string:EkfQJafvGyiofrdGnuthdxImIJw'
Signer(*, key=None, sep=':', salt=None, algorithm=None, fallback_keys=None)¶Returns a signer which uses key to generate signatures and sep to
separate values. sep cannot be in the URL safe base64 alphabet. This alphabet contains alphanumeric characters, hyphens,
and underscores. algorithm must be an algorithm supported by
hashlib, it defaults to 'sha256'. fallback_keys is a list
of additional values used to validate signed data, defaults to
SECRET_KEY_FALLBACKS.
The fallback_keys argument was added.
4.2 版后已移除: Support for passing positional arguments is deprecated.
salt 参数¶If you do not wish for every occurrence of a particular string to have the same
signature hash, you can use the optional salt argument to the Signer
class. 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.sign_object({"message": "Hello!"})
'eyJtZXNzYWdlIjoiSGVsbG8hIn0:Xdc-mOFDjs22KsQAqfVfi8PQSPdo3ckWJxPWwQOFhR4'
>>> signer = Signer(salt="extra")
>>> signer.sign("My string")
'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw'
>>> signer.unsign("My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw")
'My string'
>>> signer.sign_object({"message": "Hello!"})
'eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I'
>>> signer.unsign_object(
... "eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I"
... )
{'message': 'Hello!'}
以这种方式使用盐,会将不同的签名放入不同的命名空间。 来自一个命名空间的签名(一个特定的盐值)不能用于验证在使用不同盐值设置的不同命名空间中的同一明文字符串。这样做的结果是防止攻击者将代码中某个地方生成的签名字符串作为输入,输入到使用不同盐值生成(和验证)签名的另一段代码中。
与你的 SECRET_KEY 不同,你的盐参数不需要保密。
TimestampSigner is a subclass of Signer that appends a signed
timestamp to the value. This allows you to confirm that a signed value was
created within a specified period of time:
>>> 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, algorithm='sha256')¶sign(value)¶签名 value 并附加当前时间戳。
unsign(value, max_age=None)¶检查 value 是否在 max_age 秒前被签署,否则引发 SignatureExpired。max_age 参数可以接受一个整数或一个 datetime.timedelta 对象。
sign_object(obj, serializer=JSONSerializer, compress=False)¶Encode, optionally compress, append current timestamp, and sign complex data structure (e.g. list, tuple, or dictionary).
unsign_object(signed_obj, serializer=JSONSerializer, max_age=None)¶Checks if signed_obj was signed less than max_age seconds ago,
otherwise raises SignatureExpired. The max_age parameter can
accept an integer or a datetime.timedelta object.
4.2 版后已移除: Support for passing positional arguments is deprecated.
If you wish to protect a list, tuple or dictionary you can do so using the
Signer.sign_object() and unsign_object() methods, or signing module's
dumps() or loads() functions (which are shortcuts for
TimestampSigner(salt='django.core.signing').sign_object()/unsign_object()).
These use JSON serialization under the hood. 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
>>> signer = signing.TimestampSigner()
>>> value = signer.sign_object({"foo": "bar"})
>>> value
'eyJmb28iOiJiYXIifQ:1kx6R3:D4qGKiptAqo5QW9iv4eNLc6xl4RwiFfes6oOcYhkYnc'
>>> signer.unsign_object(value)
{'foo': 'bar'}
>>> value = signing.dumps({"foo": "bar"})
>>> value
'eyJmb28iOiJiYXIifQ:1kx6Rf:LBB39RQmME-SRvilheUe5EmPYRbuDBgQp2tCAi7KGLk'
>>> signing.loads(value)
{'foo': 'bar'}
Because of the nature of JSON (there is no native distinction between lists
and tuples) if you pass in a tuple, you will get a list from
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', serializer=JSONSerializer, compress=False)¶返回 URL 安全的,经过签名的 base64 压缩 JSON 字符串。使用 TimestampSigner 对序列化对象进行签名。
loads(string, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None, fallback_keys=None)¶与 dumps() 相反,如果签名失败引发 BadSignature。如果给定,则检查 max_age (以秒为单位)。
The fallback_keys argument was added.
9月 22, 2023