ã¡ã¼ã«é
ä¿¡ãã©ãããã©ã¼ã ã§ããSendGridã¯ãã¡ã¼ã«ã®å°éç¶æ³ãªã©ããå¼ã³åºãå
ã®ã¢ããªã±ã¼ã·ã§ã³ã«WebHookã§ä¼ãããã¨ãã§ããã
sendgrid.kke.co.jp
ã¢ããªã±ã¼ã·ã§ã³ãSendGridãããªã¯ã¨ã¹ããåãåãã«ã¯ãå½ç¶ã¤ã³ã¿ã¼ãããããã¢ã¯ã»ã¹ã§ããããã«ããå¿
è¦ãããããä½ã®èãããªãã«éæ¾ãã¦ã¯ã»ãã¥ãªãã£çã«ä¸å®ã ãããããå®å¿ããSendGridã«ã¯WebHookã«ãã¸ã¿ã«ç½²åãã¤ããæ©è½ããããæ¬ç¨¿ã§ã¯ãã®ä½¿ãæ¹ãç´¹ä»ããã
ã¾ããMail Settings
ãããSigned Event Webhook Requests
ã¨ããé
ç®ã«å
¥ããç»é¢ã®ãã¿ã³ãã¯ãªãã¯ããã¨ãVerification Keyãçæãããé»åç½²åãæå¹ã«ãªããããã¯æã
(HERP)ã®ã·ã¹ãã ã§å®éã«éç¨ãã¦ããéµã§ããã
ãã®è¨å®ãããã¨ã以å¾ã®WebHookã«ã¯ãX-Twilio-Email-Event-Webhook-Signature
ã¨X-Twilio-Email-Event-Webhook-Timestamp
ã®äºã¤ã®HTTPãããã¼ãä»ä¸ããããã¢ããªã±ã¼ã·ã§ã³å´ã¯ãããã使ã£ã¦æ¤è¨¼ããã°ããã
ã¾ããé»åç½²åã«ä½¿ãã¢ã«ã´ãªãºã ãããã·ã¥é¢æ°ãã¡ãã»ã¼ã¸ãç½²åãå
¬ééµãæ´çããã
é¢ä¿ãæ´çããã¨ããã§ãå
¬å¼ã®ããã¥ã¡ã³ããåèã«å®è£
ãé²ãã¦ãããdocs.sendgrid.com
ã¾ãã¯å¿
è¦ãªã¢ã¸ã¥ã¼ã«ãã¤ã³ãã¼ããã¦ãããããããã¨ä¸¦ã¹ãããæã¯ããã¯ãHaskellã®é¢¨ç©è©©ã§ããã
import "asn1-encoding" Data.ASN1.BinaryEncoding (BER(..))
import "asn1-encoding" Data.ASN1.Encoding (decodeASN1)
import "asn1-types" Data.ASN1.Types (ASN1(..), fromASN1)
import "base" Data.Proxy
import "base" Data.Bifunctor (bimap, first)
import "base64" Data.ByteString.Base64 (decodeBase64)
import "bytestring" Data.ByteString qualified as B
import "bytestring" Data.ByteString.Lazy qualified as BL
import "cryptonite" Crypto.ECC (Curve_P256R1)
import "cryptonite" Crypto.Error (CryptoFailable(..))
import "cryptonite" Crypto.Hash.Algorithms (SHA256(..))
import "cryptonite" Crypto.PubKey.ECC.Types (CurveName(..))
import "cryptonite" Crypto.PubKey.ECDSA qualified as ECDSA
import "x509" Data.X509 (PubKey(..), PubKeyEC(..), SerializedPoint(..))
å
¬ééµãBase64ã¨ASN.1ã§ã¨ã³ã³ã¼ãããã¦ãããã¨ã¯ããã«ããããããã®ä¸èº«ã®ãã©ã¼ããããããã¥ã¡ã³ãã«æ¸ããã¦ããªãã£ããããå°ã
è¿·ããçãããå®é¨ãã¦ã¿ãã¨ããx509ããã±ã¼ã¸ã®PubKey
ã¨ãã¦ãã³ã¼ãã§ãããã¨ãããã£ãã
type PublicKey = ECDSA.PublicKey Curve_P256R1
curve :: Proxy Curve_P256R1
curve = Proxy
parsePublicKey :: B.ByteString -> Either String PublicKey
parsePublicKey b64 = do
raw <- first show $ decodeBase64 b64
asn1 <- first show $ decodeASN1 BER $ BL.fromStrict raw
pubkey <- bimap show fst $ fromASN1 asn1
SerializedPoint point <- case pubkey of
PubKeyEC (PubKeyEC_Named SEC_p256r1 bs) -> Right bs
_ -> Left $ "Unsupported public key: " <> show pubkey
case ECDSA.decodePublic curve point of
CryptoPassed a -> Right a
CryptoFailed err -> Left $ show err
ããã¾ã§æ¥ãã°ããã¨ã¯é¢æ°ãå¼ã³åºãã ããªã®ã§ç°¡åã ãæå·ãé»åç½²åã¯ãå®è£
ãééããã°ãã ã®ã©ã³ãã ãªæååã«ãªã£ã¦ãã¾ããããã©ã¤ãã©ãªã®ããããã¿ãæããããã
verify :: PublicKey -> B.ByteString -> B.ByteString -> B.ByteString -> Either String ()
verify pubKey timestamp payload signatureBase64 = do
signature <- either (const $ Left "Malformed signature") pure $ decodeBase64 signatureBase64
point <- case decodeASN1 BER $ BL.fromStrict signature of
Right [_, IntVal r, IntVal s, _] -> pure (r, s)
Right asn1 -> Left $ "failed to decode the signature: " <> show asn1
Left err -> Left $ show err
case ECDSA.signatureFromIntegers curve point of
CryptoPassed sig
| ECDSA.verify
curve
SHA256
pubKey
sig
$ timestamp <> payload
-> pure ()
| otherwise -> Left "Verification failed"
CryptoFailed _ -> Left "ECDSA.signatureFromIntegers failed"
WebHookã®è¨å®ç»é¢ã§ãTest Your Integrationããã¯ãªãã¯ããã¨éããã¦ãããã¼ã¿ã使ã£ã¦ãå®éã«æ¤è¨¼ãã¦ã¿ãã
_test_verify :: Either String ()
_test_verify = do
pub <- parsePublicKey "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERYcga9cTuvv0EbOFM0PO/KJjCgqYwtGar22uUyPQPwUbm+OtKXGNGIaHBvkgXBCbTxG4XQ4ddfDPgfMAcguUtg=="
verify pub
"1655455728"
"[{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"processed\",\"category\":[\"cat facts\"],\"sg_event_id\":\"cPeOfuc93o3vAEftXKt_zA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"deferred\",\"category\":[\"cat facts\"],\"sg_event_id\":\"zFYYFn__N8lr3be4TqKnVw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"400 try again later\",\"attempt\":\"5\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"delivered\",\"category\":[\"cat facts\"],\"sg_event_id\":\"rj8WvpmTWgE0z2MSd41KKg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"250 OK\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"open\",\"category\":[\"cat facts\"],\"sg_event_id\":\"uz1AaVpvYihASovU-M-Jrg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"click\",\"category\":[\"cat facts\"],\"sg_event_id\":\"O2bZwfxc-xm-9zmeVZX8HA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"bounce\",\"category\":[\"cat facts\"],\"sg_event_id\":\"vMLN27M1-TVv5XADvpW3Nw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"500 unknown recipient\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"dropped\",\"category\":[\"cat facts\"],\"sg_event_id\":\"u1u9xdYlTkoDkUZFZ_j_tg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"Bounced Address\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"spamreport\",\"category\":[\"cat facts\"],\"sg_event_id\":\"CjkStOi15hPQfWO58rH9dw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"QrCQiwv2pfkejKE5Zi1D0g==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"l67dvY6MIupGpuk1r9vIXw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_resubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"fo7PLpVCoCA9WDHovGQyyw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10}]\r\n"
"MEUCIQCBYJiC1zzZeM61EbekWSGMFgpRSzaQSA4zwV3vlMgf/wIgSrMZIIYTnx4dkqDK92re4WYhcM3xEKbLIKfmcu7Et0o="
_test_verify_fail :: Either String ()
_test_verify_fail = do
pub <- parsePublicKey "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERYcga9cTuvv0EbOFM0PO/KJjCgqYwtGar22uUyPQPwUbm+OtKXGNGIaHBvkgXBCbTxG4XQ4ddfDPgfMAcguUtg=="
verify pub
"1655455729"
"[{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"processed\",\"category\":[\"cat facts\"],\"sg_event_id\":\"cPeOfuc93o3vAEftXKt_zA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"deferred\",\"category\":[\"cat facts\"],\"sg_event_id\":\"zFYYFn__N8lr3be4TqKnVw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"400 try again later\",\"attempt\":\"5\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"delivered\",\"category\":[\"cat facts\"],\"sg_event_id\":\"rj8WvpmTWgE0z2MSd41KKg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"response\":\"250 OK\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"open\",\"category\":[\"cat facts\"],\"sg_event_id\":\"uz1AaVpvYihASovU-M-Jrg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"click\",\"category\":[\"cat facts\"],\"sg_event_id\":\"O2bZwfxc-xm-9zmeVZX8HA==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"bounce\",\"category\":[\"cat facts\"],\"sg_event_id\":\"vMLN27M1-TVv5XADvpW3Nw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"500 unknown recipient\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"dropped\",\"category\":[\"cat facts\"],\"sg_event_id\":\"u1u9xdYlTkoDkUZFZ_j_tg==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"reason\":\"Bounced Address\",\"status\":\"5.0.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"spamreport\",\"category\":[\"cat facts\"],\"sg_event_id\":\"CjkStOi15hPQfWO58rH9dw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"QrCQiwv2pfkejKE5Zi1D0g==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\"},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_unsubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"l67dvY6MIupGpuk1r9vIXw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10},\r\n{\"email\":\"[email protected]\",\"timestamp\":1655455223,\"smtp-id\":\"\\u003c14c5d75ce93.dfd.64b469@ismtpd-555\\u003e\",\"event\":\"group_resubscribe\",\"category\":[\"cat facts\"],\"sg_event_id\":\"fo7PLpVCoCA9WDHovGQyyw==\",\"sg_message_id\":\"14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0\",\"useragent\":\"Mozilla/4.0 (compatible; MSIE 6.1; Windows XP; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\",\"ip\":\"255.255.255.255\",\"url\":\"http://www.sendgrid.com/\",\"asm_group_id\":10}]\r\n"
"MEUCIQCBYJiC1zzZeM61EbekWSGMFgpRSzaQSA4zwV3vlMgf/wIgSrMZIIYTnx4dkqDK92re4WYhcM3xEKbLIKfmcu7Et0o="
ghci> _test_verify
Right ()
ghci> _test_verify_fail
Left "Verification failed"
SendGridããéããã¦ãããã¼ã¿ã¯éããæ¹ãããããã¼ã¿ã¯å¼¾ãããã¨ã確èªã§ããã
ã¾ã¨ã
ã¤ã³ã¿ã¼ããããµãã³ãã®ä½äººã¨ãã¦ãä¸æ£ãªæ
å ±ã«å¯¾ããè¦æã¯æ ã£ã¦ã¯ãããªããSendGridã¯ããªã¯ã¨ã¹ãã®å
容ã¨ã¿ã¤ã ã¹ã¿ã³ããECDSAã§ç½²åãããã¨ããã·ã³ãã«ãªä»çµã¿ããããããã§ãæ¯è¼çå°ãªãã³ã¼ãã§æ¤è¨¼æ©è½ãå®è£
ã§ããã
PRæ
WebHookã®éã¯ECDSAã«ãã£ã¦éãããã¦ããããæ¡ç¨ã«ããã¦HERPã®éã¯éããã¦ãããHERPã¯é ãè¯ãã¦ç©äºãæãéãããã人ãåéãã¦ãã¾ãã
github.com