登录一个有 CSRF 保护的表单,请求序列比想象的长:先取页面拿 cookie 和 token,再带着所有隐藏字段 POST,失败后 token 还会换。这篇在本机写一个登录页,逐步走通正确的流程,再逐个故意做错,看服务器怎么拒绝。
① GET /login → 200,Set-Cookie: sid=65e3a71108ced97f; Path=/; …;页面里的表单字段(含隐藏字段)= {'csrf_token': '4a1ed29ca5ebf5a8', 'form_id': 'login-v2', 'next': '/account', 'username': '', 'password': '', 'website': ''}
② POST /login(带上全部隐藏字段和账号密码)→ 302,Location=/account,Set-Cookie: sid=90a57a610d82c7af; Path=/; …(登录成功后会话 id 换了)
③ GET /account → 200 'welcome alice'
会话 cookie 在登录前后的值:sid=65e3a71108ced97f → sid=90a57a610d82c7af(不同)先 GET /login 拿到会话 cookie 和页面里的 csrf_token,再 POST /login 带上表单里所有字段(隐藏字段取默认值,只填账号密码),成功后 302 跳转并换新的会话 id。requests.Session 负责 cookie 的往返。
没先 GET 表单、没有 cookie 就直接 POST:403 'no session' 有 cookie 但漏了 csrf_token 字段:403 'csrf token mismatch' csrf_token 值不对:403 'csrf token mismatch' token 对但漏了另一个隐藏字段 form_id:400 'missing form_id' 把页面上所有 text 输入框都填了(包括 CSS 隐藏的蜜罐 website):400 'honeypot filled' 用会话 A 的 token 配会话 B 的 cookie:403 'csrf token mismatch'
不先取表单、漏 token、token 不对、拿别的会话的 token,都是 403;缺别的隐藏字段是 400;把页面上所有输入框都填了(包括 CSS 隐藏的)会被服务器拒绝。做法是像浏览器一样提交:收集所有带 name 的 input,隐藏字段原样带回,只覆盖账号密码。
① 密码错误:401(服务器换了新的 token) ② 还用旧 token 再提交正确密码:403 'csrf token mismatch' ③ 重新 GET 表单拿新 token 后提交:302
登录失败后服务器换了新 token,旧 token 再提交被拒绝。重试要重新 GET 表单。
只发 JSON、不带头:403 'csrf header mismatch'
带 X-CSRF-Token: 200 '{"ok": true}'前端发 JSON 请求时,token 常放在 <meta name="csrf-token"> 里,用 X-CSRF-Token 请求头回传;只发 JSON 不带头会被拒绝。
做法:取表单里所有带 name 的 <input>(隐藏字段取默认值),只覆盖用户名、密码;不要碰蜜罐和其它隐藏字段。上面第一节就是这样做的:字段 = ['csrf_token', 'form_id', 'next', 'password', 'username', 'website']
在这个页面上表单共有 6 个字段,爬虫只覆盖 username 和 password,其余按页面原样提交。