أداة فحص تضمين iframe
فاحص iframe مجاني عبر الإنترنت: أدخل أي عنوان URL لصفحة ويب لفحص رؤوس X-Frame-Options و Content-Security-Policy frame-ancestors الخاصة بها، واكتشف ما إذا كانت الصفحة قابلة للتضمين في iframe، وأكد ذلك بعرض مباشر. يحفظ السجل لجلسة المتصفح الحالية فقط.
السجل (هذه الجلسة)
يُحفظ في هذا التبويب فقط (sessionStorage)؛ وسيتم مسحُه عند إغلاق التبويب. لا يُرسل أي بيانات إلى الخادم.
آلية عمل الفحص
- أدخل عنوان URL المستهدف وانقر على "تحقق". يقوم الخادم بجلب الصفحة كطلب iframe حقيقي (مع إضافة Sec-Fetch-Dest: iframe) وقراءة رؤوس استجابته.
- تُستمد النتيجة من X-Frame-Options (يمنع التضمين بـ DENY/SAMEORIGIN) و Content-Security-Policy frame-ancestors (لا يسمح إلا بـ * أو بدائل المخطط لتضمين نطاقات خارجية عشوائية).
- يتم تحميل نفس عنوان URL في نافذة iframe للعرض المباشر حتى تتمكن من التأكد مما يقوم به المتصفح فعليًا، بما في ذلك عمليات كسر التضمين بواسطة JavaScript التي لا تكشف عنها رؤوس الاستجابة.
ما الذي يحدد إمكانية تضمين صفحة ما؟
يتحدد ما إذا كان بإمكان موقع آخر عرض صفحتك داخل إطار فرعي (<iframe>) بناءً على رؤوس الاستجابة التي ترسلها الصفحة ذاتيًا. ويقوم هذا الفاحص بإجراء عملية التدقيق نفسها على جانب الخادم لتقديم حكم فوري لك.
يُعد رأس X-Frame-Options الحل الكلاسيكي المعتمد: حيث تحظر قيمة DENY التضمين في كافة الحالات، بينما تسمح SAMEORIGIN بالتضمين من نفس الأصل فحسب، وقد تم سحب الدعم لـ ALLOW-FROM في المتصفحات الحديثة. وفي حال غياب هذا الرأس، لا تطبق المتصفحات قواعد الحظر القديمة، مما قد يسمح بالتضمين.
يمثل إعداد CSP frame-ancestors البديل العصري الحالي. وهو يحدد قائمة بالأصول المسموح لها بتضمين هذه الصفحة، مثلاً: `frame-ancestors 'self' https://example.com`. ويُسمح باستخدام القيمة `*` (أو مخططًا عامًا مثل `https:`) لقبول أي أصل يستخدم HTTPS كحاوي، بينما تعمل القيم الأقل مرونة على حظر محاولات التضمين من جهات خارجية.
لكن الرؤوس وحدها لا تكفي الصورة كاملة؛ إذ يمكن لبرامج صفحات الويب تنفيذ أكواد إيقاف التضمين (مثل التحقق مما إذا كان `top !== self` وإجبار الانتقال إلى مستوى أعلى)، وقد تتعامل صفحات تسجيل الدخول أو نقاط الوصول المقيدة جغرافيًا مع طلبات خوادم مراكز البيانات بشكل مختلف. ولهذا السبب، يأتي كل فحص مصحوبًا بمعاينة مباشرة لنتيجته.
دليل سريع: تهيئة رؤوس الاستجابة
إذا كنت تملك أو تدير الموقع، يمكنك التحكم في طريقة تضمينه مباشرةً عبر رؤوس استجابة الخادم.
حظر التضمين من أي صفحة
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
تمتنع المتصفحات عن عرض الصفحة داخل أي إطار فرعي (iframe) — مما يوفر أشدّ أشكال الحماية فعاليةً ضد قرصنة النقر (Clickjacking).
السماح بالتضمين من نفس الأصل فقط
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
يُسمح فقط للصفحات التي تشترك معك في المخطط (Scheme)، والمضيف (Host)، والمنفذ (Port) بتضمين الصفحة؛ بينما تُحجب المواقع الأخرى تمامًا.
السماح بالتضمين من أصول محددة فقط
Content-Security-Policy: frame-ancestors 'self' https://trusted.example.com
أضِف الأصول الموثوقة ضمن إعداد frame-ancestors؛ وسيتم حجب أي أصل غير مدرج فيها.
السماح بالتضمين من أي صفحة
عدم تعيين X-Frame-Options
Content-Security-Policy: frame-ancestors *
يمكن لأي موقع تضمين هذه الصفحة داخل إطار فرعي. يرجى تقييم المقايضة الأمنية لهذا الخيار.
ملاحظة: حلّ إعداد CSP frame-ancestors محل X-Frame-Options. وعند توفر الاثنين معًا، تكون لسياسات CSP الأولوية — لذا يُفضّل الاعتماد على frame-ancestors.
كيف يكتشف الخادم أن الطلب قادم من iframe؟
منذ عام 2020 تقريبًا، تبدأ المتصفحات القائمة على كروميوم وفايرفوكس تلقائيًا بإرفاق مجموعة من رؤوس طلبات بيانات الاستيفاء (Fetch Metadata) مع كل تنقل أو طلب مورد فرعي. وأكثرها فائدة لفحص حالة التضمين هو `Sec-Fetch-Dest`، حيث يخبر الخادم طبيعة السياق الذي أرسل الطلب، وذلك دون حاجة إلى أي تعاون أو تعديل في كود الصفحة الطالبة.
- Sec-Fetch-Dest: document — تنقل رئيسي (على مستوى الصفحة العليا)، وليس داخل أي إطار فرعي.
- Sec-Fetch-Dest: iframe — يتم جلب الطلب وعرضه داخل عنصر `<iframe>` الفرعي.
- Sec-Fetch-Dest: frame — يتم التحميل داخل عناصر `<frame>` القديمة (إطارات مجموعات الصفحات frameset).
- Sec-Fetch-Dest: embed / object — يتم التحميل داخل عنصر `<embed>` أو `<object>`.
يرسل هذا الماسح طلبًا استكشافيًا خاصًا به يحمل رأس `Sec-Fetch-Dest: iframe`، محاكياً بدقة ما ترسله المتصفحات الحقيقية عند تضمين صفحة؛ لذا يعكس الحكم السلوك الفعلي للخادم المستهدف تجاه طلبات `iframe` الحقيقية، وليس مجرد عملية جلب عامة.
يُعد `Sec-Fetch-Dest` إشارة مفيدة لكنه لا يشكّل حدًا للحماية؛ فهو يُرسل حصريًا من قِبل المتصفحات الحديثة، بينما يمكن لعملاء غير المتصفحات — مثل `curl` والبوتات وبعض أطر العرض (WebViews) والمتصفحات الأقدم — حذفه كليًا أو إرسال قيمة اعتباطية. يجب على الخادم استخدامه لتدوين الأحداث أو تقييد معدل الطلبات، ولكن لا ينبغي الاعتماد عليه وحده للحكم على سلامة التضمين؛ فالقضاء على هذا الأمر يعود لسياسات `X-Frame-Options` و `CSP frame-ancestors` التي تفرضها المتصفحات بشكل مستقل عن رؤوس الطلبات.
يمكن للصفحة من جانب العميل أيضًا اكتشاف وضعها داخل إطار دون الحاجة إلى رؤوس HTTP، مثلًا بالمقارنة بين `window.top !== window.self` (أو باستخدام `window.frameElement` للإطارات المشتركة الأصل)، ثم اتخاذ إجراء مناسب — كتظهر رسالة تحذير أو فرض إعادة توجيه نحو نافذة المستوى الأعلى. وتشكّل هذه تقنية "تجاوز الإطار" (Frame Busting) التي أُشير إليها سابقًا، وهي فعالة بغض النظر عن محتوى رؤوس الطلبات المرسلة.
أمثلة تكوين الخادم: منع تضمين iframe
توضح الأمثلة البرمجية أدناه كيفية ضبط الخادم أو البيئة البرمجية لمنع تضمين الصفحة في أي إطار فرعي كليًّا (X-Frame-Options: DENY + CSP frame-ancestors 'none')، وهو الإعداد الأكثر تشددًا مقارنة بالخيارات في الدليل السريع أعلاه.
server {
location / {
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none'" always;
}
}
<IfModule mod_headers.c>
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none'"
</IfModule>
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
next();
});
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'Content-Security-Policy', value: "frame-ancestors 'none'" },
],
},
];
},
};
class SetFrameOptions
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('Content-Security-Policy', "frame-ancestors 'none'");
return $response;
}
}
export default {
async fetch(request) {
const response = await fetch(request);
const headers = new Headers(response.headers);
headers.set('X-Frame-Options', 'DENY');
headers.set('Content-Security-Policy', "frame-ancestors 'none'");
return new Response(response.body, { status: response.status, headers });
},
};
إذا كنت ترغب في السماح لموقعك فقط (أو بقائمة سماح محدودة) بتضمين الصفحة بدلًا من حظر الكل؟ قم باستبدال DENY بـ SAMEORIGIN، واستبدال 'none' بـ 'self' أو بقائمة صريحة للموافقة — بينما تظل باقي إعدادات التكوين دون تغيير.
نقاط هامة حول المنصات الشائعة
بالإضافة إلى القواعد العامة لرؤوس HTTP، تختلف بعض المنصات العالمية في تعاملها الخاص — وهو أمر جدير بالعلم قبل الاستنتاج بأن المحتوى "مُحظور" أو "قابل للتضمين" اعتمادًا على الرؤوس وحدها.
| المنصة / السيناريو | التقييم | السبب |
|---|---|---|
| صفحة مشاهدة YouTube (youtube.com/watch) | مُحظور | يُرجى استخدام رابط مشغل YouTube الرسمي `youtube.com/embed/{id}` بدلًا من ذلك؛ فنسخة المشاهدة العادية ترسل سياسة تضمين مقيدة. |
| صفحة المكان على خرائط Google | مُحظور | لا يُقصد بتضمين في إطار سوى عنوان `iframe` الخاص بـ Maps Embed API (google.com/maps/embed). |
| صفحات تسجيل الدخول OAuth لدى Google / Microsoft | مُحظور | تم حظرها عمدًا منذ عام 2015 لمنع تصيّد البيانات الاعتمادية عبر الإطارات المضمنة؛ ويجب أن تعمل دورة تسجيل الدخول ضمن نافذة رئيسية (Top-Level) أو نافذة منبثقة. |
| صفحات إتمام دفع الطلب (Stripe Checkout، PayPal، ومعظم بوابات البنوك) | مُحظور | يُعد تضمين نموذج الدفع في إطار بيئة كلاسيكية لهجمات النقر الخادع (Clickjacking)، لذا ترفض مزودو الخدمات ذلك رفضًا قاطعًا. |
| مستودعات أو ملفات على GitHub | مُحظور | يطبق الموقع سياسة `frame-ancestors 'none'` على نطاق واسع؛ يُرجى استخدام واجهة REST/GraphQL API أو التقاط لقطة شاشة بدلًا من iframe. |
| صفحات "نشر على الويب" في Notion / Google Docs | قابل للتضمين | صُممت صراحةً ليكون تضمينها ممكنًا، وتوفر المنصة أكواد تضمين جاهزة الاستخدام. |
| مقالات ويكيبيديا | قابل للتضمين | لا تحتوي افتراضيًا على قيود `X-Frame-Options` أو CSP، لكن يُرجى التحقق من شروط الترخيص والإسناد قبل التضمين على نطاق واسع. |
حالات الاستخدام الشائعة
- حدد مسبقًا ما إذا كان بالإمكان تضمين أداة أو لوحة معلومات أو مستند تابع لجهة خارجية في منتجك قبل كتابة كود التكامل.
- تأكد من أن موقعك يرسل سياسة `X-Frame-Options` أو `CSP frame-ancestors` التي تقصدها.
- استكشاف أخطاء حماية النقر الخادع (Clickjacking): لفهم سبب عرض الصفحة لـ about:blank أو رسالة رفض داخل عنصر iframe.
- فحص عناوين URL المُرشّحة دفعةً واحدة أثناء تجميع المحتوى أو دمج البوابة، مع إمكانية الرجوع إلى النتائج سابقاً عبر سجل الجلسة.
- الاستعداد لمراجعات الأمان أو اختبارات الاختراق من خلال توثيق سياسة وضع الإطارات (Framing Policy) للمكونات الخارجية المعتمدة.