เครื่องมือตรวจสอบ iframe

เครื่องมือตรวจสอบ iframe ออนไลน์ฟรี: กรอก URL ของเว็บเพจใดก็ได้เพื่อตรวจสอบ Header X-Frame-Options และ Content-Security-Policy frame-ancestors ว่าหน้านั้นสามารถฝังใน iframe ได้หรือไม่ พร้อมยืนยันผลด้วยการดูตัวอย่างแบบ Live โดยประวัติจะถูกเก็บไว้เฉพาะเซสชันเบราว์เซอร์ปัจจุบัน

การตรวจสอบการฝัง IFrame

:

ดำเนินการตรวจสอบ Header ตามรูปแบบเดิมสำหรับแต่ละบรรทัด แล้วเพิ่มลงในประวัติเซสชันด้านล่าง จำกัดจำนวน 20 URL ต่อชุด โดยจะทำการตรวจสอบทีละน้อยเพื่อลดภาระต่อเว็บไซต์เป้าหมาย

ตรวจสอบแล้ว: /
URL ผลลัพธ์ HTTP

ตัวอย่างแบบเรียลไทม์ (Live Preview)

การตรวจสอบ Header เป็นขั้นตอนแรกที่ทำเร็ว แต่ไม่สามารถตรวจเจอ JavaScript frame-busting ได้ หาก Header แจ้งว่า "ฝังได้" แต่ตัวอย่างยังคงว่างเปล่า แตกออกจากกรอบ หรือแสดงหน้าผิดพลาด แสดงว่าเว็บนี้ไม่ปลอดภัยต่อการฝัง ตัวอย่างแบบ Live จึงเป็นข้อตัดสินขั้นสุดท้าย

ประวัติ (เซสชันนี้)

บันทึกเฉพาะแท็บนี้เท่านั้น (sessionStorage) และจะถูกล้างเมื่อปิดแท็บ ข้อมูลไม่ถูกส่งไปยังเซิร์ฟเวอร์

ยังไม่มีรายการตรวจสอบในเซสชันนี้

หลักการทำงานของเครื่องมือตรวจสอบ

  1. กรอก URL เป้าหมายแล้วคลิกตรวจสอบ เซิร์ฟเวอร์จะดึงหน้าเว็บในรูปแบบคำขอ iframe จริง (พร้อมหัวข้อมาตรฐาน Sec-Fetch-Dest: iframe) และอ่านค่า Header ของ Response
  2. ผลสรุปคำนวณจาก X-Frame-Options (DENY / SAMEORIGIN ป้องกันการฝัง) และ Content-Security-Policy frame-ancestors (เฉพาะค่า * หรือ wildcards ของโพรโทคอลเท่านั้นที่ยอมรับการฝังจากโฮสต์ภายนอกอื่น ๆ)
  3. URL เด่นจะถูกโหลดลงใน iframe ตัวอย่างแบบ Live เพื่อให้คุณยืนยันสิ่งที่เบราว์เซอร์ จริง ๆ ทำ รวมถึง JavaScript frame-busting ที่ Header ไม่สามารถเปิดเผยได้

อะไรเป็นตัวกำหนดว่าหน้าเว็บสามารถฝังได้หรือไม่?

การที่เบราว์เซอร์จะยอมให้แสดงหน้าเว็บหนึ่งอยู่ใน `<iframe>` ของอีกไซต์นั้น ขึ้นอยู่กับการตรวจสอบ Response Headers ที่ส่งมาจากหน้าเว็บนั้นโดยตรง เครื่องมือตรวจสอบนี้จะทำการวิเคราะห์ฝั่งเซิร์ฟเวอร์ในรูปแบบเดียวกันและให้ผลการประเมินทันที

