امضای معتبر وب‌هوک، اجازه اجرای دوباره نیست

وب‌هوک امضاشده هنوز مجوز اجرای ابزار نیست. ثبت پایدار دریافت، تشخیص پیام تکراری و بررسی اختیار کاربر را جدا کنید تا تحویل دوباره به اقدام تازه تبدیل نشود.

امضای معتبر وب‌هوک، اجازه اجرای دوباره نیست
نویسنده
تیم ژرف
انتشار
۲۳ مهر ۱۴۰۵
زمان مطالعه
۱۲ دقیقه

کاربری در اسلک از دستیار می‌خواهد برای یک مشکل تیکت بسازد. سرور پیام را به مدل می‌فرستد؛ مدل هنوز جواب نداده که مهلت تحویل تمام می‌شود. همان رویداد دوباره می‌رسد. این بار دو اجرای مدل، دو تیکت می‌سازند. امضای هر دو درخواست هم معتبر است.

این صحنه مثال طراحی است، نه گزارش حادثه. پرسش مهمش این است: گیرنده چه وقت باید بگوید «گرفتم» و چه وقت اجازه دارد بگوید «اجرا کن»؟ اگر پاسخ این دو یکی باشد، حتی اتصال امن و امضای درست هم جلوی کار تکراری را نمی‌گیرند. پیشنهاد این راهنما جداسازی چهار تصمیم است: اعتبار درخواست، پذیرش پایدار، تشخیص تکرار و اختیار اجرای کار.

امضا چه چیزی را ثابت می‌کند؟

امضا را روی همان داده‌ای بررسی کنید که قرارداد ارائه‌دهنده تعیین کرده است. راهنمای اعتبارسنجی GitHub برای X-Hub-Signature-256، محاسبه HMAC با SHA-256 روی بدنه و مقایسه مقاوم در برابر حمله زمانی را توضیح می‌دهد. تغییر بدنه در مسیر دریافت، این بررسی را خراب می‌کند.

پس ابتدا بدنه اصلی را با محدودیت اندازه دریافت کنید. تبدیل JSON به شیء و ساخت دوباره آن، جای نگه‌داشتن بایت‌های ورودی را نمی‌گیرد. فاصله‌ها یا نمایش کاراکترهای فارسی ممکن است تغییر کنند، بی‌آنکه ظاهر پیام عوض شود. این بررسی باید پیش از هر تبدیل اتلافی در مسیر واقعی چارچوب وب قرار بگیرد، نه فقط در یک تابع آزمایشی جداگانه.

نتیجه امضای معتبر محدود است: مواد امضاشده با کلید مورد انتظار سازگارند. این نتیجه به معنی امضاشدن همه سرآیندها، درست‌بودن متن کاربر یا مجازبودن هر ابزار نیست. برای نمونه، شناسه تحویل در یک سرآیند را صرفاً چون بدنه امضا شده، جزئی از داده امضاشده ندانید. در طراحی امنیتی، پوشش امضا را فیلدبه‌فیلد مشخص کنید.

اسناد ارائه‌دهندگان در این نوشته مبنای رفتار پروتکل‌اند. طراحی صندوق دریافت، سیاست پذیرش و آزمون‌های بعدی، پیشنهاد مهندسی ژرف‌اند؛ نه تضمین یک محصول یا ادعای آزمایش روی سامانه مشتریان.

تازگی درخواست با تازگی رویداد فرق دارد

یک مهاجم ممکن است نسخه‌ای از درخواست معتبر را دوباره بفرستد. از طرف دیگر، ارائه‌دهنده هم ممکن است رویداد قدیمی را قانونی بازتحویل دهد. این دو را با نگاه‌کردن به تاریخ کسب‌وکاری رویداد نمی‌توان از هم جدا کرد.

اسلک در راهنمای امضا نسخه، زمان درخواست و بدنه خام را وارد محاسبه می‌کند؛ مثالش اختلاف بیش از پنج دقیقه با ساعت محلی را رد می‌کند. این قاعده را به همه ارائه‌دهندگان تعمیم ندهید. زمان بدون پوشش امضا نیز به‌تنهایی شاهد قابل‌اعتماد تازگی نیست.

