Ошибки CSRF при попытке войти в Django с помощью запросов/сессий Python

Я пытаюсь войти в Django, используя запросы Python. Есть старые вопросы об этом, но ни на один из них нет ответа из-за недостаточной детализации.

Здесь задействованы два CSRF-токена. Один - это cookie, а другой - часть формы входа. Я проверил, что Chrome возвращает маркер куки в куки, а маркер формы - в теле, и мое тело с кодировкой url соответствует формату, который браузер передает обратно.

И все же в ответ я получаю "Forbidden (CSRF token missing or incorrect.)". Может ли кто-нибудь подсказать, что я делаю неправильно?

Мой Python выглядит следующим образом:

def login(url, username, password):
    session = requests.Session()
    response = session.get(f"{url}/accounts/login/")
    print(f"First headers: {response.headers}")
    header_csrftoken = session.cookies['csrftoken']
    print(f"Header csrftoken: {header_csrftoken}")

    soup = BeautifulSoup(response.text, "html.parser")
    form = soup.find("form", {"id":"login-form"})
    found = form.find("input", {"name":"csrfmiddlewaretoken"})
    form_csrftoken = found['value']
    print(f"csrftoken2: {form_csrftoken}")
    data = {'csrfmiddlewaretoken': form_csrftoken, 'username': username, 'password': password, 'next': '/'}

    response2 = session.post(f"{url}/accounts/login/", data=data, headers={'content-type': 'x-www-form-urlencoded'})
    print(f"outgoing: {response2.request.body}")
    print(f"Headers: {response2.request.headers}")


First headers: {'Date': 'Mon, 20 Sep 2021 03:37:17 GMT', 'Server': 'WSGIServer/0.2 CPython/3.8.12', 'Content-Type': 'text/html; charset=utf-8', 'Expires': 'Mon, 20 Sep 2021 03:37:17 GMT', 'Cache-Control': 'max-age=0, no-cache, no-store, must-revalidate, private', 'Vary': 'Cookie, Origin', 'X-Frame-Options': 'DENY', 'Content-Length': '20609', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'same-origin', 'Server-Timing': 'TimerPanel_utime;dur=16.402999999996837;desc="User CPU time", TimerPanel_stime;dur=3.0629999999973734;desc="System CPU time", TimerPanel_total;dur=19.46599999999421;desc="Total CPU time", TimerPanel_total_time;dur=21.546363830566406;desc="Elapsed time", SQLPanel_sql_time;dur=0;desc="SQL 0 queries", CachePanel_total_time;dur=0;desc="Cache 0 Calls"', 'Set-Cookie': 'csrftoken=FG9ronCxQUcIvT8iY79wF90CZjBKWFEhsB2sxVPYx4YQUZ5way1cHrazU46xk6Ig; expires=Mon, 19 Sep 2022 03:37:17 GMT; Max-Age=31449600; Path=/; SameSite=Lax'}
Header csrftoken: FG9ronCxQUcIvT8iY79wF90CZjBKWFEhsB2sxVPYx4YQUZ5way1cHrazU46xk6Ig
form_csrftoken: 8a0D501NMPJXmcCJiwt7BiFhTyrzJhQLV5TEeyeetZv5LizXuXlNDAPeOjWm7IUK
outgoing: csrfmiddlewaretoken=8a0D501NMPJXmcCJiwt7BiFhTyrzJhQLV5TEeyeetZv5LizXuXlNDAPeOjWm7IUK&username=myname&password=naistrai&next=%2F
Outgoing Headers: {'User-Agent': 'python-requests/2.25.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'content-type': 'x-www-form-urlencoded', 'Cookie': 'csrftoken=FG9ronCxQUcIvT8iY79wF90CZjBKWFEhsB2sxVPYx4YQUZ5way1cHrazU46xk6Ig', 'Content-Length': '141'}

Я не знаю, задумано это или нет, но при передаче POST-запроса он проходит через django/middleware/csrf.py:CsrfViewMiddleware.process_view(). Когда он достигает этой функции, POST-данные в теле запроса недоступны через request.POST.get('csrfmiddlewaretoken'), поэтому эта функция просто возвращает пустую строку.

Вероятно, это сделано потому, что в данном случае токен csrf в теле неправильный - это токен csrf формы, а не токен csrf заголовка - и эта функция хочет проверить его на соответствие токену заголовка.

Что мне пришлось сделать, чтобы исправить это, так это добавить 'X-CSRFToken': header_csrftoken в заголовки POST.

Вернуться на верх