# 跨域问题
网络攻防包括: CSRF(跨站请求伪造攻击),XSS(跨站脚步攻击),SQL 注入,cookie 拦截修改
# 表现形式
1,接口 403
2,cor 跨域
# 产生问题的原因
# 网络域和浏览器的同源策略
# 什么是同源策略
1,协议相同
2,域名相同
3,端口相同
三者同时成立叫同源,
从域名 A 下的一个页面(一般是通过 ajax 请求)获取域名 B 下的一个资源,是不被浏览器容许的。
# 跨域有四个点要满足:
1,A 和 B 不同源
2,同源策略是浏览器做的限制(所以 APP 永远不会跨域)
3,从 H5 发送的请求,经过浏览器
4,跨域的意思是:浏览器还是会发送请求,但浏览器会拦截响应内容,如果发现响应 header 中有 "Access-Control-Allow-Origin"设置的容许访问源没有包含当前源,则拒绝将数据返回给当前源(可以设置值为 * )
ep: <img> <script> <style><link>
等标签里有 src 是不做同源限制的
# 绕过同源策略的多种方式
# 1 服务器做设置,即响应头添加"Access-Control-Allow-Origin",指定容许访问的源
@PostMapping "/account/login";
@ResponseBody
public ResulttModel<String> login
HttpServietResponse response,
HttpServietRequest request,
@RequestParam String useId,
@RequesstParam String password
// response.setHeader("Access-Control-Allow-Origin", "*");
return this.accountService.login response,userId, password;
node 的写法
// servernode.js 中间件
const http = require("http");
// 第一步 接受客户端请求
const server = http.createServer((request, response) => {
// 代理服务器,直接和浏览器交互,设置CORS的首部字段
response.writeHead(200, {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "*",
"Access-Control-Allow-Headers": "Content-Type",
});
// 第二步 将请求转发给服务器
const proxyRequest = http
.request(
{
host: "127.0.0.1",
port: 4000,
url: "/",
method: request.method,
headers: request.headers,
},
(serverResponse) => {
// 第三步 服务器收到响应
var body = "";
serverResponse.on("data", (chunk) => {
body += chunk;
});
serverResponse.on("end", () => {
console.log("the data is" + body);
// 第四步 将响应结果转发给浏览器
response.end(body);
});
}
)
.end();
});
server.listen(3000, () => {
console.log("The proxyServer is running at http://localhost:3000");
});
// servver2.js
const http = require("http");
const data = { title: "fontend", password: "123456" };
const server = http.createServer((request, response) => {
if (request.url === "/") {
response.end(JSON.stringify(data));
}
});
server.listen(4000, () => {
console.log("The server is running at http://localhost:4000");
});
# 2 document.domain
var w = window.open("http://www.qq.com");
w.document;
// Uncaught demoxception: Blocked a frame with origin "http://id.qq.com" from accessing a cross-origin frame.
// 设置document.domain
document.domain = "qq.com";
w.document;
// success
# 3 利用<script>
不受限制的设定,通过把不同源的请求伪装成一个脚本请求,服务器端返回数据后,脚本会自动回调,这就是所谓的 JSONP 跨域。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script>
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement("script");
window[callback] = function(data) {
resolve(data);
document.body.removeChild(script);
};
params = { ...params, callback };
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: "iloveyou" },
callback: "show",
}).then((data) => {
console.log(data);
});
</script>
</body>
</html>
// node服务
let express = require("express");
let app = express();
app.get("/say", function(req, res) {
let { wd, callback } = req.query;
console.log(wd);
console.log(callback);
res.end(`${callback}("我不爱你")`);
});
app.listen(3000);
# 4 服务器代理,正向代理与反向代理
正向代理:与页面同源的服务器代你向不同源的服务器请求数据并转发到页面 反向代理:使用 nginx 地址映射(a 请求 b,a 的服务器上虚拟出服务 c,其实 c 只是表面上和 a 同源,真正的映射到到 b,a 实际上是向 b 请求)
# 5 CORS 跨域资源共享(Cross-origin resource sharing)
实现 CORS 的关键在于服务器,只要服务器实现 CORS 接口,就可以跨域通信。 需要注意的是,CORS 解决跨域的话,Content-Type 的值不属于下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
...
<script>
let xhr = new XMLHttpRequest();
document.cookie = "name=xiaomin"; // cookie 不能跨域
xhr.withCredentials = true; // 前端设置是否带cookie
xhr.open("PUT", "http://localhost:4000/getData", true);
xhr.setRequestHeader("name", "xiaomin");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response);
console.log(xhr.getResponseHeader("name"));
}
}
};
xhr.send();
</script>
...
// node js server1.js
// 设置白名单
let express = require("express");
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
// node js server2.js
let express = require("express")
let app = express();
let whiteList=["http://localhost:3000"] // 设置白名单
app.use(function(req, res, next){
let origin = req.headers.origin;
if(whiteList.includes(origin)){
// 设置哪个源可以访问我
res.setHeader("Access-Control-Allow-Origin", origin);
// 允许哪个头访问我
res.setHeader("Access-Control-Allow-Headers", "name");
// 允许哪个方法访问我 为避免多次"预检"请求,返回所有支持的方法
res.setHeader("Access-Control-Allow-Methods", "PUT,GET");
// 允许携带cookie
res.setHeader("Access-Control-Allow-Credentials", true);
// 预检的存活时间 单位是秒 容许缓存该条回应xxs,在此期间,不用发出另一条预检请求
res.setHeader("Access-Control-Max-Age", 6);
// 允许返回头
res.setHeader("Access-Control-Expose-Headers", "name");
if(req.method === "OPTIONS"){
res.end();
}
}
next();
})
app.put("/getData", function(req, res){
console.log(req.headers);
res.setHeader("name", "jw");
res.end("我不爱你瞭")
})
app.get("/getData", function(req, res){
console.log(req.header);
res.end("dodo 不爱");
})
app.use(express.static(__dirname))
app.listen(4000)
# 6 window.postMessage()
<!-- A窗口<http://localhost:3000/a.html> -->
<!DOCTYPE html>
<html charset="UTF-8">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>DOCUMENT</title>
</head>
<body>
<iframe
src="http://localhost:4000/b.html"
frameborder="0"
id="frame"
onload="load()"
></iframe>
<script>
function load() {
let frame = document.getElementById("frame");
frame.contentWindow.postMessage("iloveyou", "http://localhost:4000");
window.addEventListener("message", receiveMessage, false);
}
// event.source 是通过window.open打开的弹出页面
// event.data 是popup发送给当前页面的消息
function receiveMessage(event) {
if (event.origin !== "http://localhost:4000") return;
console.log(event.data);
}
</script>
</body>
</html>
<!-- // b窗口<http://localhost:3000/b.html> -->
<!DOCTYPE html>
<html charset="UTF-8">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>DOCUMENT</title>
</head>
<body>
<script>
window.onmessage = function(e) {
console.log(e.data);
e.source.postMessage("idontloveyou", e.origin);
};
</script>
</body>
</html>
# 7 window.name
a 和 b 是同域的 http://localhost:3000 c 是独立的 http://localhost:4000 a 获取 c 的数据 a 先引用 c c 把值放到 window.name,把 a 引用的地址改到 b
<body>
<iframe
src="http://localhost:4000/c.html"
frameborder="0"
onload="load()"
id="iframe"
></iframe>
<script>
let first = true;
function load() {
if (first) {
let iframe = document.getElementById("iframe");
iframe.src = "http://localhoset:3000/b.html";
first = false;
} else {
console.log(iframe.contentWindow.name);
}
}
</script>
</body>
// b.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body></body>
</html>
// c.html
<body>
window.name="我不爱你"
</body>
# 8 window.onhashchange
路径后的 hash 值可以用来通信 目的 a 想访问 c a 给 c 转一个 hash c 收到 hash 后 c 把 hash 传给 b b 将结果放到 a 的 hash 里
<!-- // a.html -->
<script>
window.onhashchange = function() {
console.log(location.hash);
};
</script>
<!-- // b.html -->
<script>
window.parent.parent.location.hash = location.hash);
</script>
<!-- // c.html -->
<script>
console.log(location.hash);
let iframe = document.createElement("iframe");
iframe.src = "http://localhost:3000/b.html#idonotloveyou";
document.body.appendChild(iframe);
</script>
# 9 websocket
let express = require("express");
let app = express();
let WebSocket = require("ws");
let wss = new WebSocket.Server({ port: 3000 });
wss.on("connection", function(ws) {
ws.on("message", function(data) {
console.log(data);
ws.send("我不爱你");
});
});
<!-- // a.html -->
<script>
let socket = new WebSocket("ws://localhost:3000");
socket.onopen = function() {
socket.send("iloveyoou");
};
socket.onmessage = function(e) {
console.log(e.data);
};
</script>