SecurityError: Đã chặn một khung có nguồn gốc truy cập vào khung có nguồn gốc chéo


556

Tôi đang tải một <iframe>trang HTML của mình và cố gắng truy cập các phần tử bên trong nó bằng Javascript, nhưng khi tôi cố gắng thực thi mã của mình, tôi gặp lỗi sau:

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

Bạn có thể vui lòng giúp tôi tìm một giải pháp để tôi có thể truy cập các yếu tố trong khung không?

Tôi đang sử dụng mã này để thử nghiệm, nhưng vô ích:

$(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!";
    });
});

Câu trả lời:


821

Chính sách cùng nguồn gốc

Bạn không thể truy cập một <iframe>nguồn gốc khác bằng JavaScript, đó sẽ là một lỗ hổng bảo mật rất lớn nếu bạn có thể làm điều đó. Đối với các trình duyệt chính sách cùng nguồn gốc, các tập lệnh chặn các tập lệnh đang cố truy cập vào một khung có nguồn gốc khác .

Nguồn gốc được coi là khác nhau nếu ít nhất một trong các phần sau của địa chỉ không được duy trì:

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

Giao thức , tên máy chủcổng phải giống nhau trong tên miền của bạn, nếu bạn muốn truy cập vào một khung.

LƯU Ý: Internet Explorer được biết là không tuân thủ nghiêm ngặt quy tắc này, xem tại đây để biết chi tiết.

Ví dụ

Đây là những gì sẽ xảy ra khi cố gắng truy cập các URL sau từ 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 

Giải pháp thay thế

Mặc dù chính sách cùng nguồn gốc ngăn các tập lệnh truy cập nội dung của các trang web có nguồn gốc khác nhau, nếu bạn sở hữu cả hai trang, bạn có thể giải quyết vấn đề này bằng cách sử dụng window.postMessagemessagesự kiện tương đối của nó để gửi tin nhắn giữa hai trang, như sau:

  • Trong trang chính của bạn:

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

    Đối số thứ hai postMessage()có thể là '*'chỉ ra không có ưu tiên nào về nguồn gốc của điểm đến. Nguồn gốc mục tiêu phải luôn được cung cấp khi có thể, để tránh tiết lộ dữ liệu bạn gửi đến bất kỳ trang web nào khác.

  • Trong của bạn <iframe>(có trong trang chính):

    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; 
        } 
    }); 

Phương pháp này có thể được áp dụng theo cả hai hướng , tạo cả người nghe trong trang chính và nhận phản hồi từ khung. Logic tương tự cũng có thể được triển khai trong cửa sổ bật lên và về cơ bản, bất kỳ cửa sổ mới nào được tạo bởi trang chính (ví dụ: sử dụng window.open()) cũng không có sự khác biệt.

Vô hiệu hóa chính sách cùng nguồn gốc trong trình duyệt của bạn

Đã có một số câu trả lời hay về chủ đề này (tôi chỉ thấy chúng googling), vì vậy, đối với các trình duyệt có thể, tôi sẽ liên kết câu trả lời tương đối. Tuy nhiên, hãy nhớ rằng việc vô hiệu hóa chính sách cùng nguồn gốc sẽ chỉ ảnh hưởng đến trình duyệt của bạn . Ngoài ra, việc chạy trình duyệt có cài đặt bảo mật cùng nguồn gốc đã vô hiệu hóa cấp cho bất kỳ trang web nào truy cập tài nguyên nguồn gốc chéo, vì vậy nó rất không an toàn và KHÔNG BAO GIỜ nên được thực hiện nếu bạn không biết chính xác những gì bạn đang làm (ví dụ: mục đích phát triển) .


27
Bất kỳ câu trả lời nào khác tôi đã tìm thấy 1 , 2 , cho thấy CORS / Access-Control-Allow-Originkhông áp dụng cho iFrames, chỉ áp dụng cho XHRs, Phông chữ, WebGL vàcanvas.drawImage . Tôi tin postMessagelà lựa chọn duy nhất.
snappieT

