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

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

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'

Если вы передадите нестроковое значение 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'
classSigner ( 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'
classTimestampSigner ( key = None , sep = ':' , salt = None , algorithm = 'sha256' )
sign( значение )

Подпишите value и добавьте к нему текущую отметку времени.

unsign( значение , max_age = None )

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

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

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

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

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

>>> 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-адресов, подписанную сжатую строку JSON в формате base64. Сериализованный объект подписан с помощью TimestampSigner .

loads( строка , ключ = Нет , соль = 'django.core.signing' , сериализатор = JSONSerializer , max_age = Нет )

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

Copyright ©2020 All rights reserved