SecurityError: บล็อกเฟรมที่มีต้นกำเนิดไม่ให้เข้าถึงเฟรมข้ามเฟรม


555

ฉันกำลังโหลด<iframe>ในหน้า HTML ของฉันและพยายามเข้าถึงองค์ประกอบภายในโดยใช้ Javascript แต่เมื่อฉันพยายามรันโค้ดของฉันฉันได้รับข้อผิดพลาดต่อไปนี้:

SecurityError: Blocked a frame with origin "http://www.<domain>.com" from accessing a cross-origin frame.

คุณช่วยฉันหาวิธีแก้ปัญหาเพื่อให้ฉันสามารถเข้าถึงองค์ประกอบในเฟรมได้หรือไม่?

ฉันใช้รหัสนี้สำหรับการทดสอบ แต่ในไร้สาระ:

$(document).ready(function() {
    var iframeWindow = document.getElementById("my-iframe-id").contentWindow;

    iframeWindow.addEventListener("load", function() {
        var doc = iframe.contentDocument || iframe.contentWindow.document;
        var target = doc.getElementById("my-target-id");

        target.innerHTML = "Found it!";
    });
});

คำตอบ:


820

นโยบายแหล่งกำเนิดเดียวกัน

คุณไม่สามารถเข้าถึง<iframe>ด้วยต้นกำเนิดที่แตกต่างกันโดยใช้ JavaScript มันจะเป็นข้อบกพร่องด้านความปลอดภัยขนาดใหญ่ถ้าคุณสามารถทำได้ สำหรับเดียวกันกำเนิดนโยบาย เบราว์เซอร์กันโดยสคริปต์พยายามเข้าถึงกรอบที่มีต้นกำเนิดที่แตกต่างกัน

Origin ถือว่าแตกต่างกันหากที่อยู่อย่างน้อยหนึ่งส่วนต่อไปนี้ของที่อยู่ไม่ได้รับการปรับปรุง:

<protocol>://<hostname>:<port>/...

โพรโทคอ , ชื่อโฮสต์และพอร์ตจะต้องเหมือนกันโดเมนของคุณถ้าคุณต้องการที่จะเข้าถึงกรอบ

หมายเหตุ: Internet Explorer ไม่ได้ปฏิบัติตามกฎนี้อย่างเคร่งครัดดูรายละเอียดที่นี่

ตัวอย่าง

นี่คือสิ่งที่จะเกิดขึ้นพยายามเข้าถึง URL ต่อไปนี้จาก http://www.example.com/home/index.html

URL                                             RESULT 
http://www.example.com/home/other.html       -> Success 
http://www.example.com/dir/inner/another.php -> Success 
http://www.example.com:80                    -> Success (default port for HTTP) 
http://www.example.com:2251                  -> Failure: different port 
http://data.example.com/dir/other.html       -> Failure: different hostname 
https://www.example.com/home/index.html:80   -> Failure: different protocol
ftp://www.example.com:21                     -> Failure: different protocol & port 
https://google.com/search?q=james+bond       -> Failure: different protocol, port & hostname 

วิธีแก้ปัญหา

แม้ว่านโยบายที่มาดั้งเดิมบล็อกสคริปต์จากการเข้าถึงเนื้อหาของเว็บไซต์ที่มีต้นกำเนิดที่แตกต่างกันหากคุณเป็นเจ้าของทั้งสองหน้าคุณสามารถแก้ไขปัญหานี้โดยใช้window.postMessageและmessageเหตุการณ์ที่เกี่ยวข้องเพื่อส่งข้อความระหว่างสองหน้าดังนี้:

  • ในหน้าหลักของคุณ:

    let frame = document.getElementById('your-frame-id');
    frame.contentWindow.postMessage(/*any variable or object here*/, 'http://your-second-site.com');
    

    อาร์กิวเมนต์ที่สองที่postMessage()สามารถใช้'*'เพื่อระบุว่าไม่มีการตั้งค่าเกี่ยวกับที่มาของปลายทาง ควรให้แหล่งกำเนิดเป้าหมายเมื่อเป็นไปได้เสมอเพื่อหลีกเลี่ยงการเปิดเผยข้อมูลที่คุณส่งไปยังเว็บไซต์อื่น

  • ในของคุณ<iframe>(อยู่ในหน้าหลัก):

    window.addEventListener('message', event => {
        // IMPORTANT: check the origin of the data! 
        if (event.origin.startsWith('http://your-first-site.com')) { 
            // The data was sent from your site.
            // Data sent with postMessage is stored in event.data:
            console.log(event.data); 
        } else {
            // The data was NOT sent from your site! 
            // Be careful! Do not use it. This else branch is
            // here just for clarity, you usually shouldn't need it.
            return; 
        } 
    }); 
    

