Spec-Zone.ru › Django 1.9

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

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

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.

Ранее параметр max_age принимал только целое число.

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

Если вы хотите защитить список, кортеж или словарь, вы можете сделать это с помощью функций модуля подписи 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, сжатую строку JSON base64. Сериализованный объект подписывается с помощью 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/1.9/topics/signing/

Spec-Zone.ru

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