Генератор HMAC и проверка подписи
Вычисляет HMAC-SHA-1, SHA-256, SHA-384 и SHA-512 из сообщения и ключа и определяет, каким из них получена данная подпись.
Введите ключ, чтобы вычислить подписи.
Что делает этот инструмент
HMAC превращает сообщение и общий секрет в короткую подпись. Всякий, у кого есть тот же секрет, может пересчитать её и увидеть, дошло ли сообщение неизменным и пришло ли оно от того, кто секрет тоже знает. Этот инструмент считает все четыре распространённых варианта сразу и по присланной подписи говорит, каким из них она получена.
Именно второе направление обычно и приводит сюда. Вебхук не проходит проверку, и настоящий вопрос не «действительна ли эта подпись», а «что из нескольких правдоподобных вещей я делаю иначе, чем отправитель».
Ключ — это место, где всё ломается
HMAC подписывает байты байтами. Сообщение обычно очевидно — сырое тело запроса, — а ключ почти никогда, потому что секрет приходит строкой, а строка не является байтами, пока вы не решили, как её читать:
a3f2 -> 61 33 66 32 4 bytes как текст a3f2 -> a3 f2 2 bytes как hex
Оба прочтения законны, оба дают совершенно корректную подпись, и у этих двух подписей нет ничего общего. Никто вас не предупредит: ни ошибки, ни жалобы на длину — только значение, не совпадающее с тем, что вычислил отправитель. Поэтому кодировка ключа здесь видимый выбор, а не догадка: когда подпись не совпадает, это первое, что стоит переключить.
Грубый ориентир: секрет с префиксом вроде whsec_ или строка из букв в обоих регистрах и цифр обычно задуман как текст; строка ровно из 32 или 64 знаков, где встречаются только 0-9 и a-f, как правило шестнадцатеричная; а оканчивающаяся на = почти наверняка Base64. Но сверяйтесь с документацией отправителя, а не с формой, потому что форма — не доказательство.
Сообщение должно быть ровно теми байтами
Вторая половина несовпадения — сообщение. HMAC определён над байтами, поэтому всё, что меняет байты, полностью меняет подпись: частичного зачёта и почти-попадания не бывает.
- Повторная сериализация JSON. Разобрать тело и собрать его заново может переставить ключи, изменить пробелы или нормализовать числа. Подписывайте и проверяйте то сырое тело, которое получили, а не копию, прошедшую туда и обратно.
- Перевод строки в конце. Некоторые инструменты добавляют его при сохранении тела в файл; это байт, и он меняет всё.
- Кодировка символов. Тело с не-ASCII текстом должно читаться в одной и той же кодировке с обеих сторон — на практике UTF-8.
- Сжатие или промежуточный слой. Если что-то перед вашим обработчиком распаковывает или переписывает тело, проверяйте до этого, а не после.
Многие поставщики к тому же подписывают не одно тело. Stripe подписывает метку времени и тело, соединённые точкой; AWS подписывает каноничный запрос, собранный из метода, пути, заголовков и хеша полезной нагрузки. Если проверка против голого тела не проходит, подписываемая строка, скорее всего, не голое тело — это описано на стороне отправителя и стоит прочтения прежде, чем копать дальше.
Почему SHA-1 предлагается здесь и не предлагается в инструменте хеширования
Инструмент хеширования помечает SHA-1 как сломанный. Здесь HMAC-SHA-1 показан без предупреждения, и это намеренно, а не недосмотр.
Для подписей и сертификатов SHA-1 непригоден, потому что коллизии можно построить: два разных документа можно заставить иметь один и тот же хеш. HMAC от этого свойства не зависит. Его безопасность держится на секретном ключе, а конструкция — хешировать сообщение дважды, подмешивая ключ в оба раза, — устойчива даже тогда, когда базовый хеш слаб к коллизиям. HMAC-SHA-1 остаётся надёжным и именно он используется в OAuth 1.0a и старой схеме подписи AWS, так что инструмент, отказавшийся его считать, был бы просто менее полезным, не становясь безопаснее.
Для всего нового разумный выбор — SHA-256. Более длинные хеши здесь не заметно сильнее: потолок безопасности — ключ, а не длина хеша, — поэтому SHA-384 и SHA-512 стоит брать лишь тогда, когда их требует то, с чем вы обязаны взаимодействовать.
Как безопасно сравнивать подписи
Эта страница сравнивает обычным сравнением строк, и здесь это нормально: ключ у вас в руках, утекать нечему. На сервере, который проверяет входящий запрос, это уже не нормально. Обычное сравнение останавливается на первом различающемся байте, поэтому затраченное время выдаёт, какая часть догадки была верна, и тот, кто может слать много запросов, восстановит действительную подпись побайтово.
Используйте сравнение за постоянное время, которое даёт ваша платформа, — crypto.timingSafeEqual в Node, hmac.compare_digest в Python, hash_equals в PHP — по сырым байтам, а не по шестнадцатеричным строкам. Вдобавок сверяйтесь с подписью, вычисленной вами самими, а не доверяйте алгоритму, названному в запросе, и отклоняйте сообщение, метка времени которого далека от текущей, чтобы старую действительную подпись нельзя было воспроизвести.
HMAC — это не хеш и не подпись
Здесь путают три вещи, и разница важна для того, что потом можно утверждать:
- Хеш берёт сообщение и выдаёт дайджест. Вычислить его может кто угодно, поэтому он доказывает, что сообщение не изменилось случайно, а не то, что оно пришло от кого-то определённого.
- HMAC берёт сообщение и общий секрет. У обеих сторон один и тот же ключ, поэтому он доказывает, что отправитель знал секрет. Какая именно сторона отправила, он доказать не может: произвести его могла любая.
- Подпись использует закрытый ключ, который есть только у отправителя, и открытый, которым может проверить любой. Это и даёт неотказуемость: отправитель не сможет потом от неё отпереться.
Итак, HMAC — правильный инструмент между двумя системами, которые уже разделяют секрет: вебхуки, внутренние API, сессионные токены, — и неправильный, когда нужно доказать третьей стороне, кто что-то отправил. Заметьте также: он аутентифицирует, но не скрывает. Сообщение идёт открытым текстом, и о конфиденциальности HMAC не говорит ничего.
Частые вопросы
- Моя подпись не совпадает. Что проверить первым?
- Кодировку ключа, затем байты сообщения. Секрет, похожий на шестнадцатеричный, часто и задуман как шестнадцатеричный, а не как текст, и оба варианта дают совершенно разные подписи без всякой ошибки. Затем убедитесь, что подписываете сырое тело ровно в том виде, в каком оно пришло, а не пересобранную копию.
- Почему SHA-1 предлагается здесь, если инструмент хеширования называет его сломанным?
- Потому что HMAC не опирается на стойкость к коллизиям — то самое свойство, которое SHA-1 утратил. Его безопасность идёт от ключа. HMAC-SHA-1 по-прежнему надёжен и используется в OAuth 1.0a и старой подписи AWS, хотя для нового правильное значение по умолчанию — SHA-256.
- Длиннее хеш — безопаснее?
- Не сколько-нибудь заметно. Стойкость HMAC ограничена секретом, а не длиной хеша, так что SHA-512 не вчетверо лучше SHA-256. Берите тот, которого ждёт другая сторона.
- Чем HMAC отличается от цифровой подписи?
- HMAC использует секрет, известный обеим сторонам, поэтому доказывает, что отправитель знал секрет, но не то, какая сторона отправила. Цифровая подпись использует закрытый ключ, который есть только у отправителя, поэтому её может проверить третья сторона, а отправитель не сможет отпереться. Если нужно последнее, HMAC — неподходящий инструмент.
- Сравнивать ли подписи через ===?
- На сервере — нет. Обычное сравнение возвращается, как только байты разошлись, и это время выдаёт, какая часть угаданной подписи была верной. Используйте сравнение за постоянное время вашей платформы по сырым байтам. На этой странице это неважно, потому что ключ у вас и так есть.
- Отправляется ли мой ключ куда-нибудь?
- Нет. Всё вычисляется в вашем браузере через Web Crypto API; сообщение и ключ никогда не покидают ваше устройство.