SecurityError: Zablokowano dostęp do ramki z punktem początkowym

555

Ładuję na <iframe>mojej stronie HTML i próbuję uzyskać dostęp do elementów w niej za pomocą Javascript, ale kiedy próbuję wykonać kod, pojawia się następujący błąd:

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

Czy możesz mi pomóc znaleźć rozwiązanie, aby uzyskać dostęp do elementów w ramce?

Używam tego kodu do testowania, ale na próżno:

$(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!";
    });
});
mubashermubi
źródło

Odpowiedzi:

818

Polityka tego samego pochodzenia

Nie możesz uzyskać dostępu do <iframe>innego pochodzenia za pomocą JavaScript, byłoby to ogromną wadą bezpieczeństwa, gdybyś mógł to zrobić. W przypadku przeglądarek zasad tego samego pochodzenia skrypty blokują próby uzyskania dostępu do ramki o innym pochodzeniu .

Pochodzenie uważa się za różne, jeśli nie zachowano co najmniej jednej z następujących części adresu:

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

Protokół , nazwa hosta i port muszą być takie same jak w domenie, jeśli chcesz uzyskać dostęp do ramki.

UWAGA: wiadomo, że Internet Explorer nie ściśle przestrzega tej zasady, zobacz tutaj, aby uzyskać szczegółowe informacje.

Przykłady

Oto, co by się stało, próbując uzyskać dostęp do następujących adresów URL z 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 

Obejście

Mimo że zasady tego samego pochodzenia blokują skryptom dostęp do zawartości witryn o innym pochodzeniu, jeśli jesteś właścicielem obu stron, możesz obejść ten problem, używając window.postMessagei jego względnego messagezdarzenia do wysyłania wiadomości między dwiema stronami, w następujący sposób:

  • Na twojej stronie głównej:

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

    Drugim argumentem postMessage()może być '*'wskazanie braku preferencji co do pochodzenia miejsca docelowego. W miarę możliwości zawsze należy podać miejsce docelowe, aby uniknąć ujawnienia danych wysyłanych do innych witryn.

  • W twoim <iframe>(zawartym na stronie głównej):

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

Metodę tę można zastosować w obu kierunkach , tworząc odbiornik również na stronie głównej i odbierając odpowiedzi z ramki. Ta sama logika może być również zaimplementowana w wyskakujących oknach i zasadniczo w każdym nowym oknie generowanym również przez stronę główną (np. Przy użyciu window.open()), bez różnicy.

Wyłączenie polityki tego samego pochodzenia w Twojej przeglądarce

Jest już kilka dobrych odpowiedzi na ten temat (właśnie znalazłem googlujących się w Google), więc dla przeglądarek, w których jest to możliwe, połączę odpowiedź względną. Pamiętaj jednak, że wyłączenie zasad tego samego pochodzenia wpłynie tylko na Twoją przeglądarkę . Ponadto uruchomienie przeglądarki z wyłączonymi ustawieniami zabezpieczeń tego samego pochodzenia zapewnia każdemu dostęp do strony internetowej do zasobów pochodzących z różnych źródeł, więc jest to bardzo niebezpieczne i NIGDY nie powinno być wykonywane, jeśli nie wiesz dokładnie, co robisz (np. W celach programistycznych) .

Marco Bonelli
źródło
27
Każda inna odpowiedź, którą znalazłem 1 , 2 , sugeruje, że CORS / Access-Control-Allow-Originnie dotyczy iFrames, tylko XHR, czcionek, WebGL icanvas.drawImage . Wierzę, że postMessageto jedyna opcja.
snappieT
369
Pierwszy raz widziałem operator tylda „~” w javascript. Dla każdego, kto również nie wiedział, co robi: konwertuje -1 na 0, co oszczędza konieczności robienia „! = -1” na wyniku indexOf. Osobiście myślę, że nadal będę używać „! = -1”, ponieważ inni programiści łatwiej to rozumieją i unikają błędów, które powstałyby w wyniku zapomnienia o wprowadzeniu tyldy. (Ale zawsze miło jest nauczyć się czegoś nowego.)
Redzarf,
4
@SabaAhang po prostu sprawdź iframe.src, a jeśli witryna różni się od nazwy hosta Twojej domeny, nie możesz uzyskać dostępu do tej ramki.
Marco Bonelli
17
@Snuggs całkowicie źle, ~zwraca uzupełnienie liczby 2, więc nstaje się -n-1, co oznacza, że ​​tylko -1się stanie 0(co jest interpretowane jako false), a każda inna wartość przejdzie test. IE 0 = -(-1)-1nie -(-1+1).
Marco Bonelli,
2
@ użytkownik2568374 location.ancestorOrigins[0]to lokalizacja ramki nadrzędnej. Jeśli twoja ramka działa w innej witrynie i sprawdzasz za pomocą event.origin.indexOf(location.ancestorOrigins[0]), sprawdzasz, czy początek zdarzenia zawiera adres ramki nadrzędnej, co zawsze będzietrue , dlatego zezwalasz każdemu rodzicowi o dowolnym pochodzeniu na dostęp do ramki, a to oczywiście nie jest czymś, co chcesz zrobić. Co więcej, document.referrerjest to również zła praktyka, jak już wyjaśniłem w komentarzach powyżej.
Marco Bonelli
55

Uzupełnienie odpowiedzi Marco Bonellego: najlepszy obecny sposób interakcji między ramkami / ramkami iframe window.postMessage, obsługiwany przez wszystkie przeglądarki

