وب مدرن، رابط کاربری و تجربهی وب · فصل ۵ — Error Model، REST، Streaming و Webhook · 52
قابلیت اطمینان و امنیت Webhook
میتواند «Webhook Reliability و Security» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
خلاصهٔ درس
میتواند «Webhook Reliability و Security» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
خروجی این درس
میتواند «Webhook Reliability و Security» را در یک feature محدود طراحی/پیادهسازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.
۱. صفحه اول: موفقیتی که ممکن است جعلی باشد
یک گیرنده Webhook میتواند 200 OK برگرداند، JSON معقولی در log ثبت کند و در عین حال از چند جهت مستقل اشتباه باشد. Payload ممکن است جعلی باشد. یک delivery معتبر و امضاشده ممکن است دوباره ارسال شود. یک رویداد کسبوکاری ممکن است در چند delivery تکرار شود. Handler ممکن است اثر کسبوکاری را پایدار کند و قبل از ثبت پایان پردازش crash کند. رویداد جدیدتر نیز ممکن است زودتر از رویداد قدیمیتر برسد. هیچکدام از این خطاها با سبز بودن status code رد نمیشوند.
در این درس مرز Webhook را با پنج قرارداد جدا میسازیم: اصالت پیام، مدیریت secret، کنترل replay و duplicate، تفکیک acknowledgement از processing و ordering/recovery. Lab از یک fixture محلی و قطعی برای provider صورتحساب استفاده میکند و به سرویس خارجی یا شبکه خروجی وابسته نیست. artifact نهایی شما Webhook Authenticity, Replay & Idempotency Record v1 است.
قاعده اصلی محدود و دقیق است: اصالت provider را روی همان بایتهایی که قرارداد provider مشخص کرده بررسی کن؛ سپس پیام تأییدشده را با سیاست صریح duplicate، durability و ordering پردازش کن. مثال GitHub را به استاندارد عمومی همه providerها تبدیل نکن.
۲. مسیرهای یادگیری
مسیر پایه: پنج قرارداد را بهصورت پنج ردیف مستقل نگه دار. ابتدا ثابت کن signature روی raw body درست بررسی میشود. سپس delivery ID را از business event ID جدا کن. بعد یک side effect را duplicate-safe کن. در پایان سراغ acknowledgement و رویدادهای خارج از ترتیب برو.
مسیر ترمیم: از همان evidence شکستخورده شروع کن. اگر payload معتبر بعد از JSON parsing رد میشود، مرز raw bytes را بررسی کن. اگر duplicate دوبار state را تغییر میدهد، identity مربوط به idempotency را بررسی کن. اگر crash باعث اثر تکراری یا اثر گمشده میشود، ترتیب durable writeها را بررسی کن. اگر event قدیمی state جدید را عقب میبرد، rule مربوط به version و reconciliation را اصلاح کن.
مسیر باتجربه: میتوانی syntax اولیه HMAC را سریع رد کنی، اما mutation و evidence record حذف نمیشوند. باید فرضهای provider-specific، مرز durability و محدودیتهای اثباتنشده در fixture محلی را دقیق بنویسی.
۳. مدل ذهنی: یک request، چند identity
مدل مفید:
raw bytes + provider signature + delivery ID → authenticity gate → parse → business event ID/version → duplicate-safe durable effect → delivery completion mark → transport acknowledgement
delivery ID یک delivery attempt یا رکورد ارسال provider را مشخص میکند. business event ID رویداد معنایی را مشخص میکند که نباید اثر آن دوبار اعمال شود. business version یا marker دامنهای مشابه کمک میکند بفهمیم یک event دیررس stale است، current است یا وجود gap را نشان میدهد. providerها در نام و جزئیات یکسان نیستند.
Authenticity فقط میگوید: «آیا این پیام با قرارداد اعتبارسنجی provider و secret تنظیمشده سازگار است؟» پاسخ آن این نیست که «آیا قبلاً این رویداد را پردازش کردهام؟» HMAC یک سازوکار message authentication است، نه بانک اطلاعات replay. بنابراین duplicate control به state جدا نیاز دارد.
Transport acknowledgement هم سؤال دیگری را جواب میدهد: «sender برای این delivery چه پاسخی میبیند؟» این پاسخ بهتنهایی ثابت نمیکند کار کسبوکاری در downstream پایدار شده است.
۴. لایه پایه: raw bytes، HMAC و secret
در مثال GitHub-style، header امضا میتواند X-Hub-Signature-256 باشد. مقدار مورد انتظار از secret مشترک و محتوای payload ساخته میشود. ورودی verification باید همان بایتهایی باشد که provider طبق قرارداد امضا کرده است. اگر middleware ابتدا JSON را parse و سپس دوباره serialize کند، فاصلهها، escapeها یا encoding میتوانند عوض شوند و signature معتبر fail شود.
ترتیب امن این است: raw request body را نگه دار؛ signature header را بخوان؛ MAC مورد انتظار را با secret پیکربندیشده بساز؛ با comparison مناسب و constant-time مقایسه کن؛ در mismatch درخواست را رد کن؛ فقط بعد از آن body را بهعنوان ورودی قابل اعتماد parse کن.
secret نباید literal داخل source باشد. Rotation نیز صرفاً «عوضکردن رشته» نیست. در یک طراحی محدود، اگر semantics provider اجازه دهد، در window کنترلشده current secret و previous secret را برای verification میپذیریم و مشخص میکنیم کدام generation match شده، بدون اینکه خود secret را log کنیم.
نام header و algorithm مثال GitHub را به همه providerها تعمیم نده. اصل معماری این است که قرارداد verification همان provider پیاده شود.
۵. توضیح عمیق: duplicate safety و پنجره crash
provider ممکن است بعد از timeout، failure شبکه یا عملیات redelivery دوباره پیام را ارسال کند. حتی اگر request دوم کاملاً authentic باشد، اجرای دوباره business effect میتواند غلط باشد. بنابراین receiver به idempotency در مرز کسبوکار نیاز دارد.
fixture محلی هم delivery completion و هم business event identity اعمالشده را نگه میدارد. فرض کن payment event با شناسه evt-200 در delivery delivery-A برسد. handler اصالت را بررسی میکند، evt-200 را اعمال میکند و قبل از ثبت completion برای delivery-A crash میکند. provider دوباره delivery-A را میفرستد. اگر dedupe فقط بر اساس «deliveryهای کاملشده» باشد، attempt دوم completion را نمیبیند و ممکن است پرداخت را دوباره اعمال کند.
flow ترمیمشده: verification؛ parse؛ ثبت/اجرای durable business effect زیر event-level idempotency key؛ سپس ثبت delivery completion؛ سپس acknowledgement موفق. اگر بعد از effect ولی قبل از delivery mark crash رخ دهد، redelivery میبیند business event قبلاً اعمال شده، effect دوم ایجاد نمیکند، completion را ترمیم میکند و سپس موفق پاسخ میدهد.
در سیستم واقعی این invariant میتواند با database transaction، unique constraint، inbox table، idempotent command، outbox/inbox یا ابزار provider-specific پیاده شود. این درس یک ابزار را برنده همیشگی اعلام نمیکند؛ invariant قابل مشاهده میخواهد: در crash window آزمایششده، یک semantic event نباید protected effect را دوبار ایجاد کند.
۶. مثال کامل: وضعیت invoice
فرض کن payload زیر برای inv-42 میرسد:
{"event_id":"evt-102","type":"invoice.paid","invoice_id":"inv-42","version":2,"amount":1200}
request دارای provider signature و delivery ID برابر del-9002 است. receiver ابتدا signature را در برابر raw string دقیق بررسی میکند. بعد از verification، JSON parse میشود. اگر evt-102 قبلاً در applied-event record وجود نداشته باشد، version 2 با version فعلی invoice مقایسه و state جدید اعمال میشود. سپس event بهعنوان applied، delivery بهعنوان complete و در پایان response موفق ثبت میشود.
حالا crash را بعد از durable شدن invoice update و event idempotency record ولی قبل از completion del-9002 تزریق کن. در redelivery، authenticity دوباره pass میشود. delivery هنوز incomplete است اما event record میگوید evt-102 قبلاً اثر گذاشته است. receiver دوباره charge یا transition انجام نمیدهد؛ فقط completion delivery را کامل میکند و acknowledge میکند.
در این مثال سه واقعیت از هم جدا هستند: پیام authentic است؛ semantic event قبلاً applied شده؛ این delivery attempt اکنون complete است. همین جداسازی recovery را testable میکند.
۷. failure modeها و mutationها
Mutation A — verification روی body دوباره serializeشده. incoming JSON را parse کن و با JSON.stringify دوباره بساز. payloadی که فقط whitespace متفاوت دارد byte sequence دیگری میسازد. evidence مورد انتظار: delivery معتبر fail میشود. repair: verification را روی raw body قبل از parse انجام بده.
Mutation B — حذف authenticity. هر body دارای event ID معقول را قبول کن. evidence: forged event میتواند state را تغییر دهد. repair: provider-defined signature verification باید قبل از trust شدن payload اجرا شود.
Mutation C — dedupe فقط با delivery ID. فقط deliveryهای completed را نگه دار و بعد از business effect ولی قبل از delivery mark crash کن. evidence: redelivery میتواند side effect را دوباره اجرا کند. repair: business-event idempotency در effect boundary.
Mutation D — ثبت completion قبل از effect. اگر process بعد از complete mark ولی قبل از effect crash کند، redelivery بهعنوان «قبلاً پردازششده» حذف میشود و effect گم میشود. repair: effect باید قبل از success/completion boundary durable شود یا transaction/state machine invariant را حفظ کند.
Mutation E — اعتماد به arrival order. version 3 را اعمال کن و بعد version 2 دیررس را بدون check اعمال کن. evidence: state به عقب برمیگردد. repair: domain version/sequence را مقایسه و stale/gap را برای reconciliation ثبت کن.
۸. پل افراد باتجربه: قرارداد acknowledgement
جمله «سریع 2xx برگردان» بهتنهایی قرارداد نیست. اگر endpoint قبل از هر durable handoff موفق پاسخ دهد، failure process میتواند کار را از بین ببرد در حالی که provider تصور میکند delivery موفق بوده است. اگر endpoint تمام downstream work کند و دیر پاسخ دهد، provider ممکن است timeout کند و redelivery فشار duplicate را بیشتر کند.
یک طراحی رایج این است که receiver ابتدا authenticate کند، envelope حداقلی را validate کند، event را بهصورت durable و idempotent queue/record کند و بعد acknowledge کند. business processing میتواند async ادامه یابد. طراحی دیگر یک state transition کوچک را synchronously durable میکند. معیار، success condition صریح و testشده است.
در fixture محلی، success فقط وقتی برمیگردد که business effect/idempotency state و delivery completion invariant محلی را برآورده کنند. این نتیجه درباره رفتار distributed production ادعای عمومی نمیکند.
ordering، gap و reconciliation
بهطور پیشفرض فرض نکن webhook globally exactly-once یا globally ordered است. event میتواند دیر برسد، duplicate شود یا وقتی نسخه بعدی رسیده هنوز missing باشد. recovery باید domain-aware باشد.
در fixture invoice، هر event version دارد. اگر current version برابر 3 است و version 2 دیرتر برسد، receiver آن را stale ثبت میکند و state را عقب نمیبرد. اگر current version برابر 1 است و version 3 برسد، طبق policy میتواند version 3 را اعمال یا stage کند، اما باید reconciliation لازم را ثبت کند چون version 2 دیده نشده است. سیستم واقعی ممکن است provider state را fetch کند، event log را query کند یا job reconciliation بسازد؛ lab به شبکه خارجی وابسته نیست و فقط observable gap را حفظ میکند.
delivery ID و business version را یکی نکن. delivery جدید میتواند همان business event قبلی را حمل کند و یک object نیز میتواند چند event معتبر با versionهای متفاوت داشته باشد.
۹. گزینهها و trade-offها
Unique constraint / inbox table: وقتی relational store مرز effect را کنترل میکند، قوی و قابل بررسی است؛ ولی transaction و schema باید درست طراحی شوند.
Distributed cache dedupe: سریع است اما expiry/eviction میتواند duplicate window را دوباره باز کند. برای risk window محدود ممکن است کافی باشد ولی جایگزین خودکار durable idempotency نیست.
Queue-first acknowledgement: وقتی broker قابل اعتماد durability boundary است مفید است. receiver همچنان باید قبل از trust/enqueue authenticate کند و consumer پاییندستی idempotent باشد.
Provider reconciliation API: برای gap و missed event مفید است اما provider-specific است و ممکن است credential/network بخواهد. lab محلی عمداً به آن وابسته نیست.
Timestamp freshness: اگر provider timestamp را امضا و validation rule را مشخص کند میتواند replay window را محدود کند، اما universal replay prevention نیست.
۱۰. تمرین هدایتشده و مستقل
قبل از کدنویسی state را پیشبینی کن: بعد از authentic version-2 event چه رکوردهایی باید وجود داشته باشد؟ بعد از تکرار همان delivery چه چیزی نباید تغییر کند؟ بعد از crash بین durable effect و delivery completion، redelivery باید به چه state نهایی همگرا شود؟
در implementation، fixture را طوری repair کن که baseline authentic pass شود، forged signature رد شود، duplicate delivery effect را تکرار نکند، crash/redelivery فقط یک effect نهایی بسازد و stale version state را عقب نبرد.
Transfer مستقل: همین contract را برای provider حملونقل با event shipment.updated و header/payload متفاوت بازطراحی کن. separation معماری را حفظ کن: authenticity مخصوص provider، event/delivery identity، durable effect، acknowledgement policy و ordering/gap reconciliation. header مثال GitHub را بدون قرارداد صریح provider جدید کپی نکن.
۱۱. evidence قابل قبول
evidence record باید بیشتر از «testها pass شدند» باشد. implementation artifact، observation دقیق هر contract row، inference حاصل از آن، حداقل سه mutation result، repair result، changed-context transfer و یک residual limitation را ثبت کن.
ردیف authenticity خوب میگوید: «با raw body دقیق، expected HMAC match شد و processing ادامه یافت؛ بعد از reserialization، همان provider signature دیگر match نشد.» inference محدود است: «این fixture امضای provider-style تنظیمشده را روی raw input بررسی میکند.» این عبارت نمیگوید همه providerها همین header را دارند یا HMAC replay را حل میکند.
ردیف crash recovery خوب effect count قبل از crash، پاسخ fail، redelivery و effect count نهایی را ثبت میکند. inference: «در این fixture، event-level idempotency در crash window تزریقشده از effect تکراری جلوگیری میکند.» limitation: مدل local/in-memory درباره datastore، queue، proxy یا retry schedule واقعی evidence نمیدهد.
۱۲. assessment و transfer
Retrieval: تفاوت authenticity و replay protection را توضیح بده. Application: raw-body verification و duplicate-safe effect boundary را پیاده کن. Diagnosis: توضیح بده چرا crash بین effect و delivery completion خطرناک است. Decision: delivery-ID dedupe و business-event idempotency را در سناریوی billing مقایسه کن. Transfer: همین contract را برای provider دیگری با signature header و event schema متفاوت بازطراحی کن.
Evidence blocking برای authenticity، duplicate/replay، crash recovery و ordering/reconciliation باید executable باشد. نگاهکردن به UI یا دریافت 2xx بهتنهایی کافی نیست.
۱۳. منابع و مرز freshness
source boundary منجمد شامل راهنمای فعلی GitHub برای validation webhook، مستندات delivery/redelivery repository webhook و RFC 2104 برای HMAC است. بازبینی ۲۰۲۶-۰۹-۱۳ نشان داد claimهای منجمد با این منابع رسمی سازگارند. GitHub فقط مثال bounded provider است؛ RFC 2104 سازوکار HMAC را تعریف میکند و state کاربردی replay را فراهم نمیکند.
راهنمای provider باید حداکثر تا ۲۰۲۶-۱۰-۱۷ یا زودتر در صورت تغییر signature/delivery/API دوباره review شود. جداسازی معماری ممکن است پایدار بماند، اما claim provider-specific باید دوباره از منبع رسمی تأیید شود.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
WEB-008-S1 retrieval
پنج خانوادهٔ Claim درس WEB-008 را بیان کنید، قرارداد authenticity-first وبهوک را توضیح دهید و یک Non-goal صریح را بدون ادعای anti-replay خودکار، دوام کسبوکاری صرفاً از 2xx یا exactly-once/global ordering نام ببرید.
WEB-008-S2 application
Fixture محدود وبهوک را طوری Repair یا Implement کنید که اعتبارسنجی امضا روی raw body دقیق، چرخش secret پیکربندیشده، duplicate-safety در سطح delivery و business event، مرز acknowledgement و ordering recovery با Evidence Record سازگار باشند.
WEB-008-S3 diagnosis
یک خرابی عمدی در raw-body authenticity، secret handling، duplicate/replay، زمانبندی acknowledgement یا version-gap recovery را پیشبینی، اجرا، Diagnose و Repair کنید.
WEB-008-S4 decision
مکانیزم انتخابشده برای reliability وبهوک را در برابر یک shortcut محتمل دفاع کنید؛ ابتدا guarantee لازم و سپس عدمقطعیت باقیماندهٔ provider یا deployment را بیان کنید.
WEB-008-S5 transfer
Changed-context مربوط به webhook رویداد shipment.updated را مستقل طراحی کنید؛ provider signature contract، delivery identity، business-event identity، acknowledgement boundary، secret rotation، ordering recovery و یک residual limitation را مشخص کنید.
ملاکهای ارزیابی (rubric)
Critical dimensions:
- AUTHENTICITY — Verify the provider-defined signature over the exact required bytes before trusting the payload.
- SECRET_HANDLING — Keep webhook secrets out of source and support controlled rotation where provider semantics allow it.
- REPLAY_DUPLICATE — Track a provider delivery/event identity and make processing idempotent or otherwise duplicate-safe.
- ACK_PROCESSING — Separate transport acknowledgement from durable business processing and define retry/redelivery behavior.
- ORDERING_RECOVERY — Do not assume global ordering; make reconciliation possible when events arrive late, duplicate or missing.
Blocking assessments: S2, S3 and S5. S1 and S4 are non-blocking but still require explicit scope discipline. Evidence must separate observed behavior from inference, verify provider-specific authenticity over the required exact bytes, keep secret handling configuration-bound, distinguish transport acknowledgement from durable processing, prove duplicate-safe convergence, and keep ordering/version-gap recovery explicit. HMAC authenticity is not represented as anti-replay; 2xx is not represented as durable business completion; exactly-once or global ordering is not claimed. Publisher-runtime learner execution remains deferred to the pinned runtime. Internal synthetic oracle results are not represented as publisher-runtime observation. Browser/assistive-technology or real-provider network evidence is not claimed.
منابع و تازگی
مطالعهٔ موارد (case studies)
تصاویر درس
فایلهای آزمایشگاه
گیرنده محلی Webhook صورتحساب را repair کنید. authenticity مخصوص provider، delivery identity، business-event idempotency، acknowledgement و ordering/reconciliation را جدا نگه دارید. outbound network اضافه نکنید. evidence مشاهدهشده را در artifact ثبت کنید. starter عمداً خراب است.
رسانه و اسلایدهای درس
فایلهای آموزشی (دانلود)