Подписание URL с помощью HMAC или OpenSSL - PullRequest
8 голосов
/ 10 августа 2011

Меня интересует подписание URL-адреса (например, http: //.../? Somearg = value & anotherarg = anothervalue & sig = aSKS9F3KL5xc ), но у меня есть несколько требований, которые еще не оставили меня без решения.

  • Я буду использовать PHP или Python для страниц, поэтому мне нужно будет иметь возможность подписать и проверить подпись, используя один из двух.
  • Мой план состоял в том, чтобы использовать схему ключа priv / pub для подписи некоторых данных и проверки правильности подписи, но вот где это усложняется:
  • Данные неизвестны при проверкепроисходит (это не просто somearg=value&anotherarg=anothervalue)

Моим первым инстинктом было использование OpenSSL, например, с парой ключей RSA, чтобы сделать что-то вроде подписи с помощью: openssl rsautl -sign -inkey private.pem -in sensitive -out privsigned и проверки на основеТОЛЬКО на данных и ключе privsigned: openssl rsautl -verify -inkey public.pem -in privsigned -pubin.

Использование PHP openssl_get_privatekey() и openssl_sign() очень хорошо подписывает данные, но мне нужно знать (расшифрованные!) данные для проверки(которого у меня не будет): openssl_get_publickey() и openssl_verify($data, $signature, $pubkeyid); от http://php.net/openssl_verify.

Или я скучаю почто-то здесь?


Итак, я посмотрел в HMAC, но хотя многие функции хеширования доступны как в Python, так и PHP, я озадачен тем, как бы я поступил проверка хеш.PHP hash_hmac() позволяет мне создавать хеш, используя «ключ» (или в данном случае строковый ключ).Но как мне убедиться в том, что хеш действителен (т. Е. &sig= не был просто вручную введен конечным пользователем &sig=abcdefg1234.

Итак, подведем итоги (извините за длинный вопрос): Как я могу проверить, что подпись / хэш был сделан ключом моего сервера (сертификат / строка) (учитывая, что я не могу проверить, переделывая хэш упомянутых данных)? И есть ли у вас какие-либо предпочтения относительно того, какой маршрут я должен выбрать, Priv / Pub-Key или HMAC?

Любые указатели, большие или малые, очень ценятся! Заранее спасибо,

  • Джош

Ответы [ 3 ]

9 голосов
/ 05 декабря 2011

Как отметил Хеннинг Махолм, HMAC - лучший выбор, чем открытый ключ.Есть несколько рекомендаций, которые вы должны рассмотреть для своего конкретного сценария, которые повлияют на ваш выбор:

  • Хотите ли вы указать имя хоста и схему (http / https) в подписи?Может быть.
  • Хотите рассмотреть путь в подписи?Возможно.
  • Хотите рассмотреть строку запроса в подписи?Вероятно.
  • Вы хотите нормализовать порядок аргументов и экранировать до подписания?Обычно нет.
  • Хотите ли вы встраивать время подписи и т. Д. (Для создания ограниченных по времени URL-адресов)?
  • Хотите связать подписанный URL-адрес с каким-либо другим состоянием пользователя, например с файлом cookie?
  • Используете ли вы пользовательский или видимый пользователем контент непосредственно в HMAC?Если это так, вам следует «засолить» ключ, используя значение, которое рандомизируется для каждого запроса.

При вычислении подписи вам необходимо закодировать его с помощью URL-адреса (base64 иbase32 - это популярный выбор), выберите алгоритм HMAC (например, SHA-256) и решите, сколько битов подписи вы хотите сохранить (усечение значения HMAC пополам обычно безопасно).Если вы выбираете base64, остерегайтесь различных алфавитов, используемых url-safe по сравнению с не-url-safe реализациями.

Вот реализация псевдокода (без проверки ошибок или засоления и т. Д.) Для подписи path + строка запроса:

const secret = ...;

def sign(path, querystring):
  return path + "?" + querystring + "&sig=" + url_encode(base64_encode(hmacsha256(secret, path + "?" + querystring).truncate(16)))

def verify(path, querystring):
  querystring_without_sig = remove_query_parameter(querystring, "sig")
  sig = base64_decode(url_decode(get_query_parameter(querystring, "sig")))
  if hmacsha256(secret, path + "?" + querystring_without_sig)[:16] != sig:
    raise "invalid sig"

HMAC SHA256 рекомендуется и доступен на всех распространенных языках.

Java:

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secret);
return mac.doFinal(value.getBytes());

Python:

hmac.new(secret, input, hashlib.sha256).digest()

PHP:

hash_hmac("sha256", value, secret);
5 голосов
/ 10 августа 2011

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

Если вам не нужны проверяемые подписи кем-то, кто не знает секретный ключ, HMAC, вероятно, является лучшим выбором.чем системы с открытым ключом, по соображениям эффективности.Создание или проверка подписи с открытым ключом может занять несколько миллисекунд (несколько лет назад я рассчитывал одну реализацию на 15 мс на операцию), тогда как HMAC довольно быстр.

(О, и вы не можете проверить какой-либо видподписи, не зная данных, которые он должен подписать. Насколько я понимаю, это не имело бы никакого смысла).

0 голосов
/ 03 сентября 2013

Если вы хотите использовать HMAC и Python, то:

$ pip install ska

На стороне клиента

from ska import sign_url

signed_url = sign_url(
    auth_user='user', 
    secret_key='your-secret_key', 
    url='http://e.com/api/'
)

Произведенный URL выглядит следующим образом.

http://e.com/api/?valid_until=1378045287.0&auth_user=user&signature=YlZpLFsjUKBalL4x5trhkeEgqE8%3D

На стороне сервера

Обратите внимание, что в примере ниже request.GET приведен в качестве примера.Скорее всего, он будет отличаться от того, что используется в вашей среде (если вы не используете Django).

from ska import validate_signed_request_data

validation_result = validate_signed_request_data(
    data = request.GET, # Note, that ``request.GET`` is given as example.
    secret_key = 'your-secret_key'
)

validate_signed_request_data создает объект SignatureValidationResult, который в основном содержит два свойства:

  • result(bool): True, если данные верны.Ложь в противном случае.
  • причина (список): список строк, указывающих на ошибки проверки.
...