There are two myths about cross-domain:

1. ✕ dynamic requests will have cross-domain problems

Right in front of new bookmarks. Right in front of new bookmarks. Right in front of new bookmarks

2. ✕ cross-domain requests cannot be sent out

The request can be sent, the server can receive the request and return the result normally, but the result is blocked by the browser

The reason for cross-domain communication is that the same-origin policy requires the same source. That is, the protocols, domain names, and port numbers are the same.

As shown in the figure below:

The domain names, protocols, and port numbers of the three sources are different. As a result, the three sources are restricted by the same origin policy.

What exactly does the same origin policy restrict?

1. Cannot request data from a service that works at a different source (client to server)

Here is a question that has been bothering me for a long time. Why can home.com load cdn.home.com/index.js to send requests to Home.com without crossing domains? In fact, the JS loaded by home.com works in home.com, and its source is not the CDN that provides JS, so there is no cross-domain problem at this time, and script tags can load non-same origin resources, not affected by the same origin policy.

2. BOM and DOM such as document/cookie of different sources cannot be obtained. In other words, any information about another source cannot be obtained (client to client).


Why is there a same-origin policy?

1. Why restrict requests from different sources?

If a user logs in to Bank.com and opens evil.com at the same time, if there is no restriction, evil.com can request any information from Bank.com, and then evil.com can send transfer requests to Bank.com, etc.

If so, why not just restrict the writes and only the reads?

Because if you can’t even send the request, you can’t do cross-domain resource sharing, you can’t read the return result, and evil.com can’t proceed to the next step, such as obtaining the necessary validation information for the transfer request.

2. Why is cross-domain DOM reading restricted?

If not restricted, then it is easy to disguise other sites, such as an iframe or window.open, to get user actions and inputs such as account and password.

In addition, adding this HTTP header can restrict others from putting your site into its iframe:

X-Frame-Options: SAMEORIGIN


The same origin policy provides security but also causes inconvenience, because sometimes we need to make cross-domain requests, such as obtaining service information provided by a third party, because the source of the third party is different from the source of our website, so we are subject to cross-domain restrictions.

The most common cross-domain method should be CORS, as shown in the figure below:

As long as the browser detects that the response header has CORS and that the permitted source includes this site, the request response will not be intercepted.

CORS divides requests into two types: simple requests and requests that need to trigger precheck. The two are relative. What counts as “not simple”? It’s not a simple request if it’s one of the following:

(1) Use request methods other than GET/POST/HEAD, such as PUT/DELETE

Content-type /Accept = content-type /Accept = content-type /Accept

In this case, a precheck request is required. The precheck request uses OPTIONS to check whether the current request is safe, as shown in the figure below:

The server returns two requests, the first of which is OPTIONS:

The return header contains the allowed request header, request mode, source, and precheck request validity period. The figure above is set to 20 days, so you do not need to send an options request during this validity period. In fact, browsers have a maximum duration, such as Chrome’s 5 minutes. If the precheck request detects that the current request does not meet the server’s requirements, it will not issue a direct throw exception, so there is no need to send a “complex” request.

If the source is not in the allowed source range, an exception will be thrown and the return result will not be obtained:

To support CORS, nGINx can be configured as follows:

location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' The '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     add_header 'Access-Control-Allow-Origin' The '*';
     add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
     add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Ra nge';
     add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Ra nge';
}Copy the code


The second common way to cross domains is JSONP. JSONP uses the script tag to cross domains, as shown in this code:

function updateList (data) {
    console.log(data); } $body. Append (' < script SRC = "HTTP://otherdomain.com/request?callback=updateList"></script>');Copy the code

The code defines a global function, then adds the function name to the SRC of the script tag via the callback argument. The SRC of the script is the request that needs to cross domains, and then the request returns executable JS text:

// The script response returns the js content of
updateList([{
    name: 'hello'
}]);Copy the code

Since it is a JS and the upldateList function has been defined, it can be executed normally, and cross-domain data is obtained by passing parameters. This is how JSONP works.


Therefore, since the request with script/iframe/img tags is accompanied by a cookie by default, the request with these tags can bypass the same origin policy. Therefore, you can use these tags to do cross-site request forgery (CSRF), as shown in the following code:

// Transfer request<iframe src="http://Abank.com/app/transferFunds?amount=1500&destinationAccount=..."></iframe>// Configure the router to add a proxy<img src="Http://192.168.1.1/admin/config/outsideInterface? Nexthop = 123.45.67.89" style="display:none">Copy the code

If the site supports GET requests, or does not take further precautions, then a “toxic” site that users have already logged in to on a different page may be vulnerable.

If you want to use cookies, you can set the Ajax property withCredentials, as shown in the following code:

// Native request
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("GET"."http://otherdomain.com/list");
xhr.send();

