Python CAPTCHA 的服务端验证实践

1次阅读

captcha.verify()总返回false的主因是服务端验证时key与前端提交的不一致,常见于session未启用、redis与内存混用、跨域cookie配置不当(缺samesite=none; secure)、django的session_engine设为cache时读不到值、flask中手动操作session而非调用validate_captcha()等。

Python CAPTCHA 的服务端验证实践

为什么 captcha.verify() 总返回 False

不是验证码图片没生成对,而是服务端验证时用的 key 和前端提交的不一致——常见于 session 未启用、或用了不同 session backend(比如 Redis 和内存混用)。

  • captcha.generate() 默认把答案存进当前请求的 session,验证时也从同一 session 读;如果前后端走的是不同域名、或没带 SameSite=None; Secure 的 Cookie 配置,session 就断了
  • Django 用户注意:SESSION_ENGINE 设成 django.contrib.sessions.backends.cache 时,captcha 可能读不到值,建议改用 dbcached_db
  • Flask 用户别直接用 session['captcha_key'] 手动存取,flask-captcha 内部用的是带前缀的键名,得走它自己的 validate_captcha() 函数

django-simple-captcha 时怎么绕过数据库查表?

默认每次验证都查一次 CaptchaStore 表,QPS 上去后容易成瓶颈;其实可以关掉自动清理 + 改用缓存存储答案。

  • 在 settings.py 加上:CAPTCHA_CACHE_PREFIX = "captcha:",并确保 CACHES 配置可用
  • CAPTCHA_GET_FROM_POOL = True 启用预生成池,避免实时生成拖慢首屏
  • 禁用自动清理:CAPTCHA_AUTO_CLEAN = False,自己用定时任务清过期项,否则缓存里会越积越多
  • 注意:CAPTCHA_CACHE_TIMEOUT 默认是 5 分钟,和前端倒计时要对齐,否则用户看到“还剩 10 秒”但后端已失效

Flask + flask-captcha 验证失败却不报错?

默认静默失败,只返回 False,连日志都不打——调试时根本不知道卡在哪一步。

  • 调用 validate_captcha() 前,先确认 request.form.get('captcha') 确实拿到了值,且没被中间件(如 csrf 插件)误删
  • 检查 captcha_id 字段是否随表单一起提交,flask-captcha 要求必须传这个 ID 才能定位对应答案
  • 开启 debug 模式:app.config['CAPTCHA_DEBUG'] = True,它会在 response headers 里写入 X-Captcha-Debug: reason=xxx,比如 reason=expiredreason=not_found
  • 别用 request.json 提交验证码——该库只认 formargs,JSON body 会被忽略

自建 CAPTCHA 服务时,imageioPIL 图像生成差异在哪?

不用第三方包也能画图,但选错库会导致字体渲染异常、中文乱码、或 docker 里跑不起来。

立即学习Python免费学习笔记(深入)”;

  • PIL.ImageDraw.text() 在无字体路径时会 fallback 到内置 bitmap 字体,不支持中文;必须显式传 font=ImageFont.truetype("DejaVuSans.ttf", 16)
  • imageio 本身不处理文字,得配合 numpy 手动画像素,适合极简数字验证码,但维护成本高
  • Docker 镜像记得装系统字体:apt-get install -y fonts-dejavu,否则 PIL 找不到 ttf 文件,抛 OSError: cannot open Resource
  • 生成 GIF 动态验证码时,PILsave(..., save_all=True) 容易内存暴涨,建议用 imageio.mimsave() 替代

最麻烦的其实是时间同步问题:服务端生成时用本地时间算过期,但负载均衡后各机器时钟差几秒,就可能导致刚生成的验证码立刻失效。NTP 校准不是可选项,是必选项。

text=ZqhQzanResources