`X-Frame-Options` เป็น Header มาตรฐานดั้งเดิม โดยค่า DENY จะห้ามฝังหน้าเว็บในทุกกรณี, SAMEORIGIN เปิดโอกาสเฉพาะหน้าที่มาจากต้นทางเดียวกันเท่านั้น ส่วน ALLOW-FROM นั้นถูกลบออกจากการรองรับในเบราว์เซอร์ยุคใหม่แล้ว หากไม่พบ Header นี้ กฎรุ่นเก่าจะไม่ได้ทำหน้าที่บล็อกการฝังหน้าเว็บ

`Content-Security-Policy: frame-ancestors` คือตัวเลือกปัจจุบันที่ทันสมัยกว่า โดยจะมีการระบุ Origin ที่อนุญาตให้ฝังหน้าเว็บ เช่น `frame-ancestors 'self' https://example.com` หากกำหนดค่าเป็น `*` (หรือระบุแค่โปรโตคอลเช่น `https:`) จะอนุญาตให้ไซต์ HTTPS อื่นๆ นำมาฝังได้ทั้งหมด ในขณะที่การจำกัดเงื่อนไขที่แคบลงจะช่วยบล็อกการฝังจากภายนอก

แต่ Header อาจไม่ใช่ปัจจัยเดียวที่ใช้ตัดสินได้: สคริปต์ในหน้าเว็บอาจรันโค้ดป้องกัน Frame-Busting (เช่น ตรวจสอบ `top !== self` เพื่อบังคับนำทางกลับมายังระดับบนสุด) นอกจากนี้หน้าล็อกอินหรือจุดเชื่อมต่อที่จำกัดตามภูมิศาสตร์ อาจทำงานต่างออกไปเมื่อรับคำขอจาก Data Center นี่คือสาเหตุที่แต่ละผลลัพธ์จะมาพร้อมกับตัวอย่างการแสดงจริง (Live Preview) เสมอ

คู่มือลัด: การตั้งค่า Response Header

หากเป็นผู้ดูแลหรือเจ้าของเว็บไซต์ สามารถควบคุมการฝัง iframe ได้โดยตรงผ่าน Response Headers

ปิดกั้นการฝังหน้าเว็บจากทุกแหล่ง

X-Frame-Options: DENY Content-Security-Policy: frame-ancestors 'none'

เบราว์เซอร์จะปฏิเสธการแสดงผลหน้าเว็บใน iframe ทุกประเภท นี่เป็นมาตรการป้องกันการคลิกแจ็กกิ้ง (Clickjacking) ที่เข้มงวดที่สุด

อนุญาตให้ฝังเฉพาะจากโดเมนต้นทางเดียวกัน (Same-Origin)

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 *

ไซต์ใดก็สามารถนำหน้าเว็บนี้ไปฝังใน iframe ได้ ควรพิจารณา权衡ความปลอดภัยอย่างรอบคอบ

ข้อควรทราบ: Directive ใน CSP อย่าง `frame-ancestors` มาแทนที่มาตรฐาน `X-Frame-Options` ในยุคปัจจุบัน เมื่อทั้งสอง Header ปรากฏพร้อมกัน ค่า CSP จะมีความสำคัญสูงกว่า จึงแนะนำให้ใช้ `frame-ancestors` เป็นหลัก

เซิร์ฟเวอร์ตรวจจับได้อย่างไรว่าคำขอมาจาก iframe?

นับตั้งแต่ประมาณปี 2020 เป็นต้นมา เบราว์เซอร์ฐาน Chromium และ Firefox จะแนบชุด Request Headers สำหรับ Fetch Metadata อัตโนมัติไปกับทุกการโหลดหน้าเว็บและคำขอ Resourceเสริม Header ที่มีประโยชน์ที่สุดสำหรับการตรวจสอบการฝังคือ `Sec-Fetch-Dest` ซึ่งบอกเซิร์ฟเวอร์ได้ว่าทรัพยากรนี้ถูกร้องขอในบริบทประเภทใด โดยไม่ต้องพึ่งพาการตั้งค่าใดๆ จากฝั่งหน้าเว็บที่ถูกนำไปฝัง

  • `Sec-Fetch-Dest: document` — เป็นการนำทางระดับบนสุด (Top-level) ไม่ใช่การโหลดภายในกรอบใดๆ
  • `Sec-Fetch-Dest: iframe` — คำขอนี้ถูกโหลดภายในองค์ประกอบ `<iframe>`
  • `Sec-Fetch-Dest: frame` — โหลดภายใน `<frame>` (Frameset) รุ่นเก่า
  • `Sec-Fetch-Dest: embed / object` — โหลดภายในองค์ประกอบ `<embed>` หรือ `<object>`

