Неверная настройка защиты CSRF Flask в заголовках ответов - PullRequest
3 голосов
/ 08 мая 2019

Я создаю каталог продуктов, используя Flask и Firebase Authentication. Я слежу за их документами о том, как настроить клиент и сервер, используя их предварительно созданный пользовательский интерфейс и сессионные куки, как указано здесь: https://firebase.google.com/docs/auth/admin/manage-cookies

GET-запросы работают нормально, сервер проверяет сессионный cookie-файл по каждому запросу и отправляет содержимое соответствующим образом. Но когда я выполняю запрос POST (например, отправляю форму для создания нового элемента), сервер не может проанализировать файл cookie.

Я проверил с помощью Chrome Dev Tools, что cookie-файлы сеанса, отправленные на сервер как для запросов GET, так и для POST, одинаковы. Пробовал несколько вещей, которые я нашел, прибегая к помощи подобных проблем, но все работало. Я также пытался найти подобный вопрос здесь, но я не нашел ни одного.

РЕДАКТИРОВАТЬ: После нескольких часов, снова глядя на проблему, я увидел, что куки не совпадают в запросах GET и POST. Я просмотрел запросы с помощью Chrome Dev Tools и увидел, что ответ GET возвращает заголовок Set-Cookie с недопустимым cookie-файлом (что заставляет запрос POST иметь недопустимый cookie-файл и снова перенаправлять на страницу входа) ,

Это происходит только на страницах, где требуется вход в систему (и перенаправляет на страницу входа, если вы не вошли в систему), но все же я не могу понять, почему Flask отправляет заголовок Set-Cookie с недопустимым файлом cookie.

РЕДАКТИРОВАТЬ2: Через несколько часов я обнаружил, что удаление скрытого ввода 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» (возвращает GET-запросы заголовок Set-Cookie с недопустимым cookie):

@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 голосов
/ 23 мая 2019

Чтобы решить эту проблему, я изменил имя файла cookie с «сеанса» на любое другое имя.Я не знаю, почему Flask продолжает отправлять заголовки Set-Cookie для файла cookie сеанса, поэтому, если кто-нибудь знает, почему, закомментируйте его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...