After reading this section, you will learn:

  1. The causes and functions of the same origin policy;
  2. Multiple solutions across domains;

preface

We can imagine a scenario like this:

The school opened a window for parents to ask about their children’s grades. Every student in the class is an independent individual, and every student and parent come from the same family. When parents in the same family ask about their children’s grades, the window will give corresponding data. If parents from different families ask about the scores of other students, the safety of their children s scores cannot be guaranteed. Wouldn’t that be a mess?

If there is no same-origin policy

Let’s first look at the relationship between Windows, families, parents, and students in the Web world in the above example:

This window is the browser window, the parents and students inside is an independent website, and members from the same family (parents, students) is the same domain.

If there were no security policies in the Web world, then our sites could load and execute anyone else’s arbitrary files, and there would be a lot of uncontrollable problems.

Like opening a bank site and then accidentally opening a malicious site, without security measures, the malicious site can do a lot of things:

  • Modify DOM, CSSOM and other information of the bank site;
  • Inserting malicious JavaScript scripts into bank sites;
  • Hijack user login username and password;
  • Read users’ cookies, IndexDB and other data;
  • .

The same-origin policy

So how to ensure that only parents in the same family can check students’ grades? In the Web world, how do you ensure that a Web page can only be queried and modified by code in the same domain?

And that brings us to today’s hero: the same-origin policy.

The same origin policy is a convention that is the core and most basic security feature of the browser… The Web is built on the same origin policy, and browsers are just an implementation of the same origin policy.

From White Hat on Web Security

What is homology

If two urls are the same protocol, port, and host, they are the same.

The following table gives the example comparing with URLhttp://store.company.com/dir/page.html source:

URL The results of why
http://store.company.com/dir2/other.html homologous It’s just a different path
http://store.company.com/dir/inner/another.html homologous It’s just a different path
https://store.company.com/secure.html Different source Agreement is different
http://store.company.com:81/dir/etc.html Different source Different ports (http://default port is 80)
http://news.company.com/dir/other.html Different source The host different

The role of the same origin policy

As we know from the above example, the same origin policy is used to limit resource interaction from another domain, thus ensuring the privacy and data security of our website.

The same origin policy

Specifically, the same origin policy is mainly manifested in DOM, Web data, and network data:

The DOM level. The same-origin policy restricts JavaScript scripts from different sources from reading and writing the DOM object of the current page, preventing cross-domain scripts from tampering with DOM structures.

Web data layer. The same-origin policy ensures data security by restricting sites from different sources to read data such as cookies, LocalStorage, and IndexDB of the current site.

Network layer. The same origin policy restricts the sending of a site’s data to a site from a different source through, for example, XMLHttpRequest.

Tradeoffs between security and availability

Browsers make a trade-off between security and usability. As we all know, for small projects, we can put all our resources on our own server. But for medium and large projects, due to the expensive server, we project the static resource files such as images, video, etc., need to be managed in a third party to reduce operating costs, so the browser on the basis of follow security, eased restrictions, allowing img, script, style tags for cross-domain reference resources.

Cross-domain solutions

jsonp

Request JSON data from the server by adding a

Native implementation:

let scriptElement = document.createElement('script')
scriptElement.type = 'text/javascript'

// Pass the name of a callback function to the back end so that the back end can execute the callback function when it returns
scriptElement.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'
document.head.appendChild(scriptElement)

// Execute the callback function
function handleCallback(res) {
    console.warn(JSON.stringify(res));
}
Copy the code

The server returns the following (executes the global function on return) :

handleCallback({status: true.user: 'admin'})
Copy the code

The Jquery ajax:

$.ajax({
    url: 'http://www.domain2.com:8080/login'.type: 'get'.dataType: 'jsonp'.// The request is jSONP
    jsonpCallback: 'handleCallback'.// Customize the callback function
    data: {},})Copy the code

Examples of back-end Node.js code:

var querystring = require('querystring')
var http = require('http')
var server = http.createServer()

