Bir teslimatı doğrulamak
`OpenEmail.verify_webhook_signature`: sabit zamanlı, yeniden oynatma penceresiyle ve ayrıştırılmış olayı geri döndürerek.
Bir istek işleyicisinde
Bir webhook URL'si herkese açıktır. İnternetteki her şey ona doğru biçimli JSON'u POST ile gönderebilir; bu yüzden imzayı denetlemeden olayın type değerini okuyan bir işleyici, herkese açık bir yazma API'sidir.
class OpenEmailWebhooksController < ActionController::API def create event = OpenEmail.verify_webhook_signature( payload: request.raw_post, headers: request.headers, secret: ENV.fetch("OPENEMAIL_WEBHOOK_SECRET"), tolerance_seconds: 300 ) Rails.logger.info("#{event[:type]} #{event[:data]}") head :no_content rescue OpenEmail::WebhookSignatureError head :bad_request endendHAM gövdeyi bir String olarak geçirin. Ayrıştırıp yeniden serileştirmek anahtar sırasını ve boşlukları değiştirir ve imza eşleşmez; Rails'in params gibi bir Hash'in denetlenmek yerine ArgumentError fırlatmasının nedeni budur. headers:, başlık adı herhangi bir büyük/küçük harf biçiminde olan bir Hash, başlığın HTTP_X_OPENEMAIL_SIGNATURE olarak geldiği bir Rack ortamı ya da Rails'in request.headers nesnesini alır. Array olan bir değer ilk öğesinden okunur.
Elle yazılmış bir denetimin genellikle ele almadığı iki şeyi ele alır: MAC'i sabit zamanda karşılaştırır, böylece doğru önek süre ölçülerek elde edilemez; ve her iki yönde de tolerance_seconds: değerinden (aksini belirtmedikçe beş dakika) daha eski bir teslimatı reddeder, böylece yakalanmış bir istek sonsuza dek yeniden oynatılamaz. tolerance_seconds: 0 yeniden oynatma denetimini kapatır. İki hata da sessizdir. Bunlardan birine sahip bir işleyici, yazmayı düşüneceğiniz her testi geçer.
Her başarısızlıkta OpenEmail::WebhookSignatureError fırlatır: eksik bir X-OpenEmail-Signature başlığı, t=<seconds>,v1=<hex> biçiminde olmayan bir başlık, pencerenin dışındaki bir zaman damgası ya da eşleşmeyen bir imza. Başarılı olduğunda gövdeyi id, type, createdAt ve data içeren, Symbol anahtarlı bir Hash'e ayrıştırılmış olarak döndürür; böylece yanlış yapılabilecek ikinci bir JSON.parse olmaz.
Eksik ya da boş bir secret: ise bunun yerine ArgumentError fırlatır, çünkü bu kötü bir teslimat değil, yapılandırmanızdaki bir hatadır. Yalnızca OpenEmail::WebhookSignatureError yakalayın ve 400 ile yanıt verin; böylece yanlış yapılandırılmış bir sunucu 500 ile yanıt verir ve siz sorunu düzelttiğinizde teslimat yeniden denenir.
Gem'in zaten yüklediği standart kütüphanedeki OpenSSL'i kullanır; bu yüzden başka hiçbir şeye ihtiyaç duymaz.
Bir Rack ya da Sinatra uygulamasında
Rails dışında Rack ortamının kendisini headers: olarak geçirin ve ham gövdeyi istekten okuyun.
require "openemail"require "rack" webhook = lambda do |env| event = OpenEmail.verify_webhook_signature( payload: Rack::Request.new(env).body.read, headers: env, secret: ENV.fetch("OPENEMAIL_WEBHOOK_SECRET") ) warn "#{event[:type]} #{event[:id]}" [204, {}, []]rescue OpenEmail::WebhookSignatureError [400, {"content-type" => "text/plain"}, ["bad signature"]]end run webhookRack sürümü eksiksiz bir config.ru'dur. İkisinde de hızlı yanıt verin: 5 saniye içinde yanıt almayan bir teslimat başarısız sayılır ve daha sonra yeniden gönderilir; bu yüzden işi bir işe devredin ve 2xx ile yanıt verin.
OpenEmail::WEBHOOK_SIGNATURE_HEADERS bir teslimatın taşıdığı üç başlığı adlandırır: X-OpenEmail-Signature, olayın türüyle X-OpenEmail-Event ve kimliğiyle, yani gövdedeki aynı id ile X-OpenEmail-Delivery. Bu kimlik olayın her yeniden denemesinde ve yeniden oynatmasında aynı kalır; bu yüzden daha önce işlediğiniz olayları atlarken saklamanız gereken budur.
Gizli anahtar döndürülürken
webhooks.rotate_secret için bir çakışma penceresi yoktur: teslimatlar, çağrı döndüğü andan itibaren yeni gizli anahtarla imzalanır. Önce iki gizli anahtarı da kabul eden bir alıcı dağıtın, döndürün, yeni gizli anahtarı alıcının okuduğu yere kaydedin, ardından eskisini kaldırın.
def verify_delivery(payload, headers) secrets = [ENV.fetch("OPENEMAIL_WEBHOOK_SECRET"), ENV["OPENEMAIL_WEBHOOK_SECRET_NEXT"]].compact secrets.each_with_index do |secret, index| return OpenEmail.verify_webhook_signature(payload:, headers:, secret:) rescue OpenEmail::WebhookSignatureError raise if index == secrets.size - 1 endendwebhooks.test imzalı yapay bir olay gönderir; bu yüzden eskisini kaldırmadan önce yeni gizli anahtarın doğrulandığını kanıtlamak için onu döndürmeden sonra çağırın.