如何在Pyramid中添加CSRF验证?

8 投票
2 回答
4062 浏览
提问于 2025-04-16 20:03

我在每次发送帖子和XHR请求时都会传递一个csrf_token,并想要把这个token和会话中的csrf_token进行验证。如果它们不匹配,我就返回一个401错误。

我在Pyramid框架中使用了NewResponse订阅者来检查请求,并验证请求参数中的csrf_token是否和会话中的token一致。验证是成功的,但请求还是会调用视图,所以这并没有按预期工作。

有没有什么好的建议来正确处理这个问题?

@subscriber(NewResponse)
def new_response(event):
    """Check the csrf_token if the user is authenticated and the 
    request is a post or xhr req.
    """
request = event.request
response = event.response
user = getattr(request, 'user', None)
# For now all xhr request are csrf protected.
if (user and user.is_authenticated()) and \
   (request.method == "POST" or request.is_xhr) and \
    (not request.params.get('csrf_token') or \
    request.params.get('csrf_token') != unicode(request.session.get_csrf_token())):
    response.status = '401 Unauthorized'
    response.app_iter = []

2 个回答

1

Pyramid框架自带了自己的CSRF验证功能,这可能是个更好的选择。

如果你在会话中存储了CSRF令牌,那么你可以按照以下配置进行设置:

from pyramid.csrf import SessionCSRFStoragePolicy

def includeme(config):
    # ...
    config.set_csrf_storage_policy(SessionCSRFStoragePolicy())
    config.set_default_csrf_options(require_csrf=True)
9

当你的视图被调用后,NewResponse 这个订阅者会被触发。

你其实应该使用一个更早被触发的事件,比如 NewRequest 或者 ContextFound。在 Pyramid 1.0 版本中,你需要使用 ContextFound 来正确处理事情,因为在 NewRequest 事件中不能抛出异常(这个问题在 1.1 版本中修复了)。

使用 ContextFound 事件的方式是为 HTTPException 对象注册一个异常视图,像这样:

config.add_view(lambda ctx, req: ctx, 'pyramid.httpexceptions.HTTPException')

简单来说,当你抛出异常时,这样做会把异常作为响应对象返回,这对于有效的 HTTPException 对象来说是完全可以的,因为它们是有效的 Pyramid Response 对象。

然后你可以注册你的事件,并处理 CSRF 验证:

@subscriber(ContextFound)
def csrf_validation_event(event):
    request = event.request
    user = getattr(request, 'user', None)
    csrf = request.params.get('csrf_token')
    if (request.method == 'POST' or request.is_xhr) and \
       (user and user.is_authenticated()) and \
       (csrf != unicode(request.session.get_csrf_token())):
        raise HTTPUnauthorized

撰写回答