server.on('request'.function(req, res) {
    var params = qs.parse(req.url.splite('? ') [1])
    var fn = params.callback;

    // jsonp returns Settings
    res.writeHead(200, { 'Content-type': 'text/javascript' })
    res.write(fn + '(' + JSON.stringify(params) + ') ')
    res.end()
})

server.listen('8080')
console.log('Server is running at port 8080... ')
Copy the code

Disadvantages of JSONP: Only get requests are implemented.

location.hash + iframe

The location.hash mode is cross-domain, where the child frame has the hash value to change the parent frame SRC, and the page is not refreshed by changing the hash value, but the number of bytes passed is limited.

Page A MTL code:

<iframe src="b.html" id="myIframe" onload="test()"></iframe>
<script>
    // Iframe executes this function after loading the HTML page
    function test() {
        // Get the hash value set using the HTML page
        var data = window.location.hash;
        console.log(data);
    }
</script>
Copy the code

Page B.HTML code:

<script type="text/javascript">
    // Sets the hash value for the parent page
    parent.location.hash = "Hello World!"
</script>
Copy the code

Disadvantages of Location.hash: It can’t handle complex functional scenarios.

window.name + iframe

The window object has a name attribute that has the following characteristics: That is, during the life cycle of a window, each page shares a window.name, and each page has the read and write permission to window.name. Window. anme persists in all pages that a window passes, and will not be reset because of the new page.

Page A.HTML code:

<iframe src="http://laixiangran.cn/b.html" id="myIframe" onload="test()" style="display: none;">
<script>
    / / 2. The iframe load "after http://laixiangran.cn/b.html pages will perform this function
    function test() {
        var iframe = document.getElementById('myIframe');
        
        // reset iframe's onload event,
        // After resetting SRC,
        / / http://www.laixiangran.cn/a.html page with the iframe in the same source, you can visit each other
        iframe.onload = function() {
            var data = iframe.contentWindow.name; // 4. Get window.name in iframe
            console.log(data); // hello world!
        };
        
        / / 3. Reset a homologous with http://www.laixiangran.cn/a.html page page
        iframe.src = 'http://www.laixiangran.cn/c.html';
    }
</script>
Copy the code

Page B.HTML code:

<script type="text/javascript">
    // Set the contents of the current window.name
    window.name = "Hello World!";
</script>
Copy the code

The disadvantage of window.name + iframe is that it cannot handle complex functional scenarios.

postMessage

The window.postMessage(message, targetOrigin) method is a new feature introduced in HTML5 that can be used to send messages to other Window objects, regardless of whether the window object belongs to the same origin or a different source.

The window object from which the postMessage method is called is the window object from which the message is received. The first argument to this method, Message, is the message to be sent, and can only be a string. The second parameter, targetOrigin, is used to restrict the field of the window object that receives the message. If you do not want to restrict the field, you can use the wildcard *.

The Window object that needs to receive the message gets the passed message by listening for its own Message event, the content of which is stored in the event object’s Data property.

Page A.HTML code:

