Class: Uploadcare::WebhookSignatureVerifier
- Inherits:
-
Object
- Object
- Uploadcare::WebhookSignatureVerifier
- Defined in:
- lib/uploadcare/webhook_signature_verifier.rb
Overview
This object verifies a signature received along with webhook headers
Class Method Summary collapse
-
.calculate_signature(secret, body) ⇒ String
Calculate HMAC signature for webhook body.
-
.secure_compare?(first, second) ⇒ Boolean
Constant-time string comparison to prevent timing attacks.
- .valid?(webhook_body: nil, signing_secret: nil, x_uc_signature_header: nil) ⇒ Boolean
-
.valid_parameters?(signing_secret, signature_header, body) ⇒ Boolean
Check if all required parameters are present and non-empty.
Class Method Details
.calculate_signature(secret, body) ⇒ String
Calculate HMAC signature for webhook body
37 38 39 40 |
# File 'lib/uploadcare/webhook_signature_verifier.rb', line 37 def self.calculate_signature(secret, body) digest = OpenSSL::Digest.new('sha256') "v1=#{OpenSSL::HMAC.hexdigest(digest, secret, body)}" end |
.secure_compare?(first, second) ⇒ Boolean
Constant-time string comparison to prevent timing attacks
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/uploadcare/webhook_signature_verifier.rb', line 46 def self.secure_compare?(first, second) return false if first.nil? || second.nil? return false unless first.bytesize == second.bytesize OpenSSL.fixed_length_secure_compare(first, second) rescue NoMethodError result = 0 index = 0 while index < first.bytesize result |= first.getbyte(index) ^ second.getbyte(index) index += 1 end result.zero? end |
.valid?(webhook_body: nil, signing_secret: nil, x_uc_signature_header: nil) ⇒ Boolean
8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/uploadcare/webhook_signature_verifier.rb', line 8 def self.valid?(webhook_body: nil, signing_secret: nil, x_uc_signature_header: nil) webhook_body_json = webhook_body signing_secret ||= ENV.fetch('UC_SIGNING_SECRET', nil) return false unless valid_parameters?(signing_secret, x_uc_signature_header, webhook_body_json) calculated_signature = calculate_signature(signing_secret, webhook_body_json) # Use constant-time comparison to prevent timing attacks secure_compare?(calculated_signature, x_uc_signature_header) end |
.valid_parameters?(signing_secret, signature_header, body) ⇒ Boolean
Check if all required parameters are present and non-empty
25 26 27 28 29 30 31 |
# File 'lib/uploadcare/webhook_signature_verifier.rb', line 25 def self.valid_parameters?(signing_secret, signature_header, body) return false if signing_secret.nil? || signing_secret.to_s.empty? return false if signature_header.nil? || signature_header.to_s.empty? return false if body.nil? || body.to_s.empty? true end |