Spec-Zone.ru › Django 2.1

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

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

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

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

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

Защита SECRET_KEY

При создании нового проекта 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'

Если подпись или значение были изменены каким-либо образом, будет поднято исключение 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) [source]

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

Использование аргумента 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) [source]
sign(value) [source]

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

unsign(value, max_age=None) [source]

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

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

Если вы хотите защитить список, кортеж или словарь, вы можете сделать это с помощью функций модуля подписи dumps и loads. Они имитируют модуль Python pickle, но используют сериализацию 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', compress=False) [source]

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

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

Обратная функция dumps(), вызывает исключение BadSignature при ошибке подписи. Проверяет max_age (в секундах), если задано.

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

Spec-Zone.ru

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