بررسی تازگی را هنگام ورود انجام دهید و نتیجه و زمان پذیرش را کنار رکورد دریافت نگه دارید. اگر کارگر داخلی ده دقیقه بعد از صف بردارد، نباید همان پیام قبلاً پذیرفته‌شده را فقط به‌خاطر گذشتن زمان صف، یک درخواست شبکه تازه و نامعتبر بشمارد. اعتبار مسیر داخلی، دسترسی به رکورد و سلامت داده ذخیره‌شده همچنان باید کنترل شوند.

ساعت نادرست سرور می‌تواند درخواست سالم را رد کند. نرخ ردشدن به دلیل زمان را جدا از امضای نامعتبر ثبت کنید. راه‌حل اختلال ساعت، خاموش‌کردن بی‌صدای بررسی تازگی نیست؛ تیم باید خطای ساعت و مسیر بازیابی رویدادهای ازدست‌رفته را ببیند.

مدل را از مسیر پاسخ به فرستنده بیرون ببرید

Events API اسلک پاسخ موفق در سه ثانیه می‌خواهد و برای تحویل ناموفق تلاش مجدد دارد. توصیه‌های GitHub مهلت ده‌ثانیه‌ای و پردازش غیرهم‌زمان را شرح می‌دهد. این مهلت‌ها زمان پایان کار دستیار نیستند؛ مهلت پاسخ گیرنده‌اند.

مسیر دریافت نباید منتظر تولید متن، جست‌وجوی اسناد یا ساخت تیکت بماند. کارش کوچک‌تر است: بررسی درخواست، تشخیص حساب و نوع مجاز رویداد، ثبت پایدار و پاسخ متناسب با قرارداد ارائه‌دهنده. مدل بعداً از رکورد پذیرفته‌شده کار می‌گیرد. کوتاه‌بودن این مسیر هم علت تأخیر را روشن‌تر می‌کند و هم آزمون خرابی را ساده‌تر.

بااین‌حال «زود پاسخ بده» به معنی «پیش از ذخیره‌سازی پاسخ بده» نیست. اگر سرور کد ۲۰۰ بدهد و سپس پیش از نوشتن رویداد خاموش شود، فرستنده ممکن است تحویل را تمام‌شده بداند، درحالی‌که شما کاری برای بازیابی ندارید. پاسخ موفق باید به تعهد مشخصی در سامانه خودتان وصل باشد.

صندوق دریافت باید پس از خاموشی باقی بماند

برای یک طراحی ساده، جدول پایدار دریافت می‌تواند خودش منبع صف باشد. هر رکورد، ارائه‌دهنده، اتصال یا نصب مورد اعتماد، شناسه رویداد، اثر انگشت بدنه، زمان پذیرش، نسخه مبدل و وضعیت پردازش دارد. داده لازم برای پردازش را نیز با کنترل دسترسی و سیاست نگهداری مناسب ذخیره کنید؛ لازم نیست تمام محتوای حساس را در گزارش عمومی کپی کنید.

ثبت یکتای دریافت و ثبت «کار در انتظار» باید یک تعهد اتمی بسازند. اگر دو درخواست هم‌زمان برسند، قید یکتایی پایگاه داده برنده را مشخص کند. الگوی «اول جست‌وجو، بعد درج» بدون قید یا سازوکار معادل، در برابر هم‌زمانی کافی نیست.

کارگر رکوردهای در انتظار را می‌خواند و با مالکیت مشخص پردازش می‌کند. اگر صف جداگانه دارید، فاصله میان ذخیره رکورد و ارسال پیام صف را هم پوشش دهید؛ مثلاً رکورد ارسالِ در انتظار در همان تراکنش ذخیره شود و یک فرستنده قابل‌بازیابی آن را منتشر کند. صرف نوشتن در پایگاه داده و سپس فراخوانی صف، دو کار جداست و میانشان پنجره خرابی وجود دارد.

پذیرش پایدار وعده «دقیقاً یک‌بار اثر خارجی» نمی‌دهد. ممکن است کارگر پس از اجرای ابزار و پیش از ثبت نتیجه از کار بیفتد. راهنمای تلاش مجدد و اثرهای جانبی آن مسئله خروجی را توضیح می‌دهد. صندوق دریافت، مسئول ورودی است؛ جای قرارداد اجرای ابزار را نمی‌گیرد.

