timestamp = request.headers.fetch('X-Signature-Timestamp')
signature = request.headers.fetch('X-Signature')
payload = request.raw_post
data = "#{timestamp}.#{payload}"
expected = OpenSSL::HMAC.hexdigest('SHA256', ENV.fetch('WEBHOOK_SECRET'), data)
unless ActiveSupport::SecurityUtils.secure_compare(signature, expected)
head :unauthorized
end
import hashlib
import hmac
import time
timestamp = str(int(time.time()))
body = b'{"event":"member.approved","id":42}'
signing_string = f'{timestamp}.'.encode('utf-8') + body
signature = hmac.new(SECRET.encode('utf-8'), signing_string, hashlib.sha256).hexdigest()
When I need lightweight message integrity without standing up a full asymmetric trust model, HMAC signing is a solid tool. The important details are canonicalization, timestamp freshness, and constant-time comparison. Most failed implementations get the signing process almost right and that is not good enough.