เครื่องมือตรวจสอบนี้จะส่งแพ็กเก็ตทดสอบพร้อมเฮดเดอร์ Sec-Fetch-Dest: iframe เพื่อจำลองพฤติกรรมของเบราว์เซอร์จริงที่ทำการฝังหน้าเว็บอย่างครบถ้วน ส่งผลให้ผลลัพธ์สะท้อนการตอบสนองที่แท้จริงของเซิร์ฟเวอร์เป้าหมายต่อคำขอ iframe จริงๆ ไม่ใช่เพียงแค่การเรียกข้อมูลทั่วไป (generic fetch)

Sec-Fetch-Dest เป็นสัญญาณที่มีประโยชน์แต่ไม่ได้ถือเป็นชั้นเชิงป้องกันความปลอดภัย เนื่องจากมีเพียงเบราว์เซอร์สมัยใหม่ที่ส่งค่านี้ และมีไคลเอนต์อื่นที่ไม่ใช่เบราว์เซอร์ เช่น curl, บอต, Webview บางรูปแบบ หรือเบราว์เซอร์รุ่นเก่า ที่อาจไม่ส่งมาเลยหรือส่งค่าใดก็ตามเข้ามาได้ เซิร์ฟเวอร์อาจนำค่านี้ไปใช้ในการบันทึก Log หรือกำหนดอัตราการเรียกใช้ (Rate-limit) ได้ แต่ไม่ควรยึดถือเพียงลำพังในการตัดสินใจว่าการเล่นใน Frame ปลอดภัยหรือไม่ การควบคุมดังกล่าวควรขึ้นอยู่กับเฮดเดอร์ X-Frame-Options และ CSP frame-ancestors เป็นหลัก โดยเบราว์เซอร์จะบังคับใช้กฎเหล่านี้แยกต่างหากจากรหัสเฮดเดอร์ของคำขอ

ทางฝั่งไคลเอนต์ หน้าเว็บยังสามารถตรวจสอบตัวเองว่ากำลังรันอยู่ในเฟรมหรือไม่ แม้ไม่มีเฮดเดอร์ใดๆ รองรับ ตัวอย่างเช่น การเปรียบเทียบค่า window.top !== window.self (หรืออ่านค่า window.frameElement ในกรณี Same-Origin) จากนั้นจึงจัดการตอบสนองที่เหมาะสม เช่น แสดงคำเตือน หรือบังคับให้เปลี่ยนเส้นทางไปยังหน้าระดับบนสุด (Top-level redirect) ซึ่งเป็นเทคนิค Frame-busting ที่ได้กล่าวไว้ก่อนหน้านี้ และวิธีการนี้จะทำงานได้เสมอ ไม่ว่าสถานะเฮดเดอร์ของคำขอจะเป็นอย่างไร

ตัวอย่างการตั้งค่าเซิร์ฟเวอร์: ป้องกันการฝัง iframe

ตัวอย่างโค้ดด้านล่างอธิบายวิธีตั้งค่าเซิร์ฟเวอร์หรือเฟรมเวิร์กเพื่อปิดกั้นการฝัง 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' หรือระบุโดเมนที่ต้องการเจาะจงได้เลย โดยวิธีการตั้งค่าในส่วนอื่นๆ ยังคงเหมือนเดิม

จุดที่ต้องระวังของแพลตฟอร์มต่าง ๆ

