Why cross-domain problems

Cross-domain problems occur because of the same origin policy of browsers.

1. What is the same-origin policy

The same origin policy restricts how documents or scripts loaded from the same source can interact with resources from another source. This is an important security mechanism for isolating potentially malicious files.

2. Why is the same-origin policy required

For security reasons, browsers restrict cross-source HTTP requests from within scripts. For example, XMLHttpRequest follows the same origin policy. This means that Web applications using these apis can only request HTTP resources from the same domain that loaded the application, unless CORS headers or other cross-domain methods are used.

There are two security risks without the same Origin policy:

  1. Common scenarios for interface requests are as follows: Cookie access, CSRF attack: cross-site request forgery

One Day, when you are interested in a treasure to double 11 buy buy buy, network shopping cart inside all sorts of add, pop up by this time you love bean news, that must pay close attention to, take a look at your love bean is also to introduce the other half, and then click on the link in to see, you see, Maybe this site is doing something indescribable behind the scenes!

For example, because there is no same-origin policy, it makes a request to a treasure! Because when you log in to a treasure, “the server will join the set-cookie field in the response header after verification, and then the next time you send a request, the browser will automatically attach the Cookie in the HTTP request header field Cookie”, so that the illegal website is equivalent to logging in to your account, you can do whatever you want!

  1. Assuming a scenario for Dom, users need to fill in the user name, password and other login information for authentication before the following operation. When you enter the user name and password to log in successfully, your account password will be stolen. Then what does this phishing website do?
    // HTML
    <iframe name="qq" src="www.qq.com"></iframe> // JS // Since there is no same-origin policy restriction, phishing sites can directly access the Dom of other siteslet iframe = window.frames['qq']
    let userInput = iframe.document.getElementById('Account Entry box'),
        passInput = iframe.document.getElementById('Password entry field'); // The dom has already got the account and passwordCopy the code

Therefore, the same origin policy can avoid some risks. It does not mean that the same origin policy is safe, but the same origin policy is a basic security mechanism of the browser. After all, it can raise the cost of attack a little. In fact, there is no penetrable shield, but the cost of the attack is not proportional to the benefits obtained after the successful attack.

3. How to check whether the URL is the same origin

If both pages have the same protocol, port (if specified), and domain name, then both pages have the same source.

Homologous judgement: http://www.example.com/dir/page.html, for example, whether the following form is pointed out that different forms of links with its homologous: (the reason is not stated in the different attributes to explain the same as the example of the original links corresponding attributes)

link The results of why
http://www.example.com/dir/page2.html is Same protocol same domain name same port
http://www.example.com/dir2/other.html is Same protocol same domain name same port
http://www.example.com:81/dir/other.html no Different ports
https: / /www.example.com/dir/other.html no Different protocols and ports
http://en.example.com/dir/other.html no Domain name is different
http://example.com/dir/other.html no Different domain names (exact match required)
http://v2.www.example.com/dir/other.html no Different domain names (exact match required)
http://www.example.com:80/dir/other.html Not sure It depends on how the browser implements it

Tips: The difference between a master domain and a subdomain

Primary domain name: consists of two or more letters separated by periods (.). The entire domain name has only one period (.). Unique subdomain name: a domain name under the primary domain name has multiple periods (.)

For example, https://www.baidu.com/ protocol: https:// Server name: WWW main domain name: baidu.com subdomain name (subdomain name includes server name WWW + main domain name baidu.com) : www.baidu.com

The correct way to open an interface across domains

Currently, there are three commonly used ways to solve cross-domain problems:

  • Jsonp: Only GET requests can be sent
  • iframe + form
  • CORS: Cross-origin resource sharing;

1. JSONP

In HTML tags, some tags, such as script and img, have no cross-domain restrictions. To take advantage of this, we can do something like this:


      
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script type='text/javascript'>
      // The back end returns the directly executed method, which is equivalent to executing the method. Since the back end puts the returned data in the parameter of the method, it can get the res here.
      window.jsonpCb = function (res) {
        console.log(res)
      }
    </script>
    <script src='http://localhost:9871/api/jsonp? msg=helloJsonp&cb=jsonpCb' type='text/javascript'></script>
  </body>
