Криптографическая подпись ¶
Золотое правило безопасности веб-приложений - никогда не доверять данным из ненадежных источников. Иногда может быть полезно передать данные через ненадежный носитель. Значения с криптографической подписью могут передаваться по ненадежному каналу в безопасном месте, зная, что любое вмешательство будет обнаружено.
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'
Если вы хотите защитить список, кортеж, или словарь вы можете сделать это , используя
sign_object()
и unsign_object()
методы:
>>> signed_obj = signer.sign_object({'message': 'Hello!'})
>>> signed_obj
'eyJtZXNzYWdlIjoiSGVsbG8hIn0:Xdc-mOFDjs22KsQAqfVfi8PQSPdo3ckWJxPWwQOFhR4'
>>> obj = signer.unsign_object(signed_obj)
>>> obj
{'message': 'Hello!'}
Подробнее см. Защита сложных структур данных.
Если подпись или значение были изменены каким-либо образом,
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 , algorithm = None ) ¶ Возвращает подписывающего, который использует
key
для генерации подписей иsep
разделения значений.sep
не может быть вURL безопасный алфавит base64 . Этот алфавит содержит буквенно-цифровые символы, дефисы и подчеркивания.algorithm
должен быть алгоритм, поддерживаемый, поhashlib
умолчанию'sha256'
.Изменено в Django 3.1:algorithm
Параметр был добавлен.
sign_object()
И unsign_object()
были добавлены методы.
Используя salt
аргумент ¶
Если вы не хотите, чтобы каждое вхождение определенной строки имело один и тот же хэш подписи, вы можете использовать необязательный salt
аргумент Signer
класса. Использование соли засевает хеш-функцию подписи как солью, так и вашими SECRET_KEY
:
>>> signer = Signer()
>>> signer.sign('My string')
'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
>>> signer.sign_object({'message': 'Hello!'})
'eyJtZXNzYWdlIjoiSGVsbG8hIn0:Xdc-mOFDjs22KsQAqfVfi8PQSPdo3ckWJxPWwQOFhR4'
>>> signer = Signer(salt='extra')
>>> signer.sign('My string')
'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw'
>>> signer.unsign('My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw')
'My string'
>>> signer.sign_object({'message': 'Hello!'})
'eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I'
>>> signer.unsign_object('eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I')
{'message': 'Hello!'}
Использование соли таким образом помещает разные подписи в разные пространства имен. Подпись, полученная из одного пространства имен (конкретное значение соли), не может использоваться для проверки той же строки открытого текста в другом пространстве имен, в котором используется другой параметр соли. В результате злоумышленник не может использовать строку со знаком, созданную в одном месте кода, в качестве входных данных для другого фрагмента кода, который генерирует (и проверяет) подписи с использованием другой соли.
В отличие от вашего SECRET_KEY
, ваш солидный аргумент не должен оставаться в секрете.
sign_object()
И unsign_object()
были добавлены методы.
Проверка значений с отметкой времени ¶
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 , algorithm = 'sha256' ) ¶ -
sign
( значение ) ¶ Подпишите
value
и добавьте к нему текущую отметку времени.
-
unsign
( значение , max_age = None ) ¶ Проверяет,
value
был ли подписан менееmax_age
секунды назад, в противном случае - поднимаетсяSignatureExpired
.max_age
Параметр может принимать целое число илиdatetime.timedelta
объект.
-
sign_object
( obj , serializer = JSONSerializer , compress = False ) ¶ - Новое в Django 3.2.
Кодировать, при необходимости сжимать, добавлять текущую метку времени и подписывать сложную структуру данных (например, список, кортеж или словарь).
-
unsign_object
( signed_obj , serializer = JSONSerializer , max_age = None ) ¶ - Новое в Django 3.2.
Проверяет,
signed_obj
был ли подписан менееmax_age
секунды назад, в противном случае - поднимаетсяSignatureExpired
.max_age
Параметр может принимать целое число илиdatetime.timedelta
объект.
Изменено в Django 3.1:algorithm
Параметр был добавлен.-
Защита сложных структур данных ¶
Если вы хотите защитить список, кортеж или словарь вы можете сделать это , используя
Signer.sign_object()
и unsign_object()
методы, или подписавшись модуля
dumps()
или loads()
функции (которые являются ярлыками
TimestampSigner(salt='django.core.signing').sign_object()/unsign_object()
). Они используют сериализацию JSON под капотом. JSON гарантирует, что даже в случае
SECRET_KEY
кражи злоумышленник не сможет выполнять произвольные команды, используя формат рассола:
>>> from django.core import signing
>>> signer = signing.TimestampSigner()
>>> value = signer.sign_object({'foo': 'bar'})
>>> value
'eyJmb28iOiJiYXIifQ:1kx6R3:D4qGKiptAqo5QW9iv4eNLc6xl4RwiFfes6oOcYhkYnc'
>>> signer.unsign_object(value)
{'foo': 'bar'}
>>> value = signing.dumps({'foo': 'bar'})
>>> value
'eyJmb28iOiJiYXIifQ:1kx6Rf:LBB39RQmME-SRvilheUe5EmPYRbuDBgQp2tCAi7KGLk'
>>> 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
(в секундах), если есть.
sign_object()
И unsign_object()
были добавлены методы.