Spec-Zone.ru › Django 3.0

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

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

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. Они имитируют модуль 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) [source]

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

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

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

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

Spec-Zone.ru

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