چطور هندلر postMessage یه iframe رو امن کنیم
خلاصهٔ کاملتر
iframe یه سند جداست و وقتی origin (یعنی ترکیب پروتکل، دامنه و پورت) دو طرف فرق داره، اسکریپتها به DOM همدیگه دسترسی ندارن. تنها در رسمی بین این دو postMessage هست. نویسنده میگه به محض اینکه یه listener برای message اضافه کنی، این در بدون قفل باز میشه، چون مرورگر پیام هر پنجرهای رو که به پنجرهٔ تو دسترسی داره تحویل میده. به قول خودش، یه listener پنجخطی در عمل یه API عمومی و بدون احراز هویته.
مثال مقاله یه ویجته که ارتفاع خودش رو اندازه میگیره و با '*' برای والد میفرسته، و والد هم بدون هیچ بررسیای ارتفاع iframe رو عوض میکنه. تو این مثال ضررش فقط بههم ریختن layoutه، ولی اگه هندلر داده رو بیاحتیاط تو innerHTML یا eval() بریزه، کار به XSS میکشه.
حملهٔ اول: یه سایت غریبه که به صفحه دسترسی داره با یه خط پیام resize جعلی میفرسته و ویجت رو یه پیکسلی میکنه. راهحل مقایسهٔ دقیق event.origin با origin مورد انتظاره، چون این مقدار رو خود مرورگر پر میکنه و فرستنده نمیتونه جعلش کنه:
const WIDGET_ORIGIN = 'https://widget.example.com';
if (event.origin !== WIDGET_ORIGIN) return;نویسنده تأکید میکنه که چک کردن با includes یا regex شل امن نیست، چون مثلاً widget.example.com.evil.net ازش رد میشه. اگه فریم sandbox بدون allow-same-origin باشه، origin به شکل رشتهٔ "null" میاد و هیچوقت نباید این رشته رو مجاز کرد.
حملهٔ دوم از origin درست ولی پنجرهٔ اشتباه میاد، مثلاً یه نسخهٔ دیگه از همون ویجت یا یه فریم تبلیغاتی از همون دامنه. راهحلش مقایسهٔ event.source با همون contentWindowی هست که خودت embed کردی:
if (event.source !== iframe.contentWindow) return;حملهٔ سوم دادهٔ خرابه: height منفی، 1e9، یه رشتهٔ خالی یا null. چون postMessage داده رو با structured clone کپی میکنه نه JSON، حتی NaN و Infinity هم سالم میرسن، و خوندن .height از null هندلر رو با خطا متوقف میکنه. نویسنده میگه اول شکل داده رو چک کن (آبجکت باشه و type درست داشته باشه)، بعد با Number.isFinite مطمئن شو عدده و آخر سر بین ۱۰۰ تا ۱۲۰۰ پیکسل clampش کن. از Number() هم استفاده نکن چون null و رشتهٔ خالی رو به عدد تبدیل میکنه.
نکات کلیدی:
- ترتیب بررسیها: origin با تساوی دقیق، بعد source، بعد شکل و مقدار داده، و فقط بعدش تغییر layout
- event.origin رو مرورگر پر میکنه و قابل جعل نیست، ولی includes و regex شل قابل دور زدنن
- origin رشتهٔ "null" مال فریمهای sandbox هست و نباید مجاز بشه
- سمت ویجت هم به جای '*' باید origin دقیق والد رو بدی تا پیام به صفحهٔ ناشناس نرسه
- هندلر rate limit نداره، پس پیامهای پشت سر هم رو باید تجمیع و محدود کرد
- اگه صفحه لازم نیست پیامی بگیره، بهترین کار اینه که اصلاً listener نذاری