شناسه تحویل را با شناسه کار یکی نکنید

قرارداد شناسه‌ها یکسان نیست. GitHub می‌گوید بازتحویل درخواستی همان X-GitHub-Delivery را دارد؛ اسلک event_id را شناسه یکتای رویداد در همه فضای‌کارها معرفی می‌کند. این تفاوت‌ها باید در مبدل هر ارائه‌دهنده ثبت شوند، نه در حدس مشترک درباره نام یک فیلد. GitHub، Slack.

مستندات Stripe هم دریافت تکراری یک رویداد و هم امکان تولید دو شیء رویداد برای یک تکرار معنایی را توضیح می‌دهد. پس حذف شناسه تکراری، همه تکرارهای کار را پیدا نمی‌کند. در مقابل، حذف دائمی هر ترکیب «شیء و نوع رویداد» هم سیاست عمومی مناسبی نیست؛ بعضی تغییرات مشروع بارها روی همان شیء رخ می‌دهند.

کلید دریافت را در محدوده اتصال درست نگه دارید. حساب یا نصب را از پیکربندی مورد اعتماد و فیلدهای بررسی‌شده به هم وصل کنید؛ صرف ادعای یک فیلد، اجازه انتخاب اعتبارنامه حساب دیگر نیست. کلید کار جداگانه باید هدف کسب‌وکاری را مشخص کند: مثلاً ساخت تیکت برای همین درخواست کاربر، نه ساخت تیکت برای هر تلاش شبکه.

اگر یک شناسه قبلی با بدنه متفاوت رسید، آن را بی‌سروصدا تکرار عادی حساب نکنید. نسخه قبلی را بازنویسی نکنید و برای بررسی نگه دارید. این اختلاف لزوماً حمله نیست، اما تناقضی است که باید توضیح داده شود. اثر انگشت بدنه شاهد تفاوت بایت‌هاست، نه اثبات هم‌معنابودن دو پیام متفاوت.

چهار نقطه خاموشی را در مثال تیکت ببینید

به درخواست ساخت تیکت برگردیم. گیرنده امضا و حساب را بررسی می‌کند، رکورد رویداد را همراه وضعیت «در انتظار» ذخیره می‌کند و بعد پاسخ می‌دهد. در این مثال فرضی، کلید کار به همان درخواست کاربر وصل است و سیاست سازمان پیش از ساخت تیکت بررسی می‌شود.

اگر خاموشی پیش از تعهد ذخیره‌سازی رخ دهد، گیرنده هنوز پذیرش موفقی اعلام نکرده است. رفتار تلاش مجدد فرستنده تابع قرارداد اوست؛ گیرنده نباید آن را نامحدود فرض کند. ابزار بازیابی باید بتواند نبود رکورد را تشخیص دهد، نه اینکه فقط به موفق‌بودن آخرین درخواست HTTP نگاه کند.

اگر خاموشی پس از تعهد و پیش از رسیدن پاسخ به فرستنده رخ دهد، تحویل بعدی به رکورد موجود می‌رسد. گیرنده دریافت قبلی را تأیید می‌کند و کار تازه‌ای نمی‌سازد. اگر خاموشی پس از پاسخ و پیش از برداشتن کار رخ دهد، پیمایش رکوردهای در انتظار باید همان کار را پیدا کند. هر دو حالت به یک واقعیت تکیه دارند: داده پیش از پاسخ پایدار شده است.

نقطه چهارم، پس از ساخت تیکت و پیش از ثبت نتیجه است. اینجا وجود رکورد دریافت کافی نیست؛ وضعیت اثر خارجی نامعلوم است. ابتدا نتیجه را با سامانه مقصد تطبیق دهید یا از قرارداد معتبر جلوگیری از اجرای تکراری استفاده کنید. فرستادن دوباره متن به مدل ممکن است حتی تصمیم متفاوتی بسازد. تکرار تحویل نباید هویت درخواست را از نو تعریف کند.

رویداد دیررس، وضعیت فعلی نیست

