After reading this section, you will learn:
- The causes and functions of the same origin policy;
- 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/…