วิธีนี้สามารถใช้ได้ทั้งสองทิศทางสร้างผู้ฟังในหน้าหลักด้วยและรับการตอบกลับจากเฟรม ตรรกะเดียวกันนี้ยังสามารถนำมาใช้ในป๊อปอัปและโดยทั่วไปหน้าต่างใหม่ที่สร้างโดยหน้าหลัก (เช่นใช้window.open()) เช่นกันโดยไม่มีความแตกต่าง

ปิดการใช้งานนโยบายกำเนิดเดียวกันในเบราว์เซอร์ของคุณ

มีคำตอบที่ดีเกี่ยวกับหัวข้อนี้อยู่แล้ว (ฉันเพิ่งพบพวกเขา googling) ดังนั้นสำหรับเบราว์เซอร์ที่เป็นไปได้ฉันจะเชื่อมโยงคำตอบที่เกี่ยวข้อง อย่างไรก็ตามโปรดจำไว้ว่าการปิดใช้งานนโยบายต้นทางเดียวกันจะส่งผลต่อเบราว์เซอร์ของคุณเท่านั้น นอกจากนี้การทำงานของเบราเซอร์ที่มีต้นกำเนิดเดียวกันตั้งค่าความปลอดภัยเงินอุดหนุนผู้พิการใด ๆการเข้าถึงเว็บไซต์ที่ทรัพยากรข้ามกำเนิดดังนั้นมันเป็นเรื่องที่ไม่ปลอดภัยมากและไม่ควรทำถ้าคุณไม่ได้รู้ว่าสิ่งที่คุณกำลังทำ (เช่นเพื่อการพัฒนา)


27
คำตอบใด ๆ อื่น ๆ ที่ฉันได้พบ1 , 2แสดงให้เห็นว่า ธ / Access-Control-Allow-Originใช้ไม่ได้กับ iFrames เท่านั้นที่จะXHRs canvas.drawImageแบบอักษรและ ฉันเชื่อว่าpostMessageเป็นตัวเลือกเดียว
snappieT

369
ครั้งแรกที่ฉันเห็นตัวดำเนินการตัวหนอน "~" ในจาวาสคริปต์ สำหรับคนอื่นที่ยังไม่รู้ว่ามันทำอะไร: มันแปลง -1 เป็น 0 ซึ่งช่วยให้คุณไม่ต้องทำ "! = -1" จากผลลัพธ์ของ indexOf โดยส่วนตัวแล้วฉันคิดว่าฉันจะใช้ "! = -1" ต่อไปเพราะมันง่ายกว่าสำหรับโปรแกรมเมอร์คนอื่น ๆ ที่จะเข้าใจและหลีกเลี่ยงข้อบกพร่องที่มาจากการลืมใส่เครื่องหมายตัวหนอน (แต่ก็ดีที่จะเรียนรู้สิ่งใหม่ ๆ เสมอ)
Redzarf

4
@SabaAhang เพียงตรวจสอบiframe.srcและถ้าไซต์นั้นแตกต่างจากชื่อโฮสต์ของโดเมนของคุณคุณจะไม่สามารถเข้าถึงเฟรมนั้นได้
Marco Bonelli

17
@Snuggs ผิดทั้งหมด~คืนค่าส่วนที่ 2 ของจำนวนนั้นดังนั้นnจะกลายเป็น-n-1หมายความว่า-1จะกลายเป็นเท่านั้น0(ซึ่งถูกตีความว่าเป็นfalse) และค่าอื่น ๆ จะผ่านการทดสอบ IE ไม่0 = -(-1)-1 -(-1+1)
Marco Bonelli

2
@ user2568374 location.ancestorOrigins[0]เป็นที่ตั้งของเฟรมหลัก หากกรอบของคุณกำลังทำงานอยู่ภายในเว็บไซต์อื่นและคุณสามารถตรวจสอบการใช้event.origin.indexOf(location.ancestorOrigins[0])ที่คุณกำลังตรวจสอบว่าจุดเริ่มต้นของเหตุการณ์ที่เกิดขึ้นมีอยู่กรอบของแม่ซึ่งเป็นไปได้เสมอtrueดังนั้นคุณจะช่วยให้ผู้ปกครองที่มีต้นกำเนิดใด ๆในการเข้าถึงกรอบของคุณและนี้ เห็นได้ชัดว่าไม่ใช่สิ่งที่คุณต้องการจะทำ ยิ่งไปกว่านั้นdocument.referrerคือการปฏิบัติที่ไม่ดีเช่นเดียวกับที่ฉันได้อธิบายไว้ในความคิดเห็นข้างต้น
Marco Bonelli

55

คำตอบของการพึ่งพา Marco Bonelli ของ: วิธีที่ดีที่สุดในปัจจุบันของการโต้ตอบระหว่างเฟรม / iframe ที่ใช้window.postMessage, การสนับสนุนจากเบราว์เซอร์