<iframe src="http://laixiangran.cn/b.html" id="myIframe" onload="test()" style="display: none;">
<script>
    / / 1. The iframe load "after http://laixiangran.cn/b.html pages will perform this function
    function test() {
        / / 2. http://laixiangran.cn/b.html pages for the window object,
        / / and then through the postMessage sends a message to the http://laixiangran.cn/b.html page
        var iframe = document.getElementById('myIframe');
        var win = iframe.contentWindow;
        win.postMessage('I'm from http://www.laixiangran.cn/a.html page news'.The '*');
    }
</script>
Copy the code

Page B.HTML code:

<script type="text/javascript">
    // Register the message event to receive messages
    window.onmessage = function(e) {
        e = e || event; // Get the event object
        console.log(e.data); // Get the message sent via the data attribute
    }
</script>
Copy the code

Cross-domain Resource Sharing (CORS)

CORS (Cross-Origin Resource Sharing) is a W3C standard that defines how browsers and servers communicate when they must access cross-domain resources. The basic idea behind CORS is to use custom HTTP headers to let the browser communicate with the server to determine whether a request or response should succeed or fail.

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 is cross-sourced, it automatically adds some additional header information, and sometimes an additional request appears, but the user is unaware of it.

Therefore, the key to CORS communication is the server, as long as the server implements the CORS interface, it can communicate across the source.

Browsers classify CORS requests into two categories: simple and non-simple;

– The request method is one of the following: HEAD, GET, or post-HTTP – The header information does not exceed the following fields: Accept, accept-language, Content-language, last-event-id, and Content-Type

Any request that does not meet both conditions is a non-simple request.

Browsers handle these two requests differently:

A simple request

You need to add an additional Origin header to the request that contains the source information (protocol, domain name, and port) of the requested page so that the server can use this header to decide whether to respond or not. For example: Origin: http://www.laixiangran.cn.

If the server considers the request acceptable, it posts back the same source information in the Access-Control-Allow-Origin header (* if it is a public source). For example: Access – Control – Allow – Origin: http://www.laixiangran.cn.

If there is no header or if there is a header but the source information does not match, the browser will reject the request. Under normal requests, the browser will process the request. Note that neither the request nor the response contains cookie information.

To include cookie information, ajax requests need to set the withCredentials attribute of XHR to true, and the server needs to set the access-Control-allow-credentials header to true.

Non-simple request

The browser sends a Preflight request to the server before sending the actual request. This request uses the OPTIONS method to send the following headers:

  • Origin: same as a simple request;
  • Access-Control-Request-Method: a method used to request itself;
  • Access-Control-Request-Headers: (Optional) User-defined header information. Multiple headers are separated by commas (,).
Origin: http://www.laixiangran.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ
Copy the code

After sending this request, the server can decide whether to allow this type of request. The server communicates with the browser by sending the following header in the response:

  • Access-Control-Allow-Origin: The same as a simple request.
  • Access-Control-Allow-Methods: Allowed methods. Multiple methods are separated by commas.
  • Access-Control-Allow-Headers: Allowed headers, separated by commas.
  • Access-Control-Max-Age: How long (in seconds) should this Preflight request be cached.
Access-Control-Allow-Origin: http://www.laixiangran.cn
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000
Copy the code

Once the server allows the request through a Preflight request, every subsequent normal BROWSER CORS request is treated as a simple request.

advantages
  • CORS communication is no different from homologous AJAX communication, the code is exactly the same, easy to maintain;
  • Support for all types of HTTP requests;
disadvantages
  • Compatibility issues, especially for browsers below IE10;
  • The first time a non-simple request is sent, an additional request is made.

The WebSocket protocol

WebSocket Protocol is a new HTML5 protocol, which implements full-duplex communication between browser and server and allows cross-domain communication. It is an implementation of server push technology.

Front-end code:

<div>User input:<input type="text"></div>
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');

// The connection was successfully processed
socket.on('connect'.function() {
    // Listen for server messages
    socket.on('message'.function(msg) {
        console.log('data from server: ---> ' + msg); 
    });

    // The listener server is closed
    socket.on('disconnect'.function() { 
        console.log('Server socket has closed.'); 
    });
});

document.getElementsByTagName('input') [0].onblur = function() {
    socket.send(this.value);
};
</script>
Copy the code

Node.js socket background:

var http = require('http')
var socket = require('socket.io')

// Start the HTTP service
var server = http.createServer(function(req, res) {
    res.writeHead(200, { 'Content-type': 'text/html' })
    res.end()
})

server.listen(8080)

// Listen for socket connections
socket.listen(server).on('connection'.function(client) {
    // Receive information
    client.on('message'.function(msg) {
        client.send('Hello:' + msg)
        console.log('data from client: ---> ' + msg)
    })

    // Disconnect processing
    client.on('disconnect'.function() {
        console.log('Client socket has closed.')})})Copy the code

The resources

1. Security attack and defense skills 30 lectures

Time.geekbang.org/column/intr…

2. Web protocol details and packet capture

Time.geekbang.org/course/intr…

3. Detailed explanation of various cross-domain schemes

www.bilibili.com/video/BV1SE…

4. Configure the same-origin policy for the browser

Developer.mozilla.org/zh-CN/docs/…