flask-wtf CSRFProtect 분석

Hunjison·2021년 10월 30일
0

flask에서 CSRF token을 적용할 때에 거의 대부분 사용하는 것으로 보이는 flask-wft의 CSRFProtect를 분석해보았다.

분석 결과

ssti 등으로 flask 전역 변수를 변경할 수 있다면 CSRF를 우회할 수 있을 것으로 보인다.
ssti가 될 때 CSRF를 굳이 우회할 필요가 있을까..?

동작 원리

https://github.com/wtforms/flask-wtf/blob/main/src/flask_wtf/csrf.py

여기에서 CSRFProtect 클래스를 따라가면서 동작 원리를 살펴보자.

아래와 같이 기본 변수들을 설정한다. csrf_protect()를 request가 도착할 때마다 수행하는데, WTF_CSRF_ENABLED or WTF_CSRF_CHECK_DEFAULT가 False면 return 한다(검사를 수행하지 않는다)

def init_app(self, app):
    app.extensions["csrf"] = self

    app.config.setdefault("WTF_CSRF_ENABLED", True)
    app.config.setdefault("WTF_CSRF_CHECK_DEFAULT", True)
    app.config["WTF_CSRF_METHODS"] = set(
        app.config.get("WTF_CSRF_METHODS", ["POST", "PUT", "PATCH", "DELETE"])
    )
    app.config.setdefault("WTF_CSRF_FIELD_NAME", "csrf_token")
    app.config.setdefault("WTF_CSRF_HEADERS", ["X-CSRFToken", "X-CSRF-Token"])
    app.config.setdefault("WTF_CSRF_TIME_LIMIT", 3600)
    app.config.setdefault("WTF_CSRF_SSL_STRICT", True)

    app.jinja_env.globals["csrf_token"] = generate_csrf
    app.context_processor(lambda: {"csrf_token": generate_csrf})

    @app.before_request
    def csrf_protect():
        if not app.config["WTF_CSRF_ENABLED"]:
            return

        if not app.config["WTF_CSRF_CHECK_DEFAULT"]:
            return

        if request.method not in app.config["WTF_CSRF_METHODS"]:
            return

        if not request.endpoint:
            return

        if request.blueprint in self._exempt_blueprints:
            return

        view = app.view_functions.get(request.endpoint)
        dest = f"{view.__module__}.{view.__name__}"

        if dest in self._exempt_views:
            return

        self.protect()

이어서 protect() 를 보자.
method가 위에서 설정한 method(["POST", "PUT", "PATCH", "DELETE"])에 포함되지 않으면 검사 수행하지 않는다.
try 구문 아래에서 CSRF 검사를 수행하고, 아래에 추가적으로 referrer 검사와 SOP 검사도 수행한다.

def protect(self):
    if request.method not in current_app.config["WTF_CSRF_METHODS"]:
        return

    try:
        validate_csrf(self._get_csrf_token())
    except ValidationError as e:
        logger.info(e.args[0])
        self._error_response(e.args[0])

    if request.is_secure and current_app.config["WTF_CSRF_SSL_STRICT"]:
        if not request.referrer:
            self._error_response("The referrer header is missing.")

        good_referrer = f"https://{request.host}/"

        if not same_origin(request.referrer, good_referrer):
            self._error_response("The referrer does not match the host.")

    g.csrf_valid = True  # mark this request as CSRF valid

결론적으로 WTF_CSRF_ENABLED or WTF_CSRF_CHECK_DEFAULTFalse로 만들거나, WTF_CSRF_METHODS[]로 만들면 모든 검사를 하지 않도록 변경할 수 있다.

근데 다시 여기서 드는 의문은 ssti가 될 때 CSRF를 굳이 우회할 필요가 있을까..?

profile
비전공자 출신 화이트햇 해커

0개의 댓글