21
แม้ว่าลิงก์นี้อาจตอบคำถามได้ดีกว่าหากรวมส่วนสำคัญของคำตอบไว้ที่นี่และให้ลิงก์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง - จากรีวิว
Alessandro Cuttin

9
ฉันไม่เห็นด้วย @AlessandroCuttin การอธิบายวิธีการwindow.postMessageทำงานจะทำซ้ำคำตอบที่ยอมรับซึ่งฉันอ้างอิงอยู่แล้วเท่านั้น นอกจากนี้คุณค่าที่สำคัญที่คำตอบของฉันเพิ่มก็คือการอ้างอิงเอกสารภายนอก
Geert

5
ฉันคิดว่ามันจะดีกว่าถ้าคุณสามารถแก้ไขคำตอบที่ยอมรับได้และเพิ่มไว้ที่นั่น
Martin Massera

12
window.postMessage เราสามารถใช้ได้เฉพาะในกรณีที่เราสามารถเข้าถึงทั้งผู้ปกครอง (หน้า HTML ของเรา) และองค์ประกอบลูก (iframe โดเมนอื่น ๆ ) มิฉะนั้น "ไม่มีความเป็นไปได้" มันจะส่งข้อผิดพลาดเสมอ "Uncaught DOMException: บล็อกเฟรม ด้วยจุดเริ่มต้น "< yourdomainname.com >" จากการเข้าถึงเฟรมข้ามจุดกำเนิด "
VIJAY P

19

ตรวจสอบการhttp://www.<domain>.comกำหนดค่าเว็บเซิร์ฟเวอร์ของโดเมนX-Frame-Options ว่าเป็นคุณลักษณะความปลอดภัยที่ออกแบบมาเพื่อป้องกันการคลิกการโจมตีแบบโจมตี

clickJacking ทำงานอย่างไร

  1. หน้าปีศาจดูเหมือนหน้าเหยื่อ
  2. จากนั้นมันหลอกผู้ใช้ให้ป้อนชื่อผู้ใช้และรหัสผ่าน

ในทางเทคนิคความชั่วร้ายมีiframeแหล่งที่มากับหน้าเหยื่อ

<html>
    <iframe src='victim_domain.com'/>
    <input id="username" type="text" style="display: none;/>
    <input id="password" type="text" style="display: none;/>
    <script>
        //some JS code that click jacking the user username and input from inside the iframe...
    <script/>
<html>

คุณลักษณะความปลอดภัยทำงานอย่างไร

หากคุณต้องการป้องกันการแสดงผลเว็บเซิร์ฟเวอร์ภายในการiframeเพิ่มตัวเลือก x-frame

ตัวเลือก X-Frame DENY

ตัวเลือกคือ:

  1. SAMEORIGIN // อนุญาตให้โดเมนของฉันแสดง HTML ของฉันภายใน iframe เท่านั้น
  2. DENY // ไม่อนุญาตให้ HTML ของฉันแสดงผลภายใน iframe ใด ๆ
  3. "ALLOW-FROM https://example.com/ " // อนุญาตให้โดเมนเฉพาะแสดง HTML ของฉันภายใน iframe

นี่คือตัวอย่างการกำหนดค่า IIS:

   <httpProtocol>
       <customHeaders>
           <add name="X-Frame-Options" value="SAMEORIGIN" />
       </customHeaders>
   </httpProtocol>

ทางออกสำหรับคำถาม

หากเว็บเซิร์ฟเวอร์เปิดใช้งานคุณลักษณะความปลอดภัยอาจทำให้ SecurityError ฝั่งไคลเอ็นต์ได้ตามที่ควร


1
ฉันไม่คิดว่า X-Frame-Options ใช้ที่นี่ - X-Frame-Options ที่กำหนดโดยหน้าผู้เยี่ยมชม (ฝัง) อาจทำให้ผู้ปกครองปฏิเสธที่จะโหลดหน้า แต่เท่าที่ฉันรู้ว่ามันไม่มีผลกับจาวาสคริปต์ การเข้าถึง - ถึงแม้จะมี X-Frame-Options: * ฉันไม่คิดว่าคุณจะสามารถเข้าถึง DOM ของหน้าผู้มาเยี่ยมด้วย javascript ได้
โนอาห์กิลมอร์

13

สำหรับฉันฉันต้องการใช้ handshake 2 ทางความหมาย:
- หน้าต่างหลักจะโหลดเร็วขึ้นจากนั้น iframe
- iframe ควรพูดคุยกับหน้าต่างหลักทันทีที่พร้อม
- ผู้ปกครองพร้อมที่จะรับข้อความ iframe และเล่นซ้ำ

