使用WTforms和webapp2进行CSRF防护

3

WTForms文档为使用Flask实现CSRF提供了很好的示例:

class MyBaseForm(Form):
    class Meta:
        csrf = True
        csrf_class = SessionCSRF
        csrf_secret = app.config['CSRF_SECRET_KEY']

        @property
        def csrf_context(self):
            return session

我想做同样的事情,但使用webapp2会话而不是Flask会话。 csrf_context 是一个快捷方式,这样你就不必每次创建表单时都传递会话。有人知道如何为webapp2会话创建这样的快捷方式吗?
如果没有这个快捷方式,每次创建表单时都需要像这样做:
form = MyForm(meta={'csrf_context': self.session})

这是一些相当笨拙的语法,我希望能够避免使用。

1个回答

2

我想出了一种解决方案,可以减少我在问题中描述的笨拙语法。我修改了我的wt.Form子类的__init__如下:

class MyBaseForm(wt.Form):
    class Meta:
        csrf = True
        csrf_class = SessionCSRF
        csrf_secret = settings.SESSION_KEY
        csrf_time_limit = timedelta(minutes=60)

    def __init__(self, *args, **kwargs):
        if "session" in kwargs:
            super(MyBaseForm, self).__init__(
                *args, meta={'csrf_context': kwargs["session"]}, **kwargs)
        else:
            super(MyBaseForm, self).__init__(*args, **kwargs)

现在,当我创建一个表单时,我可以这样做:
form = MyForm(session=self.session)

与其使用问题中显示的笨拙语法,不如采用下面提供的技巧。

对于处理已发布表单数据,我找到了另一种简化处理的技术。

首先,我创建一个只有CSRF字段而没有其他字段的表单:

class NoFieldForm(MyBaseForm):
    pass

其次,我创建了一个装饰器来检查 CSRF:

def check_csrf(func):
    def wrapper(*args, **kwargs):
        handler = args[0]
        session = handler.session
        request = handler.request
        f = forms.NoFieldForm(request.POST, session=session)
        f.validate()
        if f.csrf_token.errors:
            msg = "The CSRF token expired. Please try again. "
            self.session["msg"] = msg
            self.redirect(self.request.path)
        else:
            func(*args, **kwargs)
    return wrapper

第三步,我装饰所有的POST处理程序:

class SomeHandler(webapp2.RequestHandler):
    @check_csrf
    def post(self):
        pass

让我解释一下。装饰器(通过调用post webhandler)将接收一些表单数据,但为了进行CSRF检查,我们将丢弃除CSRF之外的所有表单数据。我们可以使用NoFieldForm使其通用,该表单忽略任何其他存在的表单数据。我使用wtforms来检查CSRF表单字段和会话令牌是否匹配,并且会话令牌未过期。

如果CSRF通过,则调用处理程序以处理特定表单的正常处理。如果CSRF失败,则根本不调用处理程序,在我的示例中,我们将重定向回到出发点并显示错误消息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接