Module: Apertur::Signature
- Defined in:
- lib/apertur/signature.rb
Overview
Webhook signature verification utilities.
Provides constant-time signature verification for three webhook formats used by the Apertur platform.
Class Method Summary collapse
-
.secure_compare(a, b) ⇒ Boolean
Constant-time string comparison to prevent timing attacks.
-
.verify_event(body, timestamp, signature, secret) ⇒ Boolean
Verify an event webhook signature (HMAC SHA256 method).
-
.verify_svix(body, svix_id, timestamp, signature, secret) ⇒ Boolean
Verify an event webhook signature (Svix method).
-
.verify_webhook(body, signature, secret) ⇒ Boolean
Verify an image delivery webhook signature.
Class Method Details
.secure_compare(a, b) ⇒ Boolean
Constant-time string comparison to prevent timing attacks.
71 72 73 74 75 76 77 |
# File 'lib/apertur/signature.rb', line 71 def secure_compare(a, b) return false unless a.bytesize == b.bytesize OpenSSL.fixed_length_secure_compare(a, b) rescue StandardError false end |
.verify_event(body, timestamp, signature, secret) ⇒ Boolean
Verify an event webhook signature (HMAC SHA256 method).
The signed payload is “#{timestamp}.#{body}” and the signature header is formatted as sha256=<hex>.
38 39 40 41 42 43 |
# File 'lib/apertur/signature.rb', line 38 def verify_event(body, , signature, secret) signature_base = "#{}.#{body}" expected = OpenSSL::HMAC.hexdigest("SHA256", secret, signature_base) sig = signature.start_with?("sha256=") ? signature[7..] : signature secure_compare(expected, sig) end |
.verify_svix(body, svix_id, timestamp, signature, secret) ⇒ Boolean
Verify an event webhook signature (Svix method).
The signed payload is “#{svix_id}.#{timestamp}.#{body}” and the signing key is the secret decoded from hex. The signature header is formatted as v1,<base64>.
57 58 59 60 61 62 63 64 |
# File 'lib/apertur/signature.rb', line 57 def verify_svix(body, svix_id, , signature, secret) signature_base = "#{svix_id}.#{}.#{body}" key = [secret].pack("H*") expected = OpenSSL::HMAC.digest("SHA256", key, signature_base) expected_b64 = [expected].pack("m0") sig = signature.start_with?("v1,") ? signature[3..] : signature secure_compare(expected_b64, sig) end |
.verify_webhook(body, signature, secret) ⇒ Boolean
Verify an image delivery webhook signature.
The signature header is formatted as sha256=<hex> and is computed as HMAC-SHA256(body, secret).
22 23 24 25 26 |
# File 'lib/apertur/signature.rb', line 22 def verify_webhook(body, signature, secret) expected = OpenSSL::HMAC.hexdigest("SHA256", secret, body) sig = signature.start_with?("sha256=") ? signature[7..] : signature secure_compare(expected, sig) end |