Cross domain

A document or script view in one domain requests resources in another domain

  1. Resource jump: A link, redirect, form submission
  2. Resource embedding:<link> , <script> , <img> , <frame>domTags, and styles such as background: URL (), @font-face() and so on
  3. Script request:jsLaunched by theajaxThe request,domjsObject for a cross-domain request

The same-origin policy

Introduced by NetScape in 1995, the browser is the core and most basic security feature of the browser. Without this feature, browsers are vulnerable to XSS, CSFR, etc. Same-origin indicates that the protocol, domain name, and port are the same. Even if two domain names point to the same IP address, they are not same-origin.

The same origin policy restricts several behaviors

  1. Cookie.LocalStorageIndexDBUnable to read
  2. DOMJSObject not available
  3. AJAXRequest cannot be sent

Cross-domain solutions

  1. Cross domains via JSONP

    JSONP can only get requests

    <script src="http://www.b.com/request?callback=jsonPCallBack"> function jsonPCallBack(content) {... } </script>Copy the code
    $.ajax("http://www.b.com/request", {
      jsonpCallback: "moty".dataType: "jsonp".success: function(json) {...}
    });
    Copy the code
  2. Document.domain + iframe cross domain

    A. The two domains must belong to the same level-1 domain. And the protocol used, port must be consistent, otherwise document.domain can not be used for cross-domain.

    B. If it is a sub-domain name of the same level, such as m.a.com or api.a.com, you need to set the domain in javascript

    document.domain = 'a.com';
    Copy the code

    News. HTML page under news.baidu.com:

    <script>
        document.domain = 'baidu.com';
        var ifr = document.createElement('iframe');
        ifr.src = 'map.baidu.com/map.html';
        ifr.style.display = 'none';
        document.body.appendChild(ifr);
        ifr.onload = function(){
            var doc = ifr.contentDocument || ifr.contentWindow.document;
            // Here you can operate the map. HTML page under map.baidu.com
            var oUl = doc.getElementById('ul1');
            alert(oUl.innerHTML);
            ifr.onload = null;
        };
    </script>
    Copy the code

    Map. HTML page under map.baidu.com:

    <ul id="ul1">I am UL in MAP.baidu.com</ul>
    <script>
    	document.domain = 'baidu.com';
    </script>
    Copy the code
  3. location.hash + iframe

    The idea is to use location.hash to pass values.

    Hash (‘ # helloWorld ‘in url: a.com# helloWord) is location.hash (‘ # helloWorld’ in url: a.com# helloWord).

    If the file cs1. HTML under the domain name a.com is to communicate with the cs2. HTML under the domain name cnblogs.com, cs1. HTML first creates a hidden iframe. The SRC of the iframe points to the cs2. HTML page under the domain name cnblogs.com. The hash value can then be used as an argument.

    Cs2. HTML responds to the request by changing the hash value of cs1. HTML to pass the data. Firefox can be modified).

    At the same time, add a timer on cs1.html to determine whether the value of location.hash has changed at intervals. If there is a change at one point, the hash value will be obtained.

    Cs1.html under A.html

    function startRequest(){
        var ifr = document.createElement('iframe');
        ifr.style.display = 'none';
        ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';
        document.body.appendChild(ifr);
    }
     
    function checkHash() {
        try {
            var data = location.hash ? location.hash.substring(1) : ' ';
            if (console.log) {
                console.log('Now the data is '+data); }}catch(e) {};
    }
    setInterval(checkHash, 2000);
    Copy the code

    Cnblogs.com the cs2. HTML

    // Simulate a simple parameter handling operation
    switch(location.hash){
        case '#paramdo':
            callBack();
            break;
        case '#paramset':
            / / do something...
            break;
    }
     
    function callBack(){
        try {
            parent.location.hash = 'somedata';
        } catch (e) {
            // The security mechanism of Internet Explorer and Chrome cannot be modified with parent-location. hash,
            // So use an intermediate proxy iframe under the CNBlogs domain
            var ifrproxy = document.createElement('iframe');
            ifrproxy.style.display = 'none';
             // Note that the file is in the "a.com" field
            ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata';   
            document.body.appendChild(ifrproxy); }}Copy the code

    Cs3.html under A.html

    // Since parent. Parent belongs to the same domain as itself, we can change its location.hash value
    parent.parent.location.hash = self.location.hash.substring(1);
    Copy the code
  4. window.name + iframe

    Window.name (usually found in js code) is not a normal global variable, but the name of the current window. Note that each iframe has its enclosing window, which is a child of top Window. The magic of the window.name attribute is that the name value persists from page to page (and even from domain to domain), and it can support very long name values (2MB).

    window.name = 'abc';
    window.name; // abc
    window.location = 'http://www.baidu.com';
    window.name; // abc
    Copy the code

    A.html and proxy. HTML belong to the same domain, and B.html belongs to another domain

    B.h HTML content

    <script>
    	window.name = 'b.html\'s data';
    </script>
    Copy the code

    a.html

    <script type="text/javascript">
    var otherLoaded = false,
    iframe = document.createElement('iframe'),
    loadfn = function() {
        if (otherLoaded) {
            var data = iframe.contentWindow.name; // Read data
            alert(data); // Pop up B.html's data
    		// Clean up
            iframe.contentWindow.document.write(' ');
            iframe.contentWindow.close();
            document.body.removeChild(iframe);
        } else if(! otherLoaded) { otherLoaded =true;
            // Set the proxy file
            iframe.contentWindow.location = "http://localhost:8001/proxy.html"; }}; iframe.src ='http://localhost:8002/b.html';
    if (iframe.attachEvent) {
        iframe.attachEvent('onload', loadfn);
    } else {
        iframe.onload = loadfn;
    }
    document.body.appendChild(iframe);
    </script>
    Copy the code

    As you can see, the first time sets the address at the iframe b.h HTML, so b.h HTML will be loaded in, but not directly access the iframe. ContentWindow. Name, Because a.h HTML and b.h HTML currently different source, if change the loadfn implementation to var data = iframe. ContentWindow. Name; , will come up with this error:

    a.html: 
    Uncaught DOMException: Blocked a frame with origin "http://localhost:8001" from accessing a cross-origin frame.
    Copy the code

    Then what can we do, since different source, homologous to bai, so change the iframe address to and a.h HTML homologous proxy. HTML, due to the window. The name in the address change as the same, so the iframe. ContentWindow. The value of the name or the previous value, That is, the value of the B.HTML window, while meeting the homology requirements, so it can be accessed successfully.

  5. postMessage

    Window. postMessage allows programmers to send data messages between two Windows /frames across domains. Basically, it’s like Cross-domain AJAX, but instead of interacting between the browser and the server, it communicates between two clients. To be safe, you should verify the source when receiving a message, which is either Event. origin or event.source.

    a.html

    <h1 class="header">page A</h1>
    <div class="mb20">
        <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
        <button style="font-size:20px;" onclick="send()">post message</button>
    </div>
    <iframe src="http://localhost:9022/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>
    
    <script>
    	function send() {
        	var data = document.querySelector('#data').value;
    		// Trigger a messag event across domain subpages
       	 	window.frames[0].postMessage(data, 'http://localhost:9022/'); 
    	}
    
        window.addEventListener('message'.function(messageEvent) {
            if(messageEvent.origin ! = ='http://localhost:9022/') return
        	var data = messageEvent.data; 
        	console.info('message from child:', data);
    	}, false);
    </script>
    Copy the code

    b.html

    <h1 class="header">page B</h1>
    
    <input type="text" id="inp" value="some contents..">
    <button onclick="send()">send</button>
    <script>
    window.addEventListener('message'.function(ev) {
        if(ev.source ! = =window.parent) {return; }var data = ev.data;
        console.info('message from parent:', data);
    }, false);
    
    function send() {
        var data = document.querySelector('#inp').value;
        // If the domain name of the parent page is different from the specified one, postMessage fails
        window.parent.postMessage(data, 'http://localhost:9011/'); 
        // Trigger the message event for the parent page
        // parent.postMessage(data, '*'); 
    }
    </script>
    Copy the code
  6. Cross-domain Resource Sharing (CORS)

    CORS adds a series of HTTP headers so that the server can declare which sources can access resources on the server. Requests other than GET will send a pre-request in the form of OPTIONS request, so as to know the HTTP method supported by the server for resource requests. Under the condition that the server allows cross-domain resource requests, Send a real request as an actual HTTP request.

    1. Request header:

      Origin :

      Ordinary HTTP requests also carry the Origin information in CORS for backend comparison, indicating the source domain.

      Access-Control-Request-Method :

      Next requested methods, such as PUT, DELETE, and so on

      Access-Control-Request-Headers :

      Custom headers. All headers set with the setRequestHeader method will be contained in this header separated by commas

    2. The HTTP response headers

      The browser then determines whether to send a non-simple request based on the value returned by the server. Simple requests are sent directly, with an additional origin field indicating the origin of the cross-domain request. The server then returns the result with the following control fields after processing the request

      Access-Control-Allow-Origin :

      Domains that can be accessed across domains can be a list of domains or the wildcard “*”. Note that Origin only applies to domain names, not subdirectories. The http://foo.example/subdir/ is invalid. However, subdomain names must be set separately. For details, see the same Origin policy

      Access-Control-Allow-Credentials :

      Whether requests with authentication information are allowed. When the withCredentials flag of XMLHttpRequest requests is set to true, the browser gives the data to the script only after authentication is passed.

      Access-Control-Expose-Headers :

      Return headers that allow the script to access information about these headers in XMLHttpRequest after a successful request

      Access-Control-Max-Age :

      The number of seconds to cache this request. In this time range, all requests of the same type will no longer send a precheck request and will use the returned header as the basis for judgment, which is very useful, greatly optimizing the number of requests

      Access-Control-Allow-Methods :

      The allowed request methods, separated by commas

      Access-Control-Allow-Headers :

      Custom headers, separated by commas, are case insensitive

  7. Nginx agents cross domains

  8. Nodejs middleware proxies cross domains

  9. The Websocket protocol is cross-domain