As Henning Maholm noted, HMAC is a better choice than a public key. There are several best practices that you should consider in your specific scenario that will affect your choice:
- Do you want to consider the host name and scheme (http / https) in the signature? May be.
- Do you want to consider the path in the signature? Maybe.
- Do you want to consider the query string in the signature? Maybe.
- Do you want to normalize the order of arguments and escaping before signing? Usually not.
- You want to insert signature time, etc. (to create time-limited URLs)?
- Do you want to associate the signed URL with some other user state like cookie?
- Do you use user-generated or user-visible content directly in the HMAC? If so, you should โsaltโ the key using a value that is randomized for each request.
When calculating the signature, you will need to encode it by the URL (base64 and base32 are popular options) and select the HMAC algorithm (for example, SHA-256) and decide how many bits of the signature you want to keep (trimming the HMAC value in half is usually safe). If you choose base64, beware of the different alphabets used according to URL safe versions other than url-safe.
Here is the implementation of the pseudocode (without checking errors or salting, etc.) for signing the line + request:
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 is recommended and available in all common languages.
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);
pawstrong
source share