GET请求里带操作参数会被CSRF攻击吗?

南宫弋焱 阅读 19

我最近在做用户删除功能,后端同事说不能用GET请求做删除,但我看有些老接口还是用GET传id删数据。现在我用React写了个按钮,点一下就发GET请求删用户,但安全扫描工具报了CSRF风险,这到底是不是问题?

我试过改成POST,但后端还没改,暂时只能用GET。下面是我现在的代码:

const handleDelete = (userId) => {
  fetch(<code>/api/deleteUser?id=${userId}</code>, {
    method: 'GET',
    credentials: 'include'
  }).then(res => res.json())
    .then(data => console.log('Deleted:', data));
};

这种写法真的有CSRF风险吗?如果必须用GET,有什么办法能防护?

我来解答 赞 2 收藏
二维码
手机扫码查看
2 条解答
栾诺
栾诺 Lv1
是的,有CSRF风险,而且不小。

你代码里用了 credentials: 'include',浏览器会在请求里自动带上cookie。攻击者完全可以构造一个页面,里面放个隐藏的img或者自动发请求的script标签,用户只要访问那个页面,就会自动发起删除请求。这跟CSRF攻击的原理完全吻合。

实际上按照HTTP规范(RFC 7231),GET请求应该只用于获取资源,不应该有副作用。删除操作本身就是修改服务器状态,用GET本身就是不规范的做法。你后端同事不让用GET删数据是有道理的,不是故意刁难你。如果暂时必须用GET,有几个临时方案:

一是后端生成一个CSRF token,前端每次请求带上这个token,后端验证。不过既然后端连POST都没改,大概率也没做这个。

二是用自定义请求头,比如 X-CSRF-Token,因为简单请求不会带自定义头,攻击者无法通过页面脚本构造带这个头的请求。但后端得配合改。

三是检查Referer或者Origin头,虽然可以防一部分,但不是完全可靠。

最靠谱的还是把删除改成POST或者DELETE。后端同事不改是他的问题,但你得把这个风险说清楚——安全扫描工具报出来了真出了事就是生产事故。跟他约个时间 deadline把这事排上,比搞各种临时方案省心多了。
点赞
2026-03-11 15:20
倩影 ☘︎
当时我也卡在这,查了不少资料,还特意去翻了OWASP的CSRF防御指南,得说清楚:
用GET请求做删除操作,本身就是违反HTTP语义的,而且确实存在CSRF风险,哪怕你后端做了登录校验也不行。

为什么?因为CSRF的本质是:攻击者诱导你访问一个恶意页面,页面里悄悄发请求到你的目标站(比如你正在登录的后台),浏览器会自动带上cookie,如果后端只校验登录态,不校验请求意图,那就中招了。
GET请求天然容易被“诱导访问”——比如你帖个链接、图片标签、iframe、甚至别人在论坛里发个带参数的URL,你点一下就触发了,根本不用你点按钮。

你现在的代码里用的是 credentials: 'include',意味着会带上cookie,如果后端没做额外防护,攻击者只要构造一个链接比如 https://yourdomain.com/api/deleteUser?id=123,放到img标签里:



用户只要访问了那个页面,就默默删掉了用户123。
这事儿真不是危言耸听,我当年就见过生产环境这么被黑掉的——攻击者把链接发到内部群,一个同事点开手机通知预览了一下,账号就删了,因为预览时浏览器自动发了GET请求。

必须用GET的话,有三个实测可行的临时方案(但还是强烈建议尽快改成POST):

1. 加一个随机token,比如GET里带 ?id=123&token=xxx,token存在session里或JWT里,后端校验这个token是否匹配,且只用一次(用完即失效),这个叫Synchronizer Token Pattern的变体。
但注意:token不能放在cookie里自动带,否则CSRF又绕回来了——你得在前端生成token(比如从localStorage取),手动拼到URL里,后端比对。
这个方案麻烦点在于:token怎么安全下发?如果token本身通过页面渲染带进JS,那攻击者如果能XSS也能拿到,但至少比纯GET安全多了。

2. 检查 OriginReferer 头(注意:Referer可能被隐私插件删掉,Origin相对靠谱),但这个只能防跨域,不能防同域的XSS+CSRF组合拳。

3. 用 SameSite=Strict 的cookie(你后端设cookie的时候加这个属性),这样跨站请求根本不会带cookie,CSRF自然失效。
但注意:如果用户是通过站内链接跳转(比如从A页面点链接到B页面删用户),SameSite=Strict会断掉登录态,导致删不了——所以一般用 Lax,但Lax对GET请求还是会带cookie,不能彻底防CSRF。
如果你后端能改成 SameSite=Strict,且用户操作都在同源页面内完成(不跳转),那是可行的。

说白了,临时方案都有坑,最靠谱的还是改POST + CSRF token。
你后端没改的话,可以先推动一下:前端改用POST,带个隐藏字段或请求头传token,比如:

const handleDelete = (userId) => {
const token = localStorage.getItem('csrfToken'); // 从页面渲染时注入的JS变量里取
fetch('/api/deleteUser', {
method: 'POST',
credentials: 'include',
headers: {
'X-CSRF-Token': token,
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: userId })
}).then(res => res.json())
.then(data => console.log('Deleted:', data));
};

后端收到POST后,校验token是否匹配session里的,或者校验 X-CSRF-Token 头和cookie里的token是否一致。

我后来自己项目里也这么干过,改完之后扫描工具再没报CSRF了。
记住:语义上不该用GET做写操作,这是底线,别为了省点后端改动时间埋雷,等出事了排查起来更费劲。
点赞 4
2026-02-27 19:02