راهنما · API ایمیل
یک تیم محصول چگونه باید API ایمیل را بهطور امن پیادهسازی کند؟
API ایمیل را بهصورت یک گردشکار ناهمگام و مبتنی بر مجوز پیادهسازی کنید، نه یک فراخوانی مستقیم از فرم به ارائهدهنده. فراخواننده را احراز هویت کنید، مالکیت tenant بر یک دامنه From تأییدشده را تأیید کنید، پیام را اعتبارسنجی و اندازه آن را کنترل کنید، یک شناسه job پایدار در اپلیکیشن اختصاص دهید، یک بار در صف بگذارید و از یک worker ارسال کنید. هنگام پذیرش، شناسه پیام ارائهدهنده را ثبت کنید، رویدادهای تحویل را بهصورت idempotent دریافت کنید و برگشتهای دائمی و گزارشهای اسپم را در فهرست توقف ارسال قرار دهید. فقط زمانی از تلاشهای مجدد محدود استفاده کنید که ریسک ارسال تکراری کنترل شده باشد. اعتبارنامهها را سمت سرور نگه دارید، دادههای پیام در لاگها را به حداقل برسانید و میان پذیرش توسط API، تحویل به سرور ایمیل و رسیدن به صندوق ورودی تمایز بگذارید.
پیش از انتخاب ارائهدهنده، مرز API را تعریف کنید
یک API ایمیل باید نیت اپلیکیشن را نمایش دهد، بدون اینکه همه جزئیات ارائهدهنده به کد محصول نشت کند. منابعی برای پیامها، دامنههای ارسال، کلیدهای API، رویدادها و فهرست توقف ارسال تعریف کنید. تصمیم بگیرید فراخوانندگان کدام فیلدها را کنترل کنند، از جمله From، To، Reply-To، موضوع، متن، HTML و فهرست مجاز کوچکی از هدرها. هدرهای انتقالی ارائهشده توسط فراخواننده را که ممکن است با امضا یا مسیریابی ارائهدهنده تداخل داشته باشند رد کنید. ارسال را یک عملیات نوشتن پیامددار بدانید: پاسخ باید یک منبع پیام در اپلیکیشن و وضعیت فعلی آن را مشخص کند، نه اینکه نتیجهای درباره صندوق ایمیل القا کند. حساب ارائهدهنده، Region، configuration set و شناسههای انتقال را پشت یک adapter نگه دارید. این مرز مهاجرت میان ارائهدهندگان را ممکن میکند و به کنترلهای مجوزدهی، نگهداری داده و مقابله با سوءاستفاده جای ثابتی میدهد.
فراخوانندگان را احراز هویت کنید و مجوز هر دامنه فرستنده را بسنجید
کلیدهای API را فقط بهصورت hash یکطرفه ذخیره کنید و راز کامل را فقط یک بار نمایش دهید. برای هر کلید یک مالک در سطح فضای کاری، وضعیت، زمان ایجاد و مسیر ابطال تعیین کنید؛ وقتی یک یکپارچهسازی باید فقط ارسال کند یا فقط رویدادها را بخواند، دسترسیهای محدودتری اضافه کنید. احراز هویت به این پرسش پاسخ میدهد که چه کسی اعتبارنامه را ارائه کرده است، در حالی که مجوزدهی تعیین میکند آیا آن هویت میتواند از دامنه From و منبع پیام درخواستی استفاده کند. مالکیت دامنه را در هر ارسال، از جمله endpointهای دستهای، بررسی کنید و به شناسه دامنهای که کلاینت فرستاده اعتماد نکنید. پیش از فعال کردن ترافیک محیط عملیاتی، تأیید ارائهدهنده را الزامی کنید. هرگز اعتبارنامههای ارائهدهنده یا کلیدهای API فضای کاری را در JavaScript مرورگر، query string، analytics یا پیامهای خطا قرار ندهید. مجوزدهی در سطح شیء بهویژه برای شناسههای پیام، رویداد، موارد توقف ارسال، صندوق ورودی و دامنه در یک API multi-tenant اهمیت دارد.
دامنه را تأیید و احراز هویت را همراستا کنید
یک دامنه ارسال به چیزی بیش از یک پرچم در پایگاه داده نیاز دارد. بررسی مالکیت ارائهدهنده را کامل کنید و رکوردهای DKIM لازم را منتشر کنید. SPF به هاستها برای هویت SMTP MAIL FROM یا HELO مجوز میدهد، در حالی که DKIM یک دامنه امضاکننده را با امضای رمزنگاریشده پیام مرتبط میکند. DMARC ارزیابی میکند که آیا شناسه موفق SPF یا DKIM با دامنه From قابلمشاهده طبق RFC 5322 همراستاست و به مالک دامنه اجازه میدهد سیاست رسیدگی و گزارشدهی منتشر کند. اگر دامنه از قبل SPF دارد، مکانیزم لازم را در رکورد موجود ادغام کنید؛ RFC 7208 میگوید یک دامنه نباید چند رکورد منتشر کند که به انتخاب بیش از یک رکورد SPF منجر شود. سیاست سختگیرانهتر DMARC را فقط زمانی اعمال کنید که پیامهای کنترلشده و گزارشهای تجمیعی نشان دهند همه فرستندگان مشروع همراستا هستند. احراز هویت استفاده غیرمجاز از دامنه را کاهش میدهد، اما رسیدن به صندوق ورودی را تضمین نمیکند.
ساختار پیام را اعتبارسنجی و ورودی پذیرفتهشده را به حداقل برسانید
RFC 5322 پیام اینترنتی را مجموعهای از فیلدهای هدر و به دنبال آن یک بدنه اختیاری تعریف میکند و مشخصات MIME محتوا را فراتر از متن ساده گسترش میدهند. یک API میتواند بیشتر جزئیات قالب انتقال را پنهان کند و در عین حال آنها را اعمال کند. آرایههای گیرنده را نرمالسازی کنید، تعداد گیرندگان و اندازه کل کدگذاریشده را محدود کنید، دستکم یک بدنه متنی یا HTML را الزامی کنید و نشانیها را اعتبارسنجی کنید بدون اینکه وانمود کنید درستی نحو، وجود صندوق ایمیل را ثابت میکند. کاراکترهای carriage return و line feed را از فیلدهایی که به هدر تبدیل میشوند حذف کنید. Message-ID را خودتان تولید کنید یا بگذارید ارائهدهنده این کار را انجام دهد؛ از آن بهعنوان شناسه job اپلیکیشن استفاده نکنید، چون نسخه جدید یک پیام میتواند بهطور مشروع شناسه جدیدی بگیرد. فقط هدرهای سفارشی مستند را مجاز کنید، نسخههای تکراری فیلدهای محافظتشده را رد کنید و قالبها را پیش از ارسال به ارائهدهنده رندر کنید تا متغیرهای گمشده در یک وضعیت کنترلشده اپلیکیشن خطا بدهند.
یک بار در صف بگذارید و از شناسههای پایدار اپلیکیشن استفاده کنید
درخواست کاربر باید در یک تراکنش یک job پیام پایدار بسازد و سپس یک worker فراخوانی ارائهدهنده را انجام دهد. به job یک شناسه پایدار بدهید و اثر انگشت درخواست یا کلید idempotency ارائهشده توسط فراخواننده را، در صورتی که قرارداد از آن پشتیبانی کند، ثبت کنید. HTTP متد POST را بهطور پیشفرض غیر idempotent تعریف میکند و درباره تلاش مجدد خودکار هشدار میدهد، مگر اینکه کلاینت بداند عملیات عملاً idempotent است یا بداند درخواست اصلی اعمال نشده است. این موضوع برای ایمیل اهمیت دارد، زیرا timeout ممکن است پس از پذیرش پیام توسط ارائهدهنده اما پیش از دریافت پاسخ توسط worker رخ دهد. در خطای مبهم، بهجای ساختن یک ارسال تازه، ابتدا job ذخیرهشده و وضعیت ارائهدهنده را تطبیق دهید. وقتی وضعیت اپلیکیشن و انتشار در صف باید با هم جابهجا شوند از الگوی outbox استفاده کنید و پیرامون مرز idempotency یک قید یکتایی بگذارید.
تلاشهای مجدد را بر اساس دستههای خطا طراحی کنید
خطاهای اعتبارسنجی، مجوزدهی، throttling، رد شدن توسط ارائهدهنده، خطای گذرای انتقال و خطای تحویل به گیرنده را از هم جدا کنید. ورودی نامعتبر و دامنه From غیرمجاز باید بدون تلاش مجدد شکست بخورند. محدودیتهای نرخ ارائهدهنده و خطاهای موقت سرویس را میتوان با exponential backoff محدود، jitter، سقف تعداد تلاش و visibility timeout صف طولانیتر از مهلت درخواست worker دوباره امتحان کرد. timeout مبهم شبکه به تطبیق آگاه از تکرار نیاز دارد، نه یک درخواست جدید بیقیدوشرط. خود SMTP میان پاسخهای گذرای 4xx و دائمی 5xx تمایز میگذارد، اما اپلیکیشنی که از API ارائهدهنده استفاده میکند باید از معنای خطاهای مستند همان ارائهدهنده پیروی کند. jobهایی را که تلاشهایشان تمام شده به وضعیت dead-letter قابلبازبینی منتقل کنید و دلیل پاکسازیشده را نگه دارید. برگشت دائمی یک گیرنده را مانند قطعی API دوباره امتحان نکنید و گزارش اسپم را به تلاش ارسال دیگری تبدیل نکنید.
پذیرش را ثبت و رویدادهای تحویل را دریافت کنید
شناسه پیام ارائهدهنده را بلافاصله پس از پذیرش ذخیره کنید و آن را به شناسه پیام اپلیکیشن نگاشت کنید. آنگاه رویدادهای ارائهدهنده میتوانند حتی وقتی یک گزارش اسپم جزئیات گیرنده را پنهان میکند، منبع درست را بهروزرسانی کنند. برای مثال Amazon SES، ارسال موفق را از تحویل به سرور ایمیل گیرنده متمایز میکند و میتواند رویدادهای تحویل، bounce، گزارش اسپم، رد، تأخیر تحویل، شکست rendering، بازشدن و کلیک را منتشر کند. اصالت وبهوک را با سازوکار مستندشده ارائهدهنده تأیید، schema رویداد را اعتبارسنجی، با شناسه رویداد ارائهدهنده یا fingerprint قطعی deduplicate کنید و تحویل تکراری همان رویداد را بدون تکرار side effectها مجاز بدانید. payloadهای خام را فقط در صورت نیاز، رمزنگاریشده، با کنترل دسترسی و محدودیت نگهداری ذخیره کنید. وضعیت نرمالشده باید پیامدهای accepted، delivered-to-server، bounced، complained، delayed، rejected و suppressed را متمایز کند.
فهرست توقف ارسال را به کنترلی در زمان ارسال تبدیل کنید
یک رکورد توقف ارسال باید پیش از هر ارسال به ارائهدهنده بررسی شود، نه اینکه فقط در داشبورد نمایش داده شود. نشانیهایی که برگشت دائمی خوردهاند و گزارشهای اسپم معمولاً باید در فهرست توقف ارسال قرار گیرند؛ تأخیرهای موقت تحویل به سیاست متفاوتی نیاز دارند. دامنه اعمال فهرست توقف ارسال را آگاهانه تعیین کنید. یک فهرست در سطح کل حساب میتواند از اعتبار مشترک محافظت کند، اما ممکن است نتیجه یک گیرنده در یک tenant، tenant دیگری را مسدود کند. فهرستی در سطح tenant این وابستگی را کاهش میدهد، اما همچنان به لایهای برای مقابله با سوءاستفاده و ایمنی پلتفرم نیاز دارد. دلیل، رویداد منبع، tenant، زمان ایجاد و مسیر کنترلشده حذف را ثبت کنید. حذف یک مورد توقف ارسال ناشی از گزارش اسپم یا برگشت دائمی پیامددار است و باید با بازبینی آگاهانه و شواهدی مبنی بر معتبر بودن نشانی و انتظار گیرنده برای دریافت پیام انجام شود. از کپی کردن نشانیهای خام گیرندگان در لاگهای عمومی یا آزمایشها پرهیز کنید؛ ذخیرهسازی عملیاتی میتواند سیاست ارسال را اعمال کند و analytics از شمارشهای تجمیعی استفاده کند.
از ارسالهای دستهای و جریانهای حساس کسبوکار محافظت کنید
یک endpoint دستهای اثر هر خطای مجوزدهی یا اعتبارسنجی را چند برابر میکند. همان بررسیهای مالکیت دامنه، فهرست توقف ارسال، اندازه و محتوا را روی تکتک آیتمها اعمال کنید، حداکثر طول دسته را سختگیرانه تعیین کنید و نتایج هر آیتم را بدون نشت دادههای tenant دیگر برگردانید. محدودیت نرخ باید در سطح اعتبارنامه، فضای کاری، دامنه و ارائهدهنده وجود داشته باشد و کنترلهای جداگانهای برای جهشهای ناگهانی و حجم بازهای داشته باشد. یک محدودیت سراسری درخواست در ثانیه کافی نیست، زیرا یک درخواست میتواند گیرندگان زیادی داشته باشد. در ابزارهای مبتنی بر ایجنت، پیش از ارسال یک دسته پراثر، تأیید آگاهانه را الزامی کنید. وقتی قواعد رضایت و عملیات ایمیل تراکنشی و بازاریابی متفاوت است، مجوزهای آنها را جدا کنید. رشد غیرعادی گیرندگان، دامنههای مکرراً ردشده، تغییرات زیاد در برگشت یا گزارش اسپم و ایجاد سریع کلید را پایش کنید. محدودیت نرخ به ایمنی کمک میکند، اما جایگزین احراز هویت، مجوزدهی در سطح شیء، رضایت تأییدشده یا واکنش به سوءاستفاده نیست.
مسیرهای خطا را پیش از محیط عملیاتی آزمایش کنید
از شبیهسازهای ارائهدهنده یا صندوقهای ایمیل کنترلشده برای آزمایش پذیرش، تحویل به سرور گیرنده، برگشت دائمی، گزارش اسپم، تأخیر، دامنه نامعتبر، کلید ابطالشده، throttling، timeout ارائهدهنده، وبهوک تکراری و تحویل مجدد از صف استفاده کنید. تأیید کنید که یک کلید idempotency یکسان فقط یک پیام در اپلیکیشن میسازد، رویداد تکرارشده اثر جانبی تکراری ایجاد نمیکند و یک tenant نمیتواند با دامنه یا شناسه پیام tenant دیگر بخواند یا ارسال کند. یک پیام واقعی دریافتشده را از نظر From، Return-Path، DKIM، SPF، همراستایی DMARC، رندر متن و HTML، رفتار لغو اشتراک در صورت کاربرد و لینکها بررسی کنید. صف را زیر محدودیتهای تأییدشده ارائهدهنده تحت آزمون بار قرار دهید و backpressure را بهجای دور زدن آن تأیید کنید. برای سن صف، تلاشهای مجدد تمامشده، خطاهای دریافت رویداد، حاشیه سهمیه، تغییرات برگشت و گزارش اسپم و callbackهای جاافتاده ارائهدهنده هشدار تعریف کنید. چکلیست راهاندازی باید برای هر هشدار و اقدام بازیابی یک مالک مشخص کند.
الگو را با دقت در SendHQ به کار ببرید
SendHQ کلیدهای bearer در سطح فضای کاری، بررسیهای دامنه From تأییدشده، ساخت پیام تکی و دستهای، صندوقهای ایمیل ورودی، رویدادهای پیام و منابع توقف ارسال فراهم میکند. این قابلیتها از معماری این راهنما پشتیبانی میکنند: کلید را سمت سرور نگه دارید، یک منبع پیام بسازید، ID آن را حفظ کنید و بهجای اینکه پاسخ اولیه را تحویل نهایی بدانید، رویدادهای بعدی را بخوانید. صرفنظر از پلتفرم، فراخوانان همچنان مسئول گیرندگان موردنظر، ایمیل قانونی و موردانتظار، درستی محتوا و تأیید دقیق ارسالهای پیامددار هستند.
پرسشهای متداول
آیا API ایمیل باید بهصورت همگام از درون درخواست وب ارسال کند؟
معمولاً نه. یک پیام پایدار در اپلیکیشن بسازید و آن را در صف بگذارید، سپس بگذارید یک worker ارائهدهنده را فراخوانی کند. این کار تأخیر را جدا میکند، از تلاشهای مجدد محدود پشتیبانی میکند و تطبیق نتایج مبهم ارائهدهنده را آسانتر میسازد.
وقتی درخواستی دچار timeout میشود، چگونه از ایمیلهای تکراری جلوگیری کنم؟
از یک شناسه job پایدار در اپلیکیشن و یک مرز idempotency با قید یکتایی استفاده کنید. در timeout مبهم، پیش از ارسال دوباره به ارائهدهنده با هویت جدید، job موجود را تطبیق دهید.
آیا پاسخ موفق API ایمیل به معنای تحویل است؟
خیر. معمولاً نشان میدهد که API یا ارائهدهنده درخواست را پذیرفته است. از رویدادهای بعدی برای تمایز تحویل به سرور گیرنده، برگشت، گزارش اسپم، تأخیر، رد شدن و توقف ارسال از پذیرش اولیه استفاده کنید.
یک API ایمیل به چه رکوردهای DNSای نیاز دارد؟
رکوردهای دقیق به ارائهدهنده بستگی دارند، اما ارسال در محیط عملیاتی معمولاً به تأیید دامنه و DKIM، بهعلاوه یک راهبرد درست SPF و سیاست DMARC همراستا با جریانهای ارسال مشروع نیاز دارد.
آیا کلیدهای API باید در کد مرورگر ذخیره شوند؟
خیر. اعتبارنامههای فضای کاری و ارائهدهنده را در مخزن اسرار سمت سرور نگه دارید، کلیدهای API اپلیکیشن را در صورت امکان بهصورت hash ذخیره کنید، رازهای کامل را فقط یک بار نشان دهید و مسیرهای سریع ابطال و چرخش فراهم کنید.
یک API ایمیل چگونه باید برگشتهای دائمی را مدیریت کند؟
رویداد ارائهدهنده را نرمالسازی کنید، آن را به پیام اپلیکیشن نگاشت کنید و ارسالهای عادی آینده به آن گیرنده را در دامنه موردنظر متوقف کنید. حذف از فهرست باید آگاهانه و با پشتوانه شواهد باشد.
منابع
- RFC 9110: معناشناسی HTTP — Internet Engineering Task Force
- RFC 5321: پروتکل ساده انتقال ایمیل (SMTP) — Internet Engineering Task Force
- RFC 5322: قالب پیام اینترنتی — Internet Engineering Task Force
- RFC 6376: امضاهای DomainKeys Identified Mail (DKIM) — Internet Engineering Task Force
- RFC 7208: چارچوب سیاست فرستنده (SPF) — Internet Engineering Task Force
- RFC 7489: احراز هویت، گزارشدهی و انطباق پیام مبتنی بر دامنه (DMARC) — Internet Engineering Task Force
- پایش فعالیت ارسال در Amazon SES — Amazon Web Services
- عیبیابی اعلانهای Amazon SES — Amazon Web Services
- ده ریسک برتر امنیت API از OWASP، نسخه 2023 — OWASP Foundation
- قرارداد OpenAPI در SendHQ — SendHQ