369
Lần đầu tiên tôi thấy toán tử dấu "~" trong javascript. Đối với bất kỳ ai khác cũng không biết nó làm gì: nó chuyển đổi -1 thành 0, điều này giúp bạn tiết kiệm "! = -1" trên kết quả của indexOf. Cá nhân, tôi nghĩ rằng tôi sẽ tiếp tục sử dụng "! = -1" vì các lập trình viên khác dễ hiểu hơn và tránh các lỗi sẽ đến từ việc quên đặt dấu ngã vào. (Nhưng thật tuyệt khi học được điều gì đó mới.)
Redzarf

4
@SabaAhang chỉ cần kiểm tra iframe.srcvà nếu trang web đó khác với tên máy chủ của tên miền thì bạn không thể truy cập vào khung đó.
Marco Bonelli

18
@Snuggs hoàn toàn sai, ~trả về số bù 2 của số, vì vậy ntrở thành -n-1, nghĩa là chỉ -1trở thành 0(được hiểu là false) và bất kỳ giá trị nào khác sẽ vượt qua bài kiểm tra. IE 0 = -(-1)-1, không phải -(-1+1).
Marco Bonelli

2
@ user2568374 location.ancestorOrigins[0]là vị trí của khung cha. Nếu khung của bạn đang chạy bên trong một trang web khác và bạn kiểm tra sử dụng event.origin.indexOf(location.ancestorOrigins[0])bạn đang kiểm tra nếu nguồn gốc của sự kiện này chứa địa chỉ của khung của cha mẹ, mà luôn luôn sẽtrue , do đó bạn đang cho phép bất kỳ cha mẹ với bất kỳ nguồn gốc để truy cập khung hình của bạn, và điều này rõ ràng không phải là điều bạn muốn làm. Hơn nữa, document.referrerlà thực hành xấu quá, như tôi đã giải thích trong các ý kiến ​​trên.
Marco Bonelli

55

Bổ sung cho câu trả lời của Marco Bonelli: cách tương tác tốt nhất hiện nay giữa các khung / iframe đang được sử dụng window.postMessage, được hỗ trợ bởi tất cả các trình duyệt


21
Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi. - Từ đánh giá
Alessandro Cuttin

9
Tôi không đồng ý, @AlessandroCuttin. Giải thích cách làm window.postMessageviệc sẽ chỉ nhân đôi câu trả lời được chấp nhận mà tôi đã tham khảo. Hơn nữa, giá trị thiết yếu mà câu trả lời của tôi thêm vào chính xác là tham chiếu tài liệu bên ngoài.
Geert

5
Tôi nghĩ sẽ tốt hơn nếu bạn có thể chỉnh sửa câu trả lời được chấp nhận và thêm nó vào đó
Martin Massera

12
window.postMessage chúng ta chỉ có thể sử dụng nếu chúng ta có thể truy cập cả phần tử cha mẹ (trang HTML của chúng ta) và phần tử con (iframe tên miền khác). Ngược lại, "KHÔNG CÓ KHẢ NĂNG", nó sẽ luôn đưa ra lỗi "Uncaught DOMException: Bị chặn một khung với nguồn gốc "< yourdomainname.com >" từ việc truy cập khung nguồn gốc chéo. "
VIJAY P

19

Kiểm tra http://www.<domain>.comcấu hình máy chủ web của tên miền. X-Frame-Options Đây là một tính năng bảo mật được thiết kế để ngăn chặn các cuộc tấn công clickJacking,

ClickJacking hoạt động như thế nào?

  1. Trang ác trông giống hệt trang nạn nhân.
  2. Sau đó, nó lừa người dùng nhập tên người dùng và mật khẩu của họ.

Về mặt kỹ thuật, cái ác có một iframenguồn đến trang nạn nhân.

<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>

Tính năng bảo mật hoạt động như thế nào

Nếu bạn muốn ngăn yêu cầu máy chủ web được hiển thị trong một tùy chọniframe thêm khung hình x

X-Frame-Tùy chọn DENY

