Flask CSRF Protection设置响应标头上的无效Set-Cookie

时间:2019-05-08 16:20:28

标签: python firebase flask firebase-authentication

我正在使用Flask和Firebase身份验证创建产品目录。我正在关注他们的文档,其中介绍了如何使用其预先构建的UI和会话Cookie来设置客户端和服务器,如此处所述:https://firebase.google.com/docs/auth/admin/manage-cookies

<罢工> GET请求工作正常,服务器验证每个请求的会话cookie并相应地发送内容。但是当我执行POST请求(例如,提交表单以创建新项目)时,服务器无法解析Cookie。

我使用Chrome开发工具验证了在GET和POST请求上发送到服务器的会话Cookie都是相同的。尝试了几项我发现类似问题的谷歌搜索,但一切正常。我也试图在这里找到类似的问题,但没有找到任何问题。

编辑:几个小时后再次查看问题,我发现GET和POST请求上的cookie并不相同。我已经使用Chrome开发工具查看了请求,并且看到GET响应返回带有无效cookie的Set-Cookie标头(这使得POST请求具有无效cookie并再次重定向到登录页面) 。

这只会在需要登录的页面上发生(如果您未登录,则会重定向到登录页面),但是我仍然找不到为什么Flask发送带有无效Cookie的Set-Cookie标头的原因。

EDIT2:几个小时后,我发现从那些页面上的表单中删除CSRF隐藏输入可解决Cookie问题(GET请求不会生成Set-Cookie),因此必须与CSRF有关,但我不知道如何。我没有考虑使用CSRF时会话cookie有什么特殊行为?

“新商品”模板:

{% extends "layout.html" %}

{% block title %}
    New item
{% endblock %}

{% block head %}
    {{ super() }}
    <link rel="stylesheet" type="text/css" media="screen" href="{{ url_for('static', filename='form.css') }}">
{% endblock %}

{% block content %}
<form action="{{ url_for('newItem') }}" method = 'post'>
    <h1>Create a new item</h1>
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
    <label>Name</label>
    <input type='text' size='30' name='name' placeholder="Name" required>
    <label>Description</label>
    <textarea rows='4' name='description' placeholder="Description" required></textarea>
    <label>Price</label>
    <input type='text' size='30' name='price' placeholder="Price" required>
    <label>Image URI</label>
    <input type='text' size='30' name='image' placeholder="https://example.com/image.png" required>
    <label>Category</label>
    <select name='category' required>
        {% for c in categories %}
        <option value="{{ c.id }}">{{ c.name }}</option>
        {% endfor %}
    </select>
    <input type='submit' value='Create'>
    <a href="{{ url_for('categories') }}">Cancel</a>
</form>
{% endblock %}

“需要登录”装饰器:

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        session_cookie = request.cookies.get('session')
        # Verify the session cookie. In this case an additional check is added to detect
        # if the user's Firebase session was revoked, user deleted/disabled, etc.
        try:
            decoded_claims = auth.verify_session_cookie(session_cookie, check_revoked=True)
            return f(*args, **kwargs)
        except ValueError as e:
            # Session cookie is unavailable or invalid. Force user to login.
            print(e)
            return redirect(url_for('login', mode="select", signInSuccessUrl=request.url))
        except auth.AuthError as e:
            # Session revoked. Force user to login.
            print(e)
            return redirect(url_for('login', mode="select", signInSuccessUrl=request.url))
    return decorated_function

“项目”端点(按预期工作):

@app.route('/items/<int:item_id>/')
def item(item_id):
    session = DBSession()
    item = session.query(Item).get(item_id)

    session_cookie = flask.request.cookies.get('session')
    # Verify the session cookie. In this case an additional check is added to detect
    # if the user's Firebase session was revoked, user deleted/disabled, etc.
    try:
        auth.verify_session_cookie(session_cookie, check_revoked=True)
        return render_template('item.html', item=item, logged=True)
    except ValueError as e:
        # Session cookie is unavailable or invalid. Force user to login.
        print(e)
        return render_template('item.html', item=item, logged=False)
    except auth.AuthError as e:
        # Session revoked. Force user to login.
        print(e)
        return render_template('item.html', item=item, logged=False)

“ New Item”端点(将带有无效cookie的Set-Cookie标头返回到GET请求):

@app.route('/items/new/', methods=['GET', 'POST'])
@login_required
def newItem():
    session_cookie = flask.request.cookies.get('session')
    decoded_claims = auth.verify_session_cookie(session_cookie, check_revoked=True)

    session = DBSession()
    categories = session.query(Category).all()
    if request.method == 'GET':
        return render_template('new_item.html', categories=categories, logged=True)
    else:
        # SOME LOGIC HERE
        # [...]
        return redirect(url_for('item', item_id = newItem.id))

我在POST请求中收到的错误如下:

Value Error
Can't parse segment: \���

1 个答案:

答案 0 :(得分:0)

为解决此问题,我将cookie名称从'session'更改为任何其他名称。我不知道为什么Flask会继续为“会话” Cookie发送Set-Cookie标头,所以如果有人知道为什么,请注释掉它。