Spec-Zone.ru › Django 3.1

Криптографическая подпись

Золотое правило безопасности веб-приложений — никогда не доверять данным из недоверенных источников. Иногда полезно передавать данные через недоверенный канал. Криптографически подписанные значения могут быть переданы по недоверенному каналу, зная, что любая попытка подмены будет обнаружена.

Django предоставляет как низкоуровневый API для подписи значений, так и высокоуровневый API для установки и чтения подписанных куки-файлов, что является одним из наиболее распространенных способов использования подписи в веб-приложениях.

Вам также может быть полезна подпись для:

  • Генерирования URL-адресов «восстановления учетной записи» для отправки пользователям, которые потеряли свой пароль.
  • Обеспечения того, что данные, хранящиеся в скрытых полях формы, не были изменены.
  • Генерирования одноразовых секретных URL-адресов для предоставления временного доступа к защищенному ресурсу, например, загружаемому файлу, за который пользователь заплатил.

Защита секретного ключа

При создании нового проекта Django с помощью startproject, файл 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'

Если вы передадите в sign значение, отличное от строки, то оно будет принудительно преобразовано в строку перед подписью, и результат unsign вернет вам это строковое значение:

>>> signed = signer.sign(2.5)
>>> original = signer.unsign(signed)
>>> original
'2.5'

Если подпись или значение были изменены каким-либо образом, будет поднято исключение 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'
class Signer(key=None, sep=':', salt=None, algorithm=None)

Возвращает подписывающий экземпляр, который использует key для генерации подписей и sep для разделения значений. sep не может быть в алфавите URL-безопасного base64. Этот алфавит содержит буквенно-цифровые символы, дефисы и подчеркивания. algorithm должен быть алгоритмом, поддерживаемым hashlib, по умолчанию это 'sha256'.

Изменено в Django 3.1:

Параметр algorithm был добавлен.

Использование аргумента salt

Если вы не хотите, чтобы каждое вхождение определенной строки имело одинаковую хэш-подпись, вы можете использовать необязательный аргумент salt для класса Signer. Использование соли добавит соль и ваше значение 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'

Использование соли таким образом помещает разные подписи в разные пространства имен. Подпись, полученная из одного пространства имен (определенное значение соли), не может быть использована для проверки одного и того же открытого текста в другом пространстве имен, использующем другое значение соли. В результате предотвращается использование злоумышленником подписанной строки, сгенерированной в одном месте кода, как входного значения для другого фрагмента кода, который генерирует (и проверяет) подписи, используя другую соль.

В отличие от вашего SECRET_KEY, ваша соль не должна храниться в секрете.

Проверка отметенных временных значений

TimestampSigner — это подкласс Signer, который добавляет подписанную метку времени к значению. Это позволяет вам подтвердить, что подписанное значение было создано в течение заданного периода времени:

>>> 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'
class TimestampSigner(key=None, sep=':', salt=None, algorithm='sha256')
sign(value)

Подписывает value и добавляет к нему текущую метку времени.

unsign(value, max_age=None)

Проверяет, была ли value подписана менее чем max_age секунд назад, в противном случае поднимает исключение SignatureExpired. Параметр max_age может принимать целое число или объект datetime.timedelta.

Изменено в Django 3.1:

Параметр algorithm был добавлен.

Защита сложных структур данных

Если вы хотите защитить список, кортеж или словарь, вы можете сделать это с помощью функций модуля подписи dumps и loads. Они имитируют модуль pickle Python, но используют сериализацию JSON под капотом. JSON гарантирует, что даже если ваш SECRET_KEY будет украден, злоумышленник не сможет выполнить произвольные команды, используя формат pickle:

>>> 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', serializer=JSONSerializer, compress=False)

Возвращает URL-безопасную, подписанную сжатую строку base64 JSON. Сериализованный объект подписывается с использованием TimestampSigner.

loads(string, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None)

Обратное преобразование dumps(), поднимает BadSignature если подпись не проходит проверку. Проверяет max_age (в секундах), если передано.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/3.1/topics/signing/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API