นอกจากหลักการมาตรฐานของเฮดเดอร์ทั่วไปแล้ว แพลตฟอร์มยอดนิยมหลายแห่งยังมีความเฉพาะตัวของตัวเอง ซึ่งผู้พัฒนาควรทำความเข้าใจไว้ก่อนจะได้อาจทึกทักเองว่าเนื้อหาถูก "Blocked" หรือ "Embeddable" เพียงจากการตรวจสอบเฮดเดอร์เท่านั้น

แพลตฟอร์ม / สถานการณ์ พฤติกรรมการทำงาน เหตุผลประกอบ
หน้าวิดีโอ YouTube (youtube.com/watch) ถูกบล็อก ควรใช้ URL วิดีโอแบบฝังทางการของ YouTube (youtube.com/embed/{id}) แทน เนื่องจากหน้าดูวิดีโอปกติจะมีนโยบายจำกัดการเล่นใน Frame อยู่แล้ว
หน้ารายละเอียดสถานที่บน Google Maps ถูกบล็อก จะมีก็เฉพาะ URL ของ Maps Embed API (google.com/maps/embed) เท่านั้นที่เปิดรองรับการฝังใน Frame โดยตรง
หน้าเข้าสู่ระบบผ่าน OAuth ของ Google และ Microsoft ถูกบล็อก มีการบล็อกไว้ตั้งแต่ปี 2015 เพื่อป้องกันการหลอกลวงขโมยข้อมูลรับรอง (Credential Phishing) ภายใน iframe โดยกระบวนการเข้าสู่ระบบจำเป็นต้องดำเนินการในหน้าต่างหลักหรือป๊อปอัปเท่านั้น
หน้าชำระเงิน (เช่น Stripe Checkout, PayPal และ Gateway ธนาคารส่วนใหญ่) ถูกบล็อก การเล่นใน Frame ของฟอร์มชำระเงินถือเป็นช่องทางโจมตีแบบ Clickjacking ที่พบบ่อยที่สุด ผู้ให้บริการตัดเงินจึงปฏิเสธการฝังแบบเด็ดขาด
หน้า Repository หรือไฟล์บน GitHub ถูกบล็อก กำหนดนโยบาย frame-ancestors 'none' ครอบคลุมทั้งเว็บไซต์ แนะนำให้ใช้ REST/GraphQL API หรือการแสดงภาพหน้าจอ (Screenshot) แทนการใช้ iframe
หน้า "Notion" หรือ "Google Docs" แบบ Publish to web ฝังได้ ออกแบบมาให้รองรับการฝังโดยตรง และผลิตภัณฑ์ยังให้โค้ดสำหรับฝัง (Embed code) มาให้ใช้งานได้เลย
บทความใน Wikipedia ฝังได้ โดยค่าเริ่มต้นไม่มีการจำกัดด้วย X-Frame-Options หรือ CSP อย่างไรก็ดี ควรตรวจสอบเรื่องลิขสิทธิ์และเงื่อนไขการอ้างอิงอีกครั้งก่อนนำเนื้อหาไปฝังจำนวนมาก

สถานการณ์การใช้งานทั่วไป

  • ช่วยตัดสินใจล่วงหน้าว่าวิดเจ็ต (Widget), แดชบอร์ด หรือเอกสารของบุคคลที่สามสามารถนำมากับงานในผลิตภัณฑ์ของคุณได้หรือไม่ ก่อนจะเริ่มลงมือเขียนโค้ดเชื่อมต่อ (Integration)
  • ตรวจสอบให้แน่ใจว่าเว็บไซต์ของคุณส่งนโยบาย X-Frame-Options หรือ CSP frame-ancestors ตรงตามที่ตั้งใจไว้จริง ๆ
  • แก้ไขปัญหาด้านการป้องกันการคลิกจี้ (Clickjacking): ตรวจสอบสาเหตุที่ทำให้หน้าเว็บแสดงผลเป็น `about:blank` หรือแสดงข้อความปฏิเสธการทำงานเมื่ออยู่ภายใน iframe
  • ตรวจสอบรายชื่อ URL ที่ผ่านการคัดกรองเบื้องต้นแบบแบตช์ ระหว่างการรวบรวมเนื้อหาหรือการผสานระบบไปยังพอร์ทัล โดยสามารถใช้ประวัติเซสชันย้อนกลับมาดูผลลัพธ์เดิมได้
  • เตรียมความพร้อมสำหรับการทบทวนความปลอดภัยหรือการทดสอบเจาะระบบ (Penetration Testing) โดยการบันทึกนโยบายการควบคุมการแสดงผลในกรอบ (Framing Policy) ของ Third-party Dependencies