</html>

Copy the code

JSNOP can only send GET requests. JSNOP can only send POST requests. JSNOP can only send GET requests

2. Iframe + Form

In the code

const requestPost = ({url, data}) = > {
  // Create an iframe to send data to.
  const iframe = document.createElement('iframe')
  iframe.name = 'iframePost'
  iframe.style.display = 'none'
  document.body.appendChild(iframe)
  const form = document.createElement('form')
  const node = document.createElement('input')
  // Register the iframe load event handler if you need to do something when the response returns.
  iframe.addEventListener('load'.function () {
    console.log('post success')
  })

  form.action = url
  // Execute the form action URL in the specified iframe
  form.target = iframe.name
  form.method = 'post'
  for (let name in data) {
    node.name = name
    node.value = data[name].toString()
    form.appendChild(node.cloneNode())
  }
  // Form elements need to be added to the main document.
  form.style.display = 'none'
  document.body.appendChild(form)
  form.submit()

  // After the form is submitted, the form can be deleted without affecting the next data delivery.
  document.body.removeChild(form)
}
// The usage mode
requestPost({
  url: 'http://localhost:9871/api/iframePost'.data: {
    msg: 'helloIframePost'}})Copy the code

3. CORS

Cross-origin Resource Sharing (CORS) allows Web application servers to control cross-domain access and secure cross-domain data transmission. Browsers support the use of CORS in API containers, such as XMLHttpRequest or Fetch, to reduce the risk associated with cross-domain HTTP requests. CORS needs to be supported by both the client and the server. Currently, all browsers support this mechanism (micro enterprises adopt this approach).

IE 10+ provides full support for the specification, but in earlier versions (8 and 9) the CORS mechanism was accomplished through the XDomainRequest object.

The CORS specification requires that the browser first initiate a preflight request using the OPTIONS method for HTTP request methods that may have adverse effects on server data (especially HTTP requests other than GET, or POST requests with certain MIME types). To know whether the server allows the cross-domain request. The actual HTTP request is made only after the server confirms that it is allowed. In the return of the precheck request, the server side can also inform the client whether it needs to carry identity credentials (including Cookies and HTTP authentication related data).

The entire CORS communication process is completed automatically by the browser without user participation. For developers, CORS communication is no different from same-origin AJAX communication, and the code is exactly the same. As soon as the browser discovers that an AJAX request crosses the source, it automatically adds some additional headers, and sometimes an additional request, but the user doesn’t feel it.

Therefore, the key to CORS communication is the server. As long as the server implements the CORS interface, cross-source communication is possible.

The CORS standard allows cross-domain HTTP requests in the following scenarios:

  • A cross-domain HTTP request initiated by XMLHttpRequest or Fetch.
  • Web fonts (cross-domain font resources are used in CSS via @font-face), so websites can publish TrueTypes
  • Font resources, and only authorized sites are allowed to make cross-site calls.
  • WebGL map
  • Draw Images/video Images to canvas using drawImage
  • Style sheets (using CSSOM)
  • Scripts (unhandled exception)

Browsers classify CORS requests into two categories: Simple request and not-so-simple Request.

  • Use one of the following methods: * GET * HEAD * POST
  • The Fetch specification defines a set of header fields that are CORS safe. No header fields other than this set may be set artificially. The set is:
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-type (Additional restrictions need to be noted)
  • The value of the content-type is limited to one of the following:
    • text/plain
    • multipart/form-data
    • Application/X-www-form-urlencoded is a simple request as long as both of the following two criteria are met.

Any request that does not meet both conditions is a non-simple request. Browsers treat these two requests differently.

1. Simple requests

