为什么我们总说POST比GFET安全

为什么我们总在说 POST 比 GET 安全,那么到底安全在哪里呢?是只有面试题上洋洋洒洒罗列的那些问题么。

为什么我们总在说 POST 比 GET 安全,那么到底安全在哪里呢?是只有面试题上洋洋洒洒罗列的那些问题么。

起源

这个问题的起源是小伙伴在面试的时候问候选人 “ POST 与 GET 的差异,然后提到了 POST 比 GET 安全”,然后前端小组内由 PSOT 比 GET 安全在哪些地方进行了充分的讨论。

讨论

那我们先来看看普通版本的面试题答案,POST 比 GET 的优势:

1
2
3
4
5
GET 请求参数是放在 URL 上,有长度的限制,POST 没有
对参数数据类型,GET 只接受 ASCII 字符,而 POST 没有限制
GET 请求会被浏览器缓存造成意外的问题,而 POST 除非手动设置否则不会
GET 请求重放的时候不会对用户进行提示,POST 会弹窗提示,
POST 会比 GET 请求安全,因为在参数在 body 中而不是 URL 上...

那么我们一起来看看,到底为什么 POST 会比 GET 安全。

首先我们来看一个场景,萌新入职第一天愉快的登陆了 A网站 完成了注册登陆大礼包。
这时候老司机想看看萌新的安全意识直接丢了一个神秘链接给萌新,萌新开心的打开,突然发现页面 CSRF 四个大字在页面上。

萌新一脸无辜赶紧请教老司机,老司机不疾不徐的道来: “刚你在 A网站进行了登陆,这个时候服务端会给你设置一个cookie,这里cookie会存在浏览器本地,在有效期内同源的请求都会默认携带上这个cookie。我就是利用了这一点对你完成了 CSRF 的跨站脚本攻击”。

“可是什么究竟什么是跨站脚本攻击呢?”

CSRF(Cross-site request forgery)跨站请求伪造:即为在第三方网站中,向被攻击网站发送跨站请求。利用浏览本地存储的被攻击网站的cookie,绕过服务端的验证,达到冒充用户的目的。
“那么 CSRF 与 POST 更安全有什么关系呢?”

我们来展示一个简单的 demo 被攻击网站:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>被攻击网站</title>
</head>
<body>
<p>被攻击网站</p >
<script>
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:3000");
xhr.setRequestHeader("Content-Type","application/xml");
xhr.send();
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const express = require('express')
const fs = require("fs");
const app = express()
const port = 3000;
app.all("*", (req,res,next) => {
res.header("Access-Control-Allow-Origin","http://localhost:3000");
res.header("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, " + "WG-App-Version, WG-Device-Id, WG-Network-Type, WG-Vendor, WG-OS-Type, WG-OS-Version, WG-Device-Model, WG-CPU, WG-Sid, WG-App-Id, WG-Token");
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options') {
console.log("i get a option request")
res.send(200);
}
else {
next();
}
});
// 返回被攻击网站html
app.get('/index', (req, res) => {
const filename = "./index.html";
fs.readFile(filename, (err, result) => {
res.set("content-type", "text/html");
res.send(result);
res.end();
});
})
app.all('/', (req, res) => {
console.log(req.headers.referer)
if (req.headers.referer !== "http://localhost:3001/csrf.html") {
res.cookie('isVisit', 12345, {maxAge: 60 * 1000})
}
res.send(JSON.stringify({test: "test"}))
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
1
2
3
4
5
6
7
8
9
10
11
12
13
// csrf.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>csrf</title>
</head>
<body>
<p>攻击网站</p >
< img src="http://localhost:3000">
</body>
</html>

简单来说就是启动一个被攻击网站,然后攻击网站利用 image 发送一个 GET 请求来窃取用户的 cookie 达到CSRF攻击的目的。

那 POST 相较 GET 更安全那我们就要看 POST 与 GET 的区别了。

首先 GET 请求发送没有同源策略的限制,在任意的域名下都会直接发起请求,而POST请求在跨域的情况下会先发一个 OPTION 请求到服务端探寻是否允许跨域,如果服务端的设置的 Access-Control-Allow-Origin 没有设置 * 或者允许特定的域名的情况下。比如你的服务端仅设置 Access-Control-Allow-Origin: http://www.a.com 那么你在 http://www.b.com 的域名下发送一个 POST 请求会先发出一个 OPTION 的请求来探测服务端是否允许跨域。如果服务端设置的 Access-Control-Allow-Origin没有当前域名的话则POST 的请求主体则不会发送。这样的差异在服务端的差异就变成了 GTE 请求在服务端会直接收到请求并按照正常请求的逻辑去处理,而 POST 请求业务上会变成在跨域的时候仅会发送 OPTION 请求服务端并不会处理具体的业务逻辑,且 OPTION 请求并不会携带 cookie 。

从这个方面我们可以看出在跨域的情况下 POST 请求因为会预发一个 OPTION 请求从而避免像 GET 请求一样被恶意窃取 cookie 。

GET请求究竟有哪些缺陷?

1
2
3
4
5
1. 在低版本浏览器上GET请求会被缓存,造成意外的结果。
2. JSONP实现的GET请求天然存在被XSS的风险
3. GET请求会被缓存,造成意外的返回值。
4. GET请求URL长度限制
5. GET请求参数类型限制(GET仅能接受ASCII)

PSOT好在哪里

1
2
3
4
1. POST在跨域的时候会先发一个option请求检查服务端是否允许跨域。(CORS跨站脚本攻击)
2. POST参数无类型/大小限制
3. 重放POST请求的时候会提示用户。
4. POST在设置 csrfToken 放置在header中不会被窃取。

hi you can see me