GET请求能用来提交敏感操作吗?CSRF防护怎么处理?
最近在写一个删除用户的功能,后端同事说要用 POST,但我图省事直接用 GET 发了个带 token 的链接,结果被安全扫描工具报了 CSRF 风险。不是已经有 token 了吗?为啥 GET 还不行?
我试过在 Vue 里这样写:
<template>
<a @click="deleteUser">删除账号</a>
</template>
<script>
export default {
methods: {
async deleteUser() {
// 用 GET 带上 token
await this.$http.get('/api/delete-user?token=' + this.csrfToken);
}
}
}
</script>
但安全团队说这违反了“GET 不应产生副作用”的原则,哪怕有 token 也不行。那到底该怎么改?是不是必须换成 POST 才算合规?
为什么GET+URL参数token不行?
token放URL里跟没放差不多,原因很简单:这货会出现在服务器日志里、浏览器历史记录里、还有Referer请求头里。攻击者想获取token?翻日志就完事了。而且GET请求天然可以被浏览器预加载、可以被搜索引擎爬、可以被各种脚本直接请求,防护形同虚设。
怎么改?
把GET改成POST,token放请求体里:
后端接口也得跟着改,用POST方式接收。
再补充一下
如果想更省事儿,可以把CSRF token放到Cookie里(SameSite=Strict或Lax),然后后端自动校验。这样前端代码完全不用改,后端加个中间件就行。不过看你们安全团队这态度,还是踏实用POST+请求体token吧,省得又被扫出问题。
1. GET请求会被各种地方意外触发:浏览器预加载、搜索引擎爬虫、代理服务器缓存等等,你的删除链接可能莫名其妙就被执行了
2. GET请求参数会完整暴露在地址栏、服务器日志、浏览器历史记录里,token直接裸奔
安全团队说的没错,RESTful规范里GET应该只用于获取数据,修改数据的操作必须用POST/PUT/DELETE。你这情况可以这样优化:
顺便吐槽下,你们后端设计也有问题,删除操作应该用DELETE方法更规范。不过用POST至少比GET强多了。
另外前端模板那块可以改成按钮更合适,毕竟删除是危险操作:
再加个确认对话框更保险:
这样改完之后,CSRF防护既有了token,又符合HTTP方法规范,安全团队应该就没话说了。