CSRF
CSRF(Cross-site request forgery), 跨站请求伪造, 一种非常常见的 web 攻击.
原理
首先, 快速回顾一下 web 的基础知识, 因为 HTTP 协议是一种无状态的协议, 无状态是什么意思呢, 之前的协议不会对现在的协议造成影响, 也就是说 HTTP 协议是没有记忆的, 记不住信息.
举个例子, 当你访问某个纯静态网页, 你的这次请求和上次请求的结果是完全一样的(排除浏览器缓存等因素后), 不信的话可以自己写一个 html 试试.
既然 HTTP 没有记忆, 那该如何区分用户的身份呢? 很简单, HTTP 虽然没有记忆, 但是你的浏览器是有的啊, 只需要服务器在响应中加入一个独一无二的标识符, 然后让浏览器记住这个标识符, 然后浏览器在每次请求的时候都附带上这个标识符, 这样服务器就可以通过这个标识符确认用户的身份了. 这就是 Cookie 和 Session 的工作原理, 每次请求加上对应的标识符就可证明自己的身份了, 写过爬虫的同学应该不会陌生.
Cookie 和 Session 就不展开了, 感兴趣的可以自己去了解一下, 主要的区别在于是否有服务端状态存储的参与.
因为浏览器在请求的时候会自动附带上 Cookie 来验证自己的身份, 这也造成了一些问题, 什么问题呢, "跨站 Cookie".
虽然浏览器为了安全考虑, 只有在访问 Cookie 所对应的网站的时候才会发送 Cookie. 但是, 虽然我拿不到你的 Cookie, 有没有什么方法可以做到相同的效果呢, 当然有, 直接让浏览器自己发送不就好了.
只需要引诱用户点击自己的链接访问自己的网站, 然后在自己的网站中写一个访问其他网站的请求, 浏览器就会乖乖的自己去请求了, 具体见下图.

例子
直接那一个现成的项目来举例吧, DVWA, 一个专门用来演示 web 漏洞的项目, 搭环境太麻烦了, 直接拉他的镜像就好.
Podman 是一个 docker 的替代品, 对于我来说主要的优势是无守护进程, 并且可以随手配置网络代理. 没错, 可以丢掉烦人的sudo systemctl start docker了.
在终端里运行:
访问本地的 8001 端口, 就可以看到 dvwa 的登陆界面了, 用户名是admin, 密码是password.

然后点下面的按钮配置数据库

重新登陆之后就可以看到主页了

点左边的 CSRF, 然后Ctrl+Shift+C, 选中表单部分, 看看这个表单的结构

竟然是一个使用 GET 方法的表单, 一般来说 GET 请求要求要是幂等的(小朋友不要学哦, 虽然你要用也没人拦着你就是了)

可以看到, 这个表单的结构相当简单, 只有三个字段, 其中password_new和password_conf还是一样的, Change则是一个固定值. (我怎么知道他是一样的呢, 页面上不是写了那么大一个"Confirm new password"嘛)
彳亍, 现在来想想怎么攻击, 先随便试试

emm...我觉得已经没必要试了, 抬头看看浏览器地址栏就知道该怎么做了.
写一小段 HTML, 当点击按钮的时候就会发出跟表单一样的 GET 请求
Document
新密码, 点击就送
但是这样还是有个问题, 用户会知道自己的密码被改了, 如何藏起来呢, 很简单, 因为获取页面资源也是使用的 GET 请求, 只需要在页面里放一个看不见的资源就好了, 就像下面这样:
Document
再丢进浏览器里看看 (因为现代的浏览器已经默认拒绝了这种危险的 Cookie, 需要自己去设置一下)

虽然请求失败了, 但是请求已经成功了(?)
退出重新登录试试, 发现密码已经变成 114514 了.
CSRF 防护
如何抵抗 CSRF 呢, 其实也很简单, 给 Cookie 加一个 SameSite 属性, 这个属性的值为 Strict 或是 Lax, 这样浏览器就知道不要乱发 Cookie 了.
还有另一种更加安全的方法, 在表单中加一个 CSRF Token, 这是一个随机的字符串, 会随着表单一起发送到服务器, 这样服务器只需要验证这个随机的字符串能不能对的上就可以了. 具体原理见下图:

在一般的情况下, SameSite 和 CSRF Token 都是必选的, 另外有些还会有专门的双重提交 CSRF Cookie 来保障 web 安全.
最后, 展示一下战绩, 在 200h 的时候正好 2000pp (虽然 acc 依旧稀烂)

感觉如何, 音游人: good