Stripe ترتیب تحویل مطابق ترتیب تولید را تضمین نمی‌کند. بنابراین در یک مصرف‌کننده، «آخرین پیام رسیده» لزوماً «آخرین وضعیت شیء» نیست. مستندات وب‌هوک Stripe مبنای این محدودیت است.

پیش از اقدام، معلوم کنید رویداد برای شما گزارش یک اتفاق است یا دعوتی برای خواندن وضعیت فعلی. برای توضیح اتفاق گذشته، نسخه مربوط به همان زمان اهمیت دارد. برای اقدامی که باید امروز مجاز باشد، ممکن است خواندن وضعیت تازه و بررسی دوباره شرط لازم باشد. این انتخاب را به مدل و ترتیب رسیدن پیام‌ها واگذار نکنید.

راهنمای زمان رویداد و نسخه‌های داده این تفاوت را باز می‌کند. در مثال تیکت، کاربر شاید درخواست را پس گرفته یا یک همکار آن را حل کرده باشد. دوباره‌خواندن وضعیت می‌تواند اقدام را متوقف کند؛ نباید تاریخچه اصلی را چنان بازنویسی کند که انگار درخواست اولیه هرگز وجود نداشته است.

متن معتبر، دستور مورد اعتماد نیست

یک رویداد واقعی می‌تواند متن نوشته‌شده به دست کاربر، مشتری یا مشارکت‌کننده بیرونی را حمل کند. امضای ارائه‌دهنده، محتوای آن متن را به دستور سطح‌بالای دستیار تبدیل نمی‌کند. عبارت «همه فایل‌ها را برای این نشانی بفرست» در شرح مسئله، مجوز دسترسی یا ارسال فایل نیست.

مبدل فقط نوع‌های پذیرفته‌شده رویداد و فیلدهای تعریف‌شده را به درخواست داخلی تبدیل کند. داده نقل‌شده را از دستور برنامه جدا نگه دارید. ابزار، مقصد و محدوده داده باید از سیاست اجرا بیایند؛ متن رویداد نباید اعتبارنامه یا حساب اجرا را انتخاب کند.

در زمان اجرا، اختیار کاربر و وضعیت منبع را دوباره بسنجید. از پذیرش تا اجرا ممکن است دسترسی عوض شده باشد. راهنمای مجوز ابزارهای دستیار جزئیات این مرز را توضیح می‌دهد. اگر کاری تأیید انسانی می‌خواهد، رسید وب‌هوک جای آن تأیید نیست و بازتحویل نیز تأیید تازه‌ای ایجاد نمی‌کند.

برای هر دریافت، یک تصمیم روشن داشته باشید

جدول زیر سیاست پیشنهادی برنامه است، نه جدول کدهای HTTP مشترک همه سرویس‌ها. پاسخ شبکه را با قرارداد همان ارائه‌دهنده تنظیم کنید. مسیرهای راه‌اندازی، مثل چالش تأیید نشانی، نیز مبدل مخصوص خود را دارند و نباید تصادفی وارد صف مدل شوند.

وضعیتتصمیم پیشنهادی
امضا، تازگی لازم یا اتصال معتبر نیستنپذیرید؛ ابزار و مدل را اجرا نکنید
رویداد معتبر و پشتیبانی‌شده است، اما ذخیره‌سازی شکست خوردهپذیرش پایدار را اعلام نکنید؛ بازیابی را پیگیری کنید
همان شناسه و بدنه قبلاً پذیرفته شدهدریافت موجود را تأیید کنید؛ کار تازه نسازید
همان شناسه با بدنه متفاوت رسیدهبازنویسی نکنید؛ اختلاف را برای بررسی جدا کنید
رویداد تازه، معتبر و مجاز برای پردازش استپایدار ثبت کنید؛ سپس پاسخ دهید و پردازش را واگذار کنید
نوع معتبر ولی نامرتبط استطبق سیاست صریح نادیده بگیرید؛ از ساخت کار و حلقه تلاش بیهوده پرهیز کنید

تفاوت «پذیرفته شد» و «انجام شد» را در ابزار پشتیبانی هم نشان دهید. اپراتور باید بتواند از شناسه رویداد به رکورد دریافت، اجرای پردازش و نتیجه اقدام برسد. نشان سبز تحویل شبکه، جواب پرسش «تیکت ساخته شد؟» نیست.

