Spec-Zone.ru › Django 2.0

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

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

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

Подпись также может быть полезна для следующих целей:

  • Генерация 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, сжатую строку 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/2.0/topics/signing/

Spec-Zone.ru

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