รหัสนี้ใช้เพื่อตั้งค่า white label ใน iframe โดยใช้[CSS custom properties]
code:
iframe

$(function() {
    window.onload = function() {
        // create listener
        function receiveMessage(e) {
            document.documentElement.style.setProperty('--header_bg', e.data.wl.header_bg);
            document.documentElement.style.setProperty('--header_text', e.data.wl.header_text);
            document.documentElement.style.setProperty('--button_bg', e.data.wl.button_bg);
            //alert(e.data.data.header_bg);
        }
        window.addEventListener('message', receiveMessage);
        // call parent
        parent.postMessage("GetWhiteLabel","*");
    }
});

ผู้ปกครอง

$(function() {
    // create listener
    var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
    var eventer = window[eventMethod];
    var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
    eventer(messageEvent, function (e) {
        // replay to child (iframe) 
        document.getElementById('wrapper-iframe').contentWindow.postMessage(
            {
                event_id: 'white_label_message',
                wl: {
                    header_bg: $('#Header').css('background-color'),
                    header_text: $('#Header .HoverMenu a').css('color'),
                    button_bg: $('#Header .HoverMenu a').css('background-color')
                }
            },
            '*'
        );
    }, false);
});

ตามธรรมชาติคุณสามารถ จำกัด ต้นกำเนิดและข้อความได้รหัสนี้ใช้งานง่ายด้วย
ฉันพบว่าแบบทดสอบนี้มีประโยชน์:
[การส่งข้อความข้ามโดเมนด้วยข้อความ]


ฉันกำลังจัดการกับปัญหากับซาฟารีที่เอกสารใน iframe กำลังดำเนินการ JS หลังจากหน้าหลักซึ่งทำให้ข้อความที่จะส่งก่อนหน้ากว่าเอกสารใน iframe กำลังฟังข้อความ; ซึ่งตรงข้ามกับสิ่งที่โครเมียมและ Firefox ทำ - คุณได้ทดสอบโค้ดของคุณใน Safari บน iOS หรือไม่? btw postMessage ที่มีพารามิเตอร์ตัวที่สองของค่า "*" ไม่ปลอดภัยคุณควรระบุโดเมนเสมอ
sKopheK

โค้ดบล็อกแรกของคุณอยู่ที่ iframe ในพาเรนต์หรืออยู่บนเพจที่โหลดลงใน iframe?
Demonic218

0

ฉันต้องการเพิ่มการกำหนดค่าเฉพาะของ Java Spring ที่สามารถมีผลกับสิ่งนี้

ในเว็บไซต์หรือแอปพลิเคชันเกตเวย์มีการตั้งค่า contentSecurityPolicy

ใน Spring คุณสามารถใช้งานคลาสย่อยของ WebSecurityConfigurerAdapter

contentSecurityPolicy("
script-src 'self' [URLDomain]/scripts ; 
style-src 'self' [URLDomain]/styles;
frame-src 'self' [URLDomain]/frameUrl...

...

.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)

เบราว์เซอร์จะถูกบล็อกหากคุณไม่ได้กำหนด contenet ภายนอกที่ปลอดภัยที่นี่


0

หากคุณมีการควบคุมเนื้อหาของ iframe - นั่นคือถ้ามันถูกโหลดในการตั้งค่าข้ามจุดกำเนิดเช่นบน Amazon Mechanical Turk - คุณสามารถหลีกเลี่ยงปัญหานี้ได้ด้วย<body onload='my_func(my_arg)'>แอตทริบิวต์สำหรับ HTML ภายใน

ตัวอย่างเช่นสำหรับ html ภายในให้ใช้thisพารามิเตอร์ html (ใช่ - thisถูกกำหนดและมันหมายถึงหน้าต่างหลักขององค์ประกอบร่างกายภายใน):

<body onload='changeForm(this)'>

ใน HTML ภายใน:

    function changeForm(window) {
        console.log('inner window loaded: do whatever you want with the inner html');
        window.document.getElementById('mturk_form').style.display = 'none';
    </script>

-24
  • เปิดเมนูเริ่มต้น
  • พิมพ์ windows + R หรือเปิด "Run"
  • ดำเนินการคำสั่งต่อไปนี้

chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security


3
ดีสำหรับการทดสอบที่รวดเร็วและสกปรก!
user1068352

6
แย่มากสำหรับทุกอย่างที่ไม่ใช่การทดสอบที่รวดเร็วและสกปรก…และตอบคำถามที่ตอบแล้ว
Quentin

2
แม้ว่าจะมีคำสั่ง แต่ก็ไม่ทำงานเนื่องจาก Chrome หลีกเลี่ยงการปิดใช้งานการรักษาความปลอดภัยเว็บด้วยวิธีนี้
Metafaniel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.