Geert
źródło
21
Chociaż ten link może odpowiedzieć na pytanie, lepiej dołączyć tutaj istotne części odpowiedzi i podać link w celach informacyjnych. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie. - Z recenzji
Alessandro Cuttin
9
Nie zgadzam się, @AlessandroCuttin. Wyjaśnienie, jak window.postMessagedziała, powielałoby tylko zaakceptowaną odpowiedź, do której już się odwołuję. Ponadto podstawową wartością dodaną przez moją odpowiedź jest właśnie odwołanie się do dokumentacji zewnętrznej.
Geert
5
Myślę, że lepiej, jeśli możesz edytować zaakceptowaną odpowiedź i dodać ją tam
Martin Massera
12
window.postMessage, którego możemy użyć tylko wtedy, gdy będziemy w stanie uzyskać dostęp zarówno do elementu nadrzędnego (naszej strony HTML), jak i elementu podrzędnego (iframe innej domeny). W przeciwnym razie „NIE MA MOŻLIWOŚCI”, zawsze wyświetli błąd „Wyjątek DOM nieprzechwycony: Zablokowano ramkę z origin „< twojadomena.com.com ” „od dostępu do ramki krzyżowej”.
VIJAY P
18

Sprawdź, czy na serwerze internetowym domeny znajduje się http://www.<domain>.comkonfiguracja. X-Frame-Options Jest to funkcja bezpieczeństwa zaprojektowana w celu zapobiegania atakom typu ClickJacking,

Jak działa ClickJacking?

  1. Strona zła wygląda dokładnie jak strona ofiary.
  2. Następnie nakłonił użytkowników do wprowadzenia nazwy użytkownika i hasła.

Technicznie rzecz biorąc, zło ma iframeźródło strony ofiary.

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

Jak działa funkcja bezpieczeństwa

Jeśli chcesz uniemożliwić renderowanie żądania serwera WWW w ramach iframedodania opcji ramki-x

Opcje X-Frame ODMÓW

Dostępne są następujące opcje:

  1. SAMEORIGIN // zezwalaj tylko mojej własnej domenie na renderowanie mojego HTML wewnątrz elementu iframe.
  2. DENY // nie zezwalaj na renderowanie mojego HTMLa w ramce iframe
  3. „ZEZWALAJ OD https://example.com/ ” // zezwól określonej domenie na renderowanie mojego HTML w ramce iframe

Oto przykład konfiguracji IIS:

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

Rozwiązanie pytania

Jeśli serwer WWW aktywuje funkcję bezpieczeństwa, może spowodować błąd SecurityError po stronie klienta, tak jak powinien.

Shahar Shokrani
źródło
1
Nie sądzę, że mają tu zastosowanie opcje X-Frame - Opcje X-Frame zdefiniowane przez stronę gościa (osadzoną) mogą spowodować, że rodzic odmówi załadowania strony, ale o ile wiem, nie wpływa to na javascript dostęp - nawet z opcjami X-Frame: *, nie sądzę, że będziesz mógł uzyskać dostęp do DOM strony gościa innego pochodzenia za pomocą javascript
Noah Gilmore
13

Dla mnie chciałem wdrożyć dwukierunkowy uścisk dłoni, co oznacza:
- okno nadrzędne ładuje się szybciej niż ramka iframe
- ramka iframe powinna mówić do okna nadrzędnego, gdy tylko będzie gotowe
- rodzic jest gotowy na otrzymanie komunikatu iframe i odtworzenie

ten kod służy do ustawiania białej etykiety w ramce iframe za pomocą [niestandardowej właściwości 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","*");
    }
});

rodzic

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

oczywiście możesz ograniczyć pochodzenie i tekst, jest to łatwy do pracy z kodem.
Ten przykład był pomocny:
[Wiadomości między domenami z postMessage]

Yakir Manor
źródło
mam do czynienia z problemem związanym z safari, w którym dokument w iframe wykonuje swój JS później niż strona nadrzędna, co powoduje, że wiadomość jest wysyłana wcześniej niż dokument w iframe nasłuchuje wiadomości; co jest dokładnie przeciwne do tego, co robią chrome i Firefox - czy testowałeś swój kod podczas safari na iOS? btw postMessage z drugim parametrem o wartości „*” nie jest całkiem bezpieczny, zawsze należy podać domenę
sKopheK
Twój pierwszy blok kodu to ten w ramce iframe w obiekcie nadrzędnym, czy też strona ładowana do ramki iframe?
Demonic218,
0

Chciałbym dodać konfigurację specyficzną dla Java Spring, która może mieć na to wpływ.

W witrynie sieci Web lub aplikacji Gateway istnieje ustawienie contentSecurityPolicy

wiosną można znaleźć implementację podklasy 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)

Przeglądarka zostanie zablokowana, jeśli nie zdefiniujesz tutaj bezpiecznego zewnętrznego konta.

ssp
źródło
0

Jeśli masz kontrolę nad zawartością elementu iframe - to znaczy, jeśli jest on tylko ładowany w konfiguracji krzyżowej, takiej jak w Amazon Mechanical Turk - możesz obejść ten problem za pomocą <body onload='my_func(my_arg)'>atrybutu wewnętrznego HTML.

Na przykład dla wewnętrznego html użyj thisparametru html (tak - thisjest zdefiniowane i odnosi się do okna nadrzędnego wewnętrznego elementu ciała):

<body onload='changeForm(this)'>

W wewnętrznym 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>
Zhanwen Chen
źródło
-24
  • Otwórz menu Start
  • Wpisz Windows + R lub otwórz „Uruchom
  • Wykonaj następujące polecenie.

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

sakthi sudhan
źródło
3
Dobry do szybkiego i brudnego testu!
user1068352,
6
Straszne dla wszystkiego, co nie jest szybkim i nieprzyzwoitym testem… i które już zawiera adres w przyjętej odpowiedzi.
Quentin
2
Nawet z poleceniem, to doesnt pracy, ponieważ Chrome unika wyłączenie bezpieczeństwa internetowego w ten sposób
Metafaniel