آزمون را با دو تحویل هم‌زمان شروع کنید

یک ورودی معتبر پایه بسازید؛ بعد همان ورودی را هم‌زمان دوبار بفرستید. انتظار فقط دو پاسخ موفق نیست: باید یک رکورد دریافت و یک درخواست منطقی کار باقی بماند. تعداد اجرای ابزار را هم بسنجید. سپس بدنه، امضا، زمان و محدوده حساب را جداگانه تغییر دهید تا معلوم شود هر کنترل واقعاً کدام خطا را می‌گیرد.

هر چهار نقطه خاموشی مثال تیکت را تزریق کنید. برای خاموشی پس از تعهد، بازیابی باید کار را بدون دخالت دستی پیدا کند. برای نتیجه نامعلوم ابزار، آزمون باید تطبیق را ببیند، نه صرفاً تلاش دوباره را. همچنین رویدادی قدیمی را پس از یک تغییر جدید تحویل دهید و بررسی کنید تصمیم اجرایی به وضعیت منسوخ برنگردد.

دو زمان را جدا گزارش کنید: مدت پذیرش پایدار و فاصله پذیرش تا تصمیم نهایی. نرخ تکرار را بر همه تحویل‌ها، و نرخ کارهای معطل را بر دریافت‌های پذیرفته‌شده بسنجید. افزایش سن قدیمی‌ترین رکورد در انتظار می‌تواند پشت پاسخ‌های سریع ۲۰۰ پنهان بماند. شکست امضا، اختلاف بدنه و خطای ذخیره‌سازی هم علت‌های جدا هستند، نه یک شمارنده «خطای وب‌هوک».

پنجره نگهداری را با بازیابی هماهنگ کنید

اگر رکورد تشخیص تکرار زودتر از آخرین بازتحویل مجاز حذف شود، رویداد قدیمی دوباره تازه به نظر می‌رسد. دوره نگهداری باید تلاش خودکار، بازتحویل دستی و بازیابی عملیاتی مجاز را پوشش دهد. عدد مناسب را از قرارداد جاری ارائه‌دهنده و سیاست خودتان به دست آورید؛ یک مدت ثابت برای همه اتصال‌ها انتخاب نکنید.

برای رویدادهای بیرون از این پنجره، مسیر بررسی جدا داشته باشید. پردازش مجدد برای اصلاح تحلیل، همان اجازه تکرار اثر خارجی نیست. نسخه تازه مبدل یا مدل باید اجرای تحلیل تازه‌ای بسازد که به دریافت اصلی وصل است؛ کلید اقدام را صرفاً به دلیل ارتقای مدل عوض نکنید.

معیار آمادگی ساده است: تیم بتواند نشان دهد هر دریافت چرا پذیرفته یا رد شد، پس از خاموشی از کجا ادامه می‌یابد و کدام مجوز به اقدام نهایی وصل است. امضا شروع این زنجیره است. کد ۲۰۰ رسید آن است. هیچ‌کدام به‌تنهایی پایان کار را ثابت نمی‌کنند.

یادداشت منابع — بازبینی در ۱۵ اکتبر ۲۰۲۶

این پنج منبع، اسناد فنی زنده از سه ارائه‌دهنده‌اند و در صفحات بررسی‌شده تاریخ انتشار ثابت مشترکی ندارند. مهلت‌ها و قراردادهای نام‌برده باید هنگام تغییر نسخه اتصال دوباره بررسی شوند. مثال تیکت، جدول تصمیم و آزمون‌ها تحلیل ژرف‌اند، نه گزارش رخداد مشتری.

#وب‌هوک#امنیت دستیار#قابلیت اطمینان#یکپارچه‌سازی#مهندسی نرم‌افزار

مطالب مرتبط

این ابزار را در تیم خودتان راه بیندازید

اگر می‌خواهید ایجنت‌های هوش مصنوعی و اتوماسیون این راهنما را در تیم نرم‌افزار یا فرایندهای سازمانتان به کار بگیرید، از یک آزمایش کوچک شروع کنید که بتوانید نتیجه‌اش را بسنجید.