คำถามที่พบบ่อย (FAQs)

ทำไมหน้าเว็บที่ระบุสถานะว่า "สามารถฝังตัวได้" (Embeddable) ถึงยังคงแสดงเป็นหน้าว่างเปล่าในโหมดตัวอย่าง?
เฮดเดอร์ของ Response อนุญาตการทำ Framed แต่หน้าเว็บน่าจะมี JavaScript Frame-busting, จำเป็นต้องเข้าสู่ระบบหรือใช้ Cookie, หรือแสดงผลเนื้อหาที่แตกต่างกันตามผู้เข้าชมแต่ละคน ดังนั้นจึงควรเชื่อถือผลลัพธ์จากตัว Preview เป็นหลัก
สามารถ Bypass X-Frame-Options หรือ CSP ได้หรือไม่?
ไม่สามารถทำได้ เนื่องจากเบราว์เซอร์บังคับใช้เฮดเดอร์เหล่านี้เพื่อวัตถุประสงค์ด้านความปลอดภัย (ป้องกันการโจมตีแบบ Clickjacking) การหาทางออกทางฝั่งไคลเอนต์มีความไม่น่าเชื่อถือ หากต้องการฝังเนื้อหาจากแหล่งที่คุณไม่สามารถควบคุมได้ กรุณาขอให้เจ้าของเว็บไซต์เพิ่ม Origin ของคุณเข้าไปใน Allowlist ของค่า `frame-ancestors` หรือใช้งานฟีเจอร์ Embed/API ทางการที่พวกเขานำเสนอแทน
ความแตกต่างระหว่างค่า DENY และ SAMEORIGIN คืออะไร?
`DENY` จะปิดกั้นการแสดงผลหน้าเว็บในทุก iframe แม้แต่จากเว็บที่มี Same-Origin เดียวกันก็ตาม ในขณะที่ `SAMEORIGIN` จะอนุญาตให้ทำ Framed ได้เฉพาะหน้าเว็บที่มี Scheme, Host และ Port เดียวกันเท่านั้น
เครื่องมือตรวจสอบเก็บข้อมูล URL ของฉันไว้หรือไม่?
ประวัติการใช้งานจะถูกเก็บเฉพาะในแท็บเบราว์เซอร์ของคุณผ่าน `sessionStorage` เท่านั้น และจะถูกล้างออกอัตโนมัติเมื่อปิดแท็บ ส่วนเซิร์ฟเวอร์ตรวจสอบจะทำเพียงการ Fetch URL ที่ระบุมา เพื่อนำไปอ่านค่า Response Headers เท่านั้น
ทำไมการใช้ URL เดิมในเวลาที่ต่างกันจึงให้ผลลัพธ์ที่แตกต่างกัน?
เนื่องจากเว็บไซต์อาจส่ง Header ที่แตกต่างกันขึ้นอยู่กับ Path, หลังการ Redirect, ค่า User-Agent, หรือการตั้งค่าจากโปรแกรมทดสอบ A/B และกฎของ WAF (Web Application Firewall) แนะนำให้รันการตรวจสอบอีกครั้ง เพื่อดูสถานะล่าสุด ณ ปัจจุบัน