For simple requests, the browser issues CORS requests directly. Specifically, add an Origin field to the header information. As an example, the browser automatically adds an Origin field to the header when it realizes that the cross-source AJAX request is a simple one.

GET /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com Accept-language: en-us Connection: Keep alive - the user-agent: Mozilla / 5.0...Copy the code

In the header above, the Origin field specifies the source (protocol + domain + port) from which the request came. Based on this value, the server decides whether to approve the request or not.

If Origin does not specify a licensed source, the server will return a normal HTTP response. The browser realizes that the response header does not contain the Access-Control-Allow-Origin field (more on that below), and it throws an error that is caught by XMLHttpRequest’s onError callback. Note that this error cannot be identified by the status code, because the status code for the HTTP response might be 200.

If Origin specifies a domain name within the license, the server returns a response with several additional header fields.

Access-control-allow-origin: http://api.bob.com // Required access-control-allow-credentials:true// Access-Control-expose-headers: FooBar // Content-type: text/ HTML; charset=utf-8Copy the code

In the header above, there are three fields related to CORS requests, all beginning with Access-Control-.

(1) Access – Control – Allow – Origin

This field is required. Its value is either the value of the Origin field at the time of the request, or an *, indicating acceptance of requests for any domain name.

(2) Access – Control – Allow – Credentials

This field is optional. Its value is a Boolean value indicating whether cookies are allowed to be sent. By default, cookies are not included in CORS requests. If set to true, the server explicitly approves that cookies can be included in the request and sent to the server. This value can only be set to true if the server does not want the browser to send cookies.

(3) the Access – Control – Expose – Headers

This field is optional. In CORS requests, the getResponseHeader() method of the XMLHttpRequest object takes only six basic fields: Cache-control, Content-language, Content-Type, Expires, Last-Modified, Pragma. If you want to get other fields, you must specify access-Control-expose-headers. The above example specifies that getResponseHeader(‘FooBar’) can return the value of the FooBar field.

As mentioned above, CORS requests do not send cookies and HTTP authentication information by default. To send cookies to the server, specify the access-Control-allow-credentials field with the server’s permission.

Access-Control-Allow-Credentials: true
Copy the code

On the other hand, the developer must turn on the withCredentials attribute in the AJAX request.

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
Copy the code

Otherwise, the browser won’t send a Cookie, even if the server agrees to do so. Or, if the server asks for a Cookie, the browser won’t handle it.

However, if the withCredentials setting is omitted, some browsers still send cookies together. In this case, you can explicitly disable the withCredentials.

xhr.withCredentials = false;
Copy the code

Note that access-Control-allow-Origin cannot be set to an asterisk if cookies are to be sent, and must specify an explicit domain name consistent with the requested web page. At the same time, cookies still follow the same origin policy, only the Cookie set with the server domain name will be uploaded, cookies of other domain names will not be uploaded, and (cross-source) document. Cookie in the original web page code can not read cookies under the server domain name.

2. Non-simple request (precheck request)

2.1 Precheck request

Non-simple requests are requests that have special requirements on the server, such as the request method being PUT or DELETE, or the content-Type field being of Type Application/JSON.

CORS requests that are not simple requests are preceded by an HTTP query request, called a “preflight” request.

The browser asks the server if the domain name of the current web page is on the server’s license list, and what HTTP verb and header fields can be used. The browser issues a formal XMLHttpRequest request only if it receives a positive response; otherwise, an error is reported.

The following is a JavaScript script from the browser.

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header'.'value');
xhr.send();
Copy the code

In the code above, the HTTP request is PUT and sends a Custom Header x-custom-header.

The browser realizes that this is not a simple request and automatically issues a “pre-check” request, asking the server to confirm that it is ok to do so. Here is the HTTP header for this “precheck” request.

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Copy the code

The request method for the “precheck” request is OPTIONS, indicating that the request is being queried. In the header information, the key field is Origin, indicating which source the request came from.

In addition to the Origin field, the precheck request header contains two special fields.

(1) Access – Control – Request – Method