/ / jquery request
$.ajax({
   url: "http://otherdomain.com/list".xhrFields: {
      withCredentials: true}});Copy the code

Just like the IMG /script tag, it carries cookies and supports other methods besides GET. Therefore, this method can also achieve CSRF, as shown in the figure below:

Therefore, if the transfer request simply does not support GET and no other protection measures are taken, there is still a risk of CSRF attack. So what do we do?

Method one is to display the token ticket in the parameter of each request. Although the cross-domain request can carry a cookie, the cookie from other sources can not be obtained through document.cookie, so the attacker can not get the token in the cookie in the code. So there’s no way out. The disadvantage of method 1 is that it exposes the token, so it is best not to use GET, because GET will spell parameters in the URL, and users may inadvertently send the link to others, but not knowing that the link contains their login information.

The second method is to request a random string before each transfer request. This string can only be used once for transfer or payment request, and it will be discarded when used up. The request can be successful only when this string matches, and the attacker cannot get this string, because if the cross-domain request has cookie, The browser requires that access-control-allow-origin cannot be a wildcard character and can only be a specified source. For example:

Access-Control-Allow-Origin: http://renren.com
Because the attacker’s domain name is not in the source, it is not able to get the request result, so it cannot request the random string. Therefore, this method can also avoid CSRF attacks.
If allow-origin is set to * and withCredentials is set to true in Ajax, the browser will throw an exception and cannot get the result:
In addition, the service needs to specify the allow-Credentials header, as shown in the following code:
add_header "Access-Control-Allow-Origin" "http://fedren.com";
add_header "Access-Control-Allow-Credentials" "true";Copy the code

There are two other things to note about cookies, as shown below:



After talking about client to server, let’s talk about client to client
How do I communicate with a frame, including iframe or pages opened using window.open.

If the parent page is opened by the parent window object, the parent window object is opened by the open window object. If the parent page is opened by the open window object, the parent window object is opened by the window.opener. If the parent window is the same as the iframe, the iframe window object can be obtained through iframe.contentWindow. If the parent window is different from the iframe source, there is a cross-domain problem.

The basic principle of using postMessage is as follows:

// main frame
let iframeWin = document.querySelector("#my-iframe")
                .contentWindow;
iframeWin.postMessage({age: 18}, "http://parent.com");
iframeWin.onmessage = function(event) {
    console.log("recv from iframe ", event.data);
};

// iframe
window.onmessage = function(event) {
    // test event.origin
    if(event.origin ! == expectOrigin) {return;
    }
    console.log("recv from main frame ", event.data);
};

window.parent.postMessage("hello, this is from iframe "."http://child.com");
Copy the code

Take YouTube video embedded on the page as an example. You can embed a YouTube video on the page through the following code. What you embed is a cross-domain iframe, so it involves how to communicate with iframe. For example, how to know the status of the iframe, trigger the event onPlayerReady defined by the parent page, this is the iframe notifies the parent page, and the parent page can adjust player.stopvideo to control the behavior of the iframe, this is the parent page notifies the iframe.

Iframe notify the parent page is through the window. The parent. PostMessage, at the same time to monitor the message events:

In line 4304 of the code above, c is window.parent. The embed-player.js is the iframe js, and the iframe JS sends a message via postMessage, as shown in the window on the right. Then widgetapi.js in the parent window receives the message.

Similarly, the parent window JS uses postMessage to send a message to the iframe, as shown in the figure below:

You may need to define additional global variables or functions for other frames to use, or define a set of event mechanisms (native events /jQuery/Vue events, etc.).

If a subdomain such as mail.hello.com wants to cross hello.com, you can explicitly set the subdomain’s document.domain value to the parent domain’s domain:

document.domain = "hello.com";Copy the code
There will be no cross-domain problems.


In addition, if you need to communicate with different tabs of the same origin, you can use localStorage. That is, if one page sets localStorage, the other pages will trigger the storage event:
window.addEventListener('storage'.function(e) {
    e.key;
    e.oldValue;
    e.newValue;
    e.url;
    e.storageArea;
});
Copy the code

I haven’t tried this before, so readers can try it.

In addition, WebSocket is not subject to the same origin policy, there are no cross-domain issues. CSS font files are cross-domain, specifying CORS can load other source font files (usually placed on the CDN). The external image dynamically loaded by canvas also needs to specify the CORS header for image processing, otherwise it can only be drawn but not read.


Finally, cross-domain is divided into two types. One is cross-domain request, and the other is to access cross-domain pages. Cross-domain requests can be accessed through CORS/JSONP and other methods, and cross-domain pages are mainly accessed through postMesssage. Because cross-domain requests can be made as well as carried with them cookies, you need to avoid the risk of cross-site request forgery attacks, especially when money is involved.