CTFd CSRF 校验模块解读
实现上传头像功能的时候遇到了 403,隐约感觉到可能是 csrf 的问题,但是嫌麻烦一直懒得翻源码里的校验逻辑,反而让自己怀疑是 mime type 有限制,走了弯路。
机缘巧合发现了 csrf 的校验位置,总算是通了
定位代码位置
在 CTFd/__init__.py
文件下,观察如下代码片段
init_request_processors(app)init_template_filters(app)init_template_globals(app)# Importing here allows tests to use sensible names (e.g. api instead of api_bp)from CTFd.admin import adminfrom CTFd.api import apifrom CTFd.auth import authfrom CTFd.challenges import challengesfrom CTFd.errors import render_errorfrom CTFd.events import eventsfrom CTFd.matches import matchesfrom CTFd.scoreboard import scoreboardfrom CTFd.share import socialfrom CTFd.teams import teamsfrom CTFd.users import usersfrom CTFd.views import viewsfrom CTFd.writeup import writeupapp.register_blueprint(views)app.register_blueprint(teams)app.register_blueprint(users)app.register_blueprint(matches)app.register_blueprint(challenges)
大概能猜出来 csrf 校验应该是在注册蓝图之前初始化的,也就是 init_request_processors
。进去后发现一堆 @app.before_request
的装饰器,感觉对了。往下翻,查询 403 找到
@app.before_requestdef csrf():try:func = app.view_functions[request.endpoint]except KeyError:abort(404)if hasattr(func, "_bypass_csrf"):returnif request.headers.get("Authorization"):returnif not session.get("nonce"):session["nonce"] = generate_nonce()if request.method not in ("GET", "HEAD", "OPTIONS", "TRACE"):if request.content_type == "application/json":if session["nonce"] != request.headers.get("CSRF-Token"):abort(403)if request.content_type != "application/json":if session["nonce"] != request.form.get("nonce"):abort(403)
bingo。原来只有 json 的 csrf token 是放在 header 里的,难怪 403 了。