- What is the same origin policy?
- Common cross-domain scenarios
- The same origin policy restricts the following behaviors
- Cross-domain solutions
- through
jsonp
Cross domain CORS
document.domain
+iframe
Cross domainlocation.hash
+iframe
window.name
+iframe
Cross domainpostMessage
Cross domainnginx
Agent cross-domainnodejs
Middleware proxies cross domainsWebSocket
Agreement cross-domain
- through
What is the same origin policy?
The cornerstone of browser security is the same-Origin Policy.
Same-origin means that the protocol, domain name, and port are the same. Even if two different domain names point to the same IP address, they are not same-origin
It is the core and most basic security function of the browser. Without the same origin policy, the browser is vulnerable to XSS, CSFR and other attacks
XSS: XSS attack usually refers to the use of the vulnerability left during the development of the web page, through a clever way to inject malicious command code into the web page, the user load and execute the attacker malicious web program cross-site request attack: To put it simply, the attacker uses some technical means to trick the user’s browser into visiting a previously authenticated website and performing some operations (such as sending emails, sending messages, or even property operations such as transferring money or buying goods). Because the browser has been authenticated, the site being visited will act as if it is a genuine user action. This exploits a flaw in user authentication on the Web: simple authentication can only guarantee that a request is sent from a user’s browser, but not that the request itself is voluntarily made by the user
Common cross-domain scenarios
URL | Outcome | Reason |
http://store.company.com/dir/inner/another.html | Same origin | Only the path differs |
http://store.company.com/dir/inner/another.html | Same origin | Only the path differs |
https://store.company.com/page.html | Failure | Different protocol |
http://store.company.com:81/dir/page.html | Failure | Different port (http:// is port 80 by default) |
http://news.company.com/dir/page.html | Failure | Different host |
The same origin policy restricts the following behaviors
Cookie
,LocalStorage
和IndexDB
Unable to readDOM
Unable to getAJAX
Request cannot be sent
Cross-domain solutions
throughjsonp
Cross domain
A scheme that makes use of script tags without cross-domain constraints
Usually, in order to reduce the load of the Web server, we separate static resources such as JS, CSS and IMG to another server with an independent domain name, and then load static resources from different domain names in the HTML page through corresponding tags, which are allowed by the browser. Based on this principle, we can dynamically create script. Request a reference url to achieve cross-domain communication. Jsonp takes advantage of this feature
The advantages and disadvantages:
JSONP
Is a common way for a server to communicate with a client across sources. The biggest feature is simple to apply, the old browser all support, server transformation is very small- Can only achieve
get
A request that is insecure and vulnerableXSS
attack
Example:
function jsonp({ url, params, cb }) {
return new Promise((resolve, reject) = > {
let script = document.createElement("script");
window[cb] = function(data) { resolve(data); }; params = { ... params, cb };let arrs = [];
for (let key in params) {
arrs.push(`${key}=${params[key]}`);
}
script.src = `${url}?${arrs.join("&")}`;
document.body.appendChild(script);
});
}
jsonp({
url: "http://localhost:3000/say".params: { wd: "haoxl" },
cb: "show"
}).then(data= > {
console.log(data);
});
Copy the code
Create an Express server:
app.get("/say".function(req, res) {
let { wd, cb } = req.query;
res.end(`${cb}('hello')`);
});
app.listen(3000.function() {
console.log("Example app listening on port 3000!");
});
Copy the code
CORS
CORS, or Cross-Origin Resource Sharing, is a W3C standard that allows browsers to issue XMLHttpRequest requests to cross-source servers, overcoming the limitation that AJAX can only be used in the same source
Tips: Common cross-domain request: only the server set access-Control-allow-origin, front-end does not need to set, if the request with cookie: both front and back ends need to set. Because of the same-origin policy restriction, the cookie read is the cookie of the domain where the cross-domain request interface resides, not the current page
The advantages and disadvantages:
- Currently, all browsers support this feature (IE8+ : IE8/9 is required
XDomainRequest
Object to supportCORS
)),CORS
It has also become a mainstream cross-domain solution - The whole
CORS
The communication process is automatically completed by the browser without user participation. For developers,CORS
The communication is no different than homologous AJAX communication, and the code is exactly the same. Once the browser finds outAJAX
When requests cross sources, additional headers are automatically added, and sometimes an additional request is made, but the user does not feel it CORS
withJSONP
The use of the same purpose, but more thanJSONP
More powerful.JSONP
Only supportGET
CORS supports all types of requestsHTTP
The request.JSONP
The advantage of CORS is that it supports older browsers and can request data from sites that do not support CORS
Two types of requests: Browsers classify CORS requests into two categories:
- Simple Request
- Non-simple Request (not-so-simple Request)
As long as the following two conditions are met, it is a simple request. Any request that does not meet both of the following conditions is a non-simple request
- The request method is one of three methods: GET, HEAD, and POST
- The HTTP header does not exceed the following fields: Accept, accept-language, Content-language, and Content-Type. text/plain, multipart/form-data, application/x-www-form-urlencoded), DPR, Downlink, Save-Data, Viewport-Width, Width
Simple request: The browser makes a CORS request directly. Specifically, add an Origin field to the header information.
Non-simple requests: requests that have special requirements on the server, such as the request method PUT or DELETE, or the content-Type field Type application/json. CORS requests that are not simple requests add an HTTP query request, called a preflight request, before formal communication. 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
Request Headers:
- Access-Control-Request-Method
- Access-Control-Request-Headers
Access-control-request-method This field is required to list the HTTP methods used by the browser for CORS requests
Access-control-request-headers This field is a comma-separated string that specifies the additional header field that the browser will send for CORS requests
Response Headers:
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
- Access-Control-Allow-Origin
- Access-Control-Allow-Credentials
- Access-Control-Expose-Headers
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
- Access-Control-Max-Age
1. Access-control-allow-origin 2. Access-control-allow-credentials This field is optional. Its value is a Boolean that indicates 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
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
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
3. The access-Control-expose-headers 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
6. Access-control-allow-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
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
8. Access-control-max-age Specifies the validity period of the precheck request in seconds. This field is optional. 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
Examples of code for localhost:8004
let xhr = new XMLHttpRequest();
// Enforces a request header cookie in the front-end setting
document.cookie = "name=haoxl";
xhr.withCredentials = true;
xhr.open("GET"."http://localhost:3000/getData".true);
// Set the custom request header
xhr.setRequestHeader("name"."haoxl");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.warn("xhr.response");
console.log(xhr.response);
// get the request header from the background that has changed the value of name
console.warn("xhr.getResponseHeader");
console.log(xhr.getResponseHeader("name"));
console.log("\n"); }}}; xhr.send();Copy the code
Node server code (port: 3000) Example:
var express = require("express");
var app = express();
let whiteList = ["http://localhost:8004"];
app.use(function(req, res, next) {
let origin = req.headers.origin;
if (whiteList.includes(origin)) {
// Set that source to be accessible to me, but not to be used in conjunction with the cookie credential response header
res.setHeader("Access-Control-Allow-Origin", origin);
// Allow headers with name to be accessed
res.setHeader("Access-Control-Allow-Headers"."name");
// Sets which request methods are accessible
res.setHeader("Access-Control-Allow-Methods"."GET");
// Set access to requests with cookies
res.setHeader("Access-Control-Allow-Credentials".true);
// If the name header is changed in the background, the browser will consider it unsafe
res.setHeader("Access-Control-Expose-Headers"."name");
// Precheck survivability time -options please refer to
res.setHeader("Access-Control-Max-Age".3);
// Set no processing when a prerequest is sent
if (req.method === "OPTIONS") {
res.end(); //OPTIONS Do not do any processing
}
}
next();
});
app.put("/getData".function(req, res) {
res.end("hello world");
});
app.get("/getData".function(req, res) {
res.end("Nice to meet you");
});
app.listen(3000.function() {
console.log("Example app listening on port 3000!");
});
Copy the code
document.domain
+ iframe
Cross domain
This solution applies only to cross-domain scenarios where the primary domain is the same and the subdomains are different (the level-1 domain name of web pages is the same, but the level-2 domain name is different). How it works: The co-domain is realized when both pages are forced to use JavaScript to set document.domain as the base primary domain
Default a.html = www.haoxl.com, b.html = test.haoxl.com
a.html
:
<iframe src="http://test.haoxl.com" onload="load()"></iframe>
<script>
function load() {
// Tell the page its main domain name to be the same as the main domain name of B.html so that the value of B can be accessed in A
document.domain = "haoxl.com";
// After page A introduces page B, directly obtain the value of b in the following way
console.log(frame.contentWindow.b);
}
</script>
Copy the code
b.html
:
<html>
<head>
<title>b.html</title>
<meta charset="utf8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<script>
window.domain = "haoxl.com";
function myFn() {
console.log("onload b.html");
this.b = "bHtml";
}
</script>
</head>
<body onload="myFn()">
<p>b.html</p>
</body>
</html>
Copy the code
location.hash
+ iframe
Implementation principle: A and B communicate with each other across domains through the middle page C (and C and A are co-domains). Three pages, different fields use iframe location.hash to pass values, the same fields directly access JavaScript to communicate
A domain: A.html -> B domain: B.html -> A domain: C.HTML, A and B different domain can only hash value one-way communication, B and C are also different domain can only one-way communication, but C and A are the same domain, so C can access all objects on A page through parent. Parent
a.html
( www.a.com/a.html ):
<iframe
id="iframe"
src="http://www.b.com/b.html"
style="display:none;"
></iframe>
<script>
var iframe = document.getElementById("iframe");
// Pass hash values to B.html
setTimeout(function() {
iframe.src = iframe.src + "#user=admin";
}, 1000);
// callback methods open to homologous C.HTML
function onCallback(res) {
alert("data from c.html ---> " + res);
}
</script>
Copy the code
b.html
(www.b.com/b.html):
<iframe
id="iframe"
src="http://www.a.com/c.html"
style="display:none;"
></iframe>
<script>
var iframe = document.getElementById("iframe");
// listen for hash values from A.html and pass them to C.HTML
window.onhashchange = function() {
iframe.src = iframe.src + location.hash;
};
</script>
Copy the code
c.html
(www.a.com/c.html):
<script>
// Listen for hash values from B.html
window.onhashchange = function() {
// Return the result by manipulating the javascript callback of the same domain A.html
window.parent.parent.onCallback(
"hello: " + location.hash.replace("#user="."")); };</script>
Copy the code
window.name
+ iframe
Cross domain
The browser window has the window.name attribute. The most important feature of this property is that, regardless of whether the same source, as long as the previous page in the same window set this property, the next page can read it. And can support very long name values (2MB)
The parent window opens a child window that loads a web page from a different source, which writes information to the window.name property
window.name = data;
Copy the code
Next, the child window jumps back to a url in the same domain as the main window:
location = "http://parent.url.com/xxx.html";
Copy the code
The main window can then read the child window’s window.name:
var data = document.getElementById("myFrame").contentWindow.name;
Copy the code
The advantage of this approach is that window.name is very large and can hold very long strings; The disadvantage is that you must listen for changes in the window.name property of the child window, which affects web page performance
a.html
(www.a.com/a.html):
var proxy = function(url, callback) {
var state = 0;
var iframe = document.createElement("iframe");
// Load the cross-domain page
iframe.src = url;
// The onload event fires twice, the first time the cross-domain page is loaded and the data is stored in window.name
iframe.onload = function() {
if (state === 1) {
// After the second onload succeeds, the data in the domain window.name is read
callback(iframe.contentWindow.name);
destoryFrame();
} else if (state === 0) {
// After the first onload succeeds, switch to the same-domain proxy page
iframe.contentWindow.location = "http://www.a.com/c.html";
state = 1; }};document.body.appendChild(iframe);
// After the data is retrieved, the iframe is destroyed to free memory; This also ensures security (not accessed by other fields frame JS)
function destoryFrame() {
iframe.contentWindow.document.write("");
iframe.contentWindow.close();
document.body.removeChild(iframe); }};// Request cross-domain B page data
proxy("http://www.b.com/b.html".function(data) {
alert(data);
});
Copy the code
C. HTML (www.a.com/c.html): intermediate proxy page, the same domain as A.HTML, the content is empty
b.html
(www.b.com/b.html):
<script>
window.name = "This is b.html data!";
</script>
Copy the code
postMessage
Cross domain
HTML5 has introduced a new API to solve this problem: Cross-document Messaging API.
The API adds a window.postMessage method to window objects, allowing cross-window communication regardless of whether the two Windows are identical
The first parameter of the postMessage method is the specific content of the message, and the second parameter is the origin of the window that receives the message.
It can be used to solve the following problems:
- Data transfer between the page and the new window it opens
- Messaging between multiple Windows
- Page with nested IFrame message delivery
- Cross-domain data transfer for the three scenarios above
For example, a.js b.js uses node to enable two interfaces with different port numbers to simulate a local implementation across domains
The parent window localhost:4001/a.html sends a message to the child window localhost:4002/b.html by calling the postMessage method. Both parent and child Windows can listen for messages from each other through message events
Usage: The postMessage(data, Origin) method takes two arguments:
data
:HTML5
The specification supports any primitive type or copiable object, but some browsers only support strings, so it’s best to use them when passing argumentsJSON.stringify()
serializationorigin
: Protocol + host + port number. The value can also be set to “*”, indicating that it can be passed to any window. If you want to specify the same source as the current window, set it to “/”.
a.html(http://localhost:3000/a.html)
:
<html>
<head>
<title>1111111111</title>
<meta charset="utf-8" />
</head>
<body>
<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</p>
</body>
<script>
window.onmessage = function(e) {
console.warn("a.html:");
console.log(e.data);
console.warn("e:");
console.log(e);
e.source.postMessage("Nice to meet you!", e.origin);
};
</script>
</html>
Copy the code
b.html(http://localhost:4000/b.html)
:
<html>
<head>
<title>bbbbbbbbbbbbbbbbbb</title>
<meta charset="utf-8" />
</head>
<body>
<iframe
src="http://localhost:3000/a.html"
id="frame"
onload="load()"
></iframe>
</body>
<script>
function load() {
let frame = document.getElementById("frame");
frame.contentWindow.postMessage(
"Hello from b.html"."http://localhost:3000"
);
window.onmessage = function(e) {
console.warn("b.html: ");
console.log(e.data);
};
}
</script>
</html>
Copy the code
nginx
Agent cross-domain
Nginx (Engine X) is a high-performance HTTP and reverse proxy server, as well as an IMAP/POP3/SMTP server
Nginx server configuration:
nginx.conf:
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root C:\Projects\Myself\practice; index index.html index.htm; } location /api/ { proxy_pass http://www.jalever.top:4000; Proxy_cookie_domain www.jalever.top localhost; add_header Access-Control-Allow-Origin http://www.jalever.top; * add_header access-control-allow-credentials true; * add_header access-control-allow-credentials true; } # default ... . }Copy the code
Page code:
function load() {
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("get"."http://localhost:80/api/user=admin".true);
xhr.send();
}
Copy the code
Nodejs backend code:
var http = require("http");
var server = http.createServer();
var qs = require("querystring");
var PORT = process.env.PORT || 4000;
server.on("request".function(req, res) {
var params = qs.parse(req.url.substring(2));
res.writeHead(200, {
"Set-Cookie": "l=a123456; Path=/; Domain=www.jalever.top; HttpOnly" // HttpOnly: the script cannot read
});
res.write(JSON.stringify(params));
res.end();
});
server.listen(PORT);
console.log("server is listening on port: ", PORT);
Copy the code
result:
nodejs
Middleware proxies cross domains
NodeJS http-proxy-middleware implements cross-domain proxy. The principle is roughly the same as nginx, which starts a proxy server to realize data forwarding. You can also set the cookieDomainRewrite parameter to modify the domain name in the cookie in the response header to write cookies to the current domain for interface login authentication
WebSocket
Agreement cross-domain
The WebSocket object provides an API for creating and managing WebSocket connections and for sending and receiving data over that connection. It is full duplex communication based on TCP, that is, the server and client can communicate bidirectionally and allow cross-domain communication. The basic protocols are WS ://(non-encrypted) and WSS ://(encrypted)
Page code examples:
function load() {
let socket = new WebSocket("ws://localhost:3000");
socket.onopen = function() {
socket.send("message from index.html");
};
socket.onmessage = function(e) {
console.warn("e.data");
console.log(e.data);
};
}
Copy the code
Node server code:
var express = require("express");
var WebSocket = require("ws");
var port = process.env.PORT || 3000;
var wss = new WebSocket.Server({ port: port });
wss.on("connection".function(ws) {
ws.on("message".function(data) {
console.log("----------data----------");
console.log(data);
console.log("----------data----------");
ws.send("data from node");
});
});
Copy the code