Các tùy chọn là:

  1. SAMEORIGIN // chỉ cho phép tên miền của riêng tôi hiển thị HTML của tôi trong iframe.
  2. DENY // không cho phép HTML của tôi được hiển thị bên trong bất kỳ iframe nào
  3. "ALLOW-TỪ https://example.com/ " // cho phép tên miền cụ thể hiển thị HTML của tôi trong iframe

Đây là ví dụ cấu hình IIS:

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

Giải pháp cho câu hỏi

Nếu máy chủ web kích hoạt tính năng bảo mật, nó có thể gây ra SecurityError phía máy khách.


1
Tôi không nghĩ rằng Tùy chọn khung X áp dụng ở đây - Tùy chọn khung X được xác định bởi trang khách (được nhúng) có thể khiến phụ huynh từ chối tải trang, nhưng theo tôi biết thì nó không ảnh hưởng đến javascript truy cập - ngay cả với Tùy chọn khung X: *, tôi không nghĩ bạn sẽ có thể truy cập DOM của một trang khách gốc khác bằng javascript
Noah Gilmore

13

Đối với tôi, tôi muốn thực hiện bắt tay 2 chiều, nghĩa là:
- cửa sổ cha mẹ sẽ tải nhanh hơn iframe
- iframe sẽ nói chuyện với cửa sổ cha mẹ ngay khi nó sẵn sàng
- cha mẹ sẵn sàng nhận thông báo iframe và phát lại

mã này được sử dụng để đặt nhãn trắng trong iframe bằng mã [Thuộc tính tùy chỉnh CSS]
:
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","*");
    }
});

cha mẹ

$(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);
});

tự nhiên bạn có thể giới hạn nguồn gốc và văn bản, đây là mã dễ làm việc với mã
tôi thấy bài kiểm tra này rất hữu ích:
[Nhắn tin tên miền với postMessage]


Tôi đang xử lý một vấn đề với safari trong đó tài liệu trong iframe đang thực thi JS của nó muộn hơn trang mẹ khiến tin nhắn được gửi sớm hơn tài liệu trong iframe đang nghe tin nhắn; điều này hoàn toàn trái ngược với những gì chrome và firefox làm - bạn đã kiểm tra mã của mình trong safari trên ios chưa? btw postMessage với tham số thứ hai của giá trị "*" không hoàn toàn an toàn, bạn phải luôn chỉ định tên miền
sKopheK

Khối mã đầu tiên của bạn, đó là trên iframe trong phần cha mẹ hay nó nằm trên trang được tải vào iframe?
Demonic218

0

Tôi muốn thêm cấu hình cụ thể của Java Spring có thể ảnh hưởng đến điều này.

Trong trang Web hoặc ứng dụng Gateway có cài đặt contentSecurityPolicy

trong Spring, bạn có thể tìm thấy việc triển khai lớp con WebSecurityConfigurerAdOG

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

...

.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)

Trình duyệt sẽ bị chặn nếu bạn chưa xác định contenet bên ngoài an toàn ở đây.


0

Nếu bạn có quyền kiểm soát nội dung của iframe - nghĩa là, nếu nó chỉ được tải trong một thiết lập có nguồn gốc chéo như trên Amazon Mechanical Turk - bạn có thể tránh vấn đề này bằng <body onload='my_func(my_arg)'>thuộc tính cho html bên trong.

Ví dụ: đối với html bên trong, hãy sử dụng thistham số html (có - thisđược xác định và nó đề cập đến cửa sổ cha của phần tử cơ thể bên trong):

<body onload='changeForm(this)'>

Trong html bên trong:

    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>

-25
  • Mở menu bắt đầu
  • Nhập windows + R hoặc mở "Chạy
  • Thực hiện lệnh sau.

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


3
Tốt cho thử nghiệm nhanh chóng và bẩn!
dùng1068352

6
Thật khủng khiếp cho bất cứ điều gì không phải là một thử nghiệm nhanh chóng và bẩn thỉu và đã giải quyết trong câu trả lời được chấp nhận.
Quentin

2
Ngay cả với lệnh, nó cũng không hoạt động vì Chrome tránh vô hiệu hóa bảo mật web theo cách này
Metafaniel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.