Криптографическая подпись
Золотое правило безопасности веб-приложений — никогда не доверяйте данным из недоверенных источников. Иногда бывает полезно передавать данные через недоверенный канал. Криптографически подписанные значения могут передаваться через недоверенный канал, зная, что любая попытка подделки будет обнаружена.
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не может быть в алфавите безопасного 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', 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.11/topics/signing/