This field is required to list which HTTP methods are used by the browser for CORS requests, in this example PUT.

(2) Access – Control – Request – Headers

This field is a comma-separated string that specifies the additional Header field to be sent by a browser CORS request, x-custom-header in the example above.

2.2 Response to precheck request

After receiving the precheck Request, the server checks the Origin, access-Control-request-method, and access-Control-request-headers fields and confirms that cross-source requests are allowed, it can respond.

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
Copy the code

In the HTTP response above, the key is the Access-Control-Allow-Origin field, which indicates that http://api.bob.com can request data. This field can also be set to an asterisk to indicate approval of any cross-source request.

Access-Control-Allow-Origin: *
Copy the code

If the browser denies the “precheck” request, it will return a normal HTTP response, but without any CORS related header fields. At this point, the browser decides that the server did not approve the precheck request, and therefore fires an error that is caught by the ONError callback function of the XMLHttpRequest object. The console will print the following error message.

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
Copy the code

Other CORS related fields that the server responds to are as follows.

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
Copy the code

(1) Access – Control – Allow – the Methods

This field is required, and its value is a comma-separated string indicating all methods supported by the server for cross-domain requests. Notice that all supported methods are returned, not just the one requested by the browser. This is to avoid multiple “pre-check” requests.

(2) Access – Control – Allow – Headers

The access-Control-allow-HEADERS field is required if the browser Request includes the access-Control-request-HEADERS field. It is also a comma-separated string indicating all header information fields supported by the server, not limited to those requested by the browser in precheck.

(3) the Access – Control – Allow – Credentials

This field has the same meaning as a simple request.

(4) the Access – Control – Max – Age

This field is optional and specifies the validity period of the precheck request, in seconds. In the result above, the validity period is 20 days (1728000 seconds), which allows the response to be cached for 1728000 seconds (20 days), during which time another precheck request is not issued.

2.3 Normal Browser Requests and Responses Once the server passes the “precheck” request, every normal browser CORS request will have the same Origin header field as a simple request. The server also responds with an Access-Control-Allow-Origin header field.

The following is a normal CORS request for the browser after the “precheck” request.

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Copy the code

The Origin field in the header above is automatically added by the browser.

The following is a normal response from the server.

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
Copy the code

In the header above, the access-Control-Allow-Origin field is mandatory for each response.

HTTP response header field

1. Access-Control-Allow-Origin

The response header can contain an access-Control-allow-Origin field with the following syntax: access-Control-allow-Origin

Access-Control-Allow-Origin: | *

Where, the value of the Origin parameter specifies the outdomain URI that is allowed to access the resource. For requests that do not require credentials, the server can specify the value of this field as a wildcard, indicating that requests from all domains are allowed.

For example, the following field values will allow requests from Mozilla.com:

Access-Control-Allow-Origin: mozilla.com

If the server specifies a specific domain name instead of an “*”, the value of the Vary field in the response header must contain Origin. This tells the client that the server returns different content for different source sites.

JSONP supports only GET requests, and CORS supports all types of HTTP requests. JSONP has the advantage of supporting older browsers and being able to request data from sites that do not support CORS.

DOM levels cross domains

document.domain

A page may change its source due to certain restrictions. A script can set the value of document.domain to its current domain or to the superdomain of its current domain. If it is set to the superdomain of its current domain, the shorter domain is used for subsequent source checks. Assuming that http://store.company.com/dir/other.html document a script execution of the following statements:

document.domain = "company.com";
Copy the code

After this statement is executed, Page will be successfully through the homologous detection of http://company.com/dir/page.html (assuming that http://company.com/dir/page.html will be the document. The domain is set to company.com, To indicate that it wants to allow this – see document.domain for more information). However, Company.com cannot set document.domain to otherCompany.com because it is not company.com’s superdomain

Canvas getImageData,toDataURL cross domain

By adding the cross-origin attribute, the problem of getImageData and toDataURL cross-domain can be solved. For details, see Canvas cross-domain