Requests: Заголовок авторизации сеанса не отправляется при перенаправлении

Созданный на 28 дек. 2015  ·  35Комментарии  ·  Источник: psf/requests

Я использую запросы на попадание на developer-api.nest.com и устанавливаю заголовок авторизации с токеном-носителем. На некоторые запросы этот API отвечает перенаправлением 307. Когда это произойдет, мне все еще нужно, чтобы заголовок авторизации был отправлен в следующем запросе. Я пробовал использовать requests.get() а также сеанс.

Я полагаю, я мог бы обойти это, не разрешив перенаправления, обнаружив 307, а затем самостоятельно выполнив новый запрос, но мне интересно, является ли это ошибкой. Следует ли ожидать, что заголовок авторизации будет отправляться по всем запросам, сделанным в контексте сеанса?

In [41]: s = requests.Session()

In [42]: s.headers
Out[42]: {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.7.0 CPython/3.4.3 Darwin/15.2.0'}

In [43]: s.headers['Authorization'] = "Bearer <snip>"

In [45]: s.get("https://developer-api.nest.com/devices/thermostats/")
Out[45]: <Response [401]>

In [46]: s.get("https://developer-api.nest.com/devices/thermostats/")
Out[46]: <Response [200]>

In [49]: Out[45].history
Out[49]: [<Response [307]>]

In [50]: Out[46].history
Out[50]: []

In [51]: Out[45].request.headers
Out[51]: {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.7.0 CPython/3.4.3 Darwin/15.2.0'}

In [52]: Out[46].request.headers
Out[52]: {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.7.0 CPython/3.4.3 Darwin/15.2.0', 'Authorization': 'Bearer <snip>'}

Самый полезный комментарий

Есть два обходных пути, специфичных для Nest.

Один из них - передать параметр auth с access_token вместо использования заголовка авторизации. Я нашел это на https://gist.github.com/tylerdave/409ffa08e1d47b1a1e23

Другой - сохранить словарь с заголовками, которые вы бы использовали, не следовать перенаправлениям, а затем сделать второй запрос, снова передав заголовки:

    headers = {'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/json'}
    initial_response = requests.get('https://developer-api.nest.com', headers=headers, allow_redirects=False)
    if initial_response.status_code == 307:
        api_response = requests.get(initial_response.headers['Location'], headers=headers, allow_redirects=False)

Все 35 Комментарий

Куда перенаправление?

Ах, другой домен. firebase-apiserver03-tah01-iad01.dapi.production.nest.com

Да, это несколько преднамеренно: мы очень агрессивно удаляем заголовки авторизации при перенаправлении на новый хост. Это функция безопасности для работы с CVE 2014-1829 , вызванной сохранением заголовков при перенаправлении за пределы хоста.

Однако с определенной точки зрения у нас все еще есть ошибка, потому что вы устанавливаете заголовок авторизации для Session , а не для запроса. В принципе, это означает: «Меня не волнует, куда идет перенаправление, добавьте заголовок». Я все еще _think_, я бы предпочел этот подход, который, по крайней мере, гарантирует, что мы не открыты для любой формы атаки, даже если он делает этот конкретный случай несколько сложнее. Однако я открыт для убеждения, что мы здесь слишком параноики.

Однако я открыт для убеждения, что мы здесь слишком параноики.

Я менее склонен к убеждению, но готов слушать.

Тем не менее, можно написать отдельный механизм Auth для сохранения таких заголовков в _allowed_ доменах, что потребует от нас некоторой работы в rebuild_auth .

Я не буду спорить с безопасностью того, как это работает прямо сейчас. Хотя было бы неплохо иметь какой-то механизм, позволяющий отказаться от «небезопасного» поведения. Возможно, установка базового домена для сохранения этих заголовков на (в данном случае nest.com) или, возможно, списка доменов, на которые можно их отправлять.

Да, это намного сложнее, чем может предоставить ядро ​​запросов. Вот почему мне интересно, может ли отдельный класс / обработчик Auth работать лучше всего для такого рода вещей. Я не уверен, что это сработает, потому что я совершенно уверен, что мы не вызываем prepare_auth безоговорочно.

Это не будет работать в стандартной модели, потому что мы не вызываем безоговорочно prepare_auth . Однако для выполнения этой роли можно использовать транспортный адаптер, даже если это несколько необычное использование этого API.

Я думаю, что ТА - это абсолютно неправильный совет здесь.

  • Если для сеанса предоставляется аутентификация, его следует отправлять для каждого запроса, который делает сеанс.
  • Возможно, нам следует удалить session.auth . Это не особо полезно.

Если для сеанса предоставляется аутентификация, его следует отправлять для каждого запроса, который делает сеанс.

Я принципиально не согласен. Сессии не используются для одного домена, если бы они были, у меня не было бы проблем с этим.

Возможно, нам следует удалить session.auth. Это не особо полезно.

Думаю, это полезно. Я думаю, было бы лучше, если бы нельзя было назначать ему кортеж. Я бы предпочел увидеть класс Auth, который указывает, для каких доменов его использовать. Мы могли бы просто использовать AuthHandler из requests-toolbelt, который позволяет людям указывать учетные данные для домена при использовании запросов. Это обеспечивает немного более безопасный способ обработки аутентификации на основе сеанса. Обратной стороной является то, что для этого требуется, чтобы пользователи соглашались на такую ​​аутентификацию.

Мне также нужно, чтобы это было исправлено, чтобы я мог сохранить заголовки для перенаправления.

@jtherrmann Если это заголовок аутентификации, самый простой способ обойти проблему - установить обработчик аутентификации на уровне сеанса, который просто всегда помещает нужный заголовок в запрос.

Был ли этому уделен какой-либо прогресс или дополнительное рассмотрение?
Я столкнулся с той же проблемой.

@ethanroy Никаких дополнительных соображений, кроме моего предложения об использовании обработчика аутентификации на уровне сеанса, в комментарии непосредственно над вашим.

Связано: если сеанс перенаправляет и удаляет аутентификацию, вызов get снова повторно применяет аутентификацию и использует кешированное перенаправление. Так что постучите дважды, и вы попадете внутрь. Предполагаемое поведение?

>>> s = requests.Session()
>>> s.headers.update({"Authorization": "Token {}".format(API_TOKEN)})
>>> s.get(url)

<Response [403]>

>>> s.get(url)

<Response [200]>

@GregBakker Да, иш. Это совокупность предполагаемого поведения. Однако эта ошибка указывает на то, что исходный 403 не должен происходить.

@Lukasa, когда вы говорите: «

Да, это _должно_ работать.

@jwineinger, так как же вы

Есть два обходных пути, специфичных для Nest.

Один из них - передать параметр auth с access_token вместо использования заголовка авторизации. Я нашел это на https://gist.github.com/tylerdave/409ffa08e1d47b1a1e23

Другой - сохранить словарь с заголовками, которые вы бы использовали, не следовать перенаправлениям, а затем сделать второй запрос, снова передав заголовки:

    headers = {'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/json'}
    initial_response = requests.get('https://developer-api.nest.com', headers=headers, allow_redirects=False)
    if initial_response.status_code == 307:
        api_response = requests.get(initial_response.headers['Location'], headers=headers, allow_redirects=False)

Я столкнулся с той же проблемой и решил ее, переопределив метод rebuild_auth в пользовательской реализации requests.Session :

from requests import Session

class CustomSession(Session):
    def rebuild_auth(self, prepared_request, response):
        return

s = CustomSession()
s.get(url, auth=("username", "password"))

@ sigmavirus24 , что случилось с @ Gabriël-Loo в растворе ? Проблемы безопасности?

@ j08lue да. Пожалуйста, прочтите ветку. Есть CVE ассоциированные с не зачистками аутентификации перед выполнением произвольного редиректа на новый домен. Подумайте о проблеме так:

Я делаю запросы к api.github.com и злоумышленнику удается заставить меня выполнить перенаправление на another-domain.com которое они контролируют, и я передаю свой токен с доступом на запись в мои репозитории (включая запросы), затем он Может показаться, что я совершаю коммиты по запросам, хотя на самом деле они совершают эти коммиты через API. Они могут включать в запросы код, который ослабит его безопасность и, возможно, причинит вам серьезный вред. Вот что может произойти, если вы безоговорочно отправляете свои учетные данные для аутентификации при каждом перенаправлении.

Даже в этом случае, допустим, перенаправление не является вредоносным, действительно ли вам удобно передавать свои учетные данные для службы другой компании или службе? Исходный сервис может хранить конфиденциальные данные для вас, ваших клиентов или чего-то еще. Даже если новый домен, на который вы были перенаправлены, не использует ваши учетные данные, но потенциально регистрирует их как неожиданные данные, кто-то, кто атакует их и может получить эти журналы, может затем использовать ваши учетные данные для исходного домена, если они могут понять, где они принадлежат. Вы действительно готовы пойти на такой риск?

Спасибо за иллюстрацию, @ sigmavirus24. Если эта проблема в конечном итоге запрещает пересылку конфиденциальных заголовков на перенаправления, то почему этот поток все еще открыт? Я не мог придумать более подходящей ошибки, чем та, которую вы получили (403), поэтому здесь нет необходимости в каких-либо действиях, не так ли? Или что ты имел в виду , @Lukasa?

Недавно я столкнулся с этой проблемой при работе с непубличным API. Проблемы безопасности вполне имеют смысл как причина для отключения аутентификации при перенаправлении. Я думаю, что такое решение, как @ gabriel-loo, - это то, что люди могут рассмотреть, если они верят, что находятся в достаточно безопасной среде для этого. Или обработчик уровня сеанса. Или найдите другой способ решения проблемы, полностью пропустив перенаправление, как было предложено выше, если это возможно. Так что, в соответствии с представлением, это на самом деле не ошибка.

Однако я потратил больше времени, чем мне, вероятно, потребовалось, чтобы понять, почему несколько других HTTP-клиентов, отличных от Python, _did_ передают заголовок auth и работают нормально, когда это не так. Одно предложение: было бы неплохо выдать предупреждение с помощью warnings здесь, чтобы было более понятно для вызывающих абонентов, когда заголовок присутствует и удаляется. Я могу предположить, что звонящий редко хочет, чтобы его предупреждали об этом.

@tlantz обычно это кажется разумным. Запросы как проект (а также urllib3, одна из его зависимостей) вызвали значительное возмущение, когда он выдает какие-либо предупреждения, будь то через модуль предупреждений или через ведение журнала. Кроме того, модуль предупреждений предназначен для тех вещей, над которыми люди должны действовать, например, не используя версию Python, которая была скомпилирована для последней версии OpenSSL.

В большинстве случаев такое поведение не столь проблематично, как, например, невозможность проверить сертификат для TLS-соединения. Это явно не поможет вам или кому-либо еще, кто выразил свое искреннее и серьезное разочарование по этому поводу. Имея это в виду, мне интересно, не лучше ли попытаться зарегистрировать это на уровне DEBUG . Если кто-то использует ведение журнала (как правило, достойная практика) и включает этот уровень, он отображается для них. В дальнейшем, учитывая, как мало регистрируется сам запрос, он будет довольно заметным как журнал отладки. Кажется ли это справедливым компромиссом?

Да, это кажется вполне справедливым компромиссом. Рассуждения вокруг warnings имеют для меня смысл. Я думаю, что к тому времени, когда вы были озадачены в течение 30 минут или около того, вы все равно добавляете logging вокруг своих вещей в DEBUG , поэтому я думаю, что сообщение DEBUG попадет в 95% случаев, когда люди застревают, пытаясь выяснить, что не работает.

Я использую сеанс для хранения заголовка авторизации, но он не отправляется при перенаправлении
запросы (2.18.4)

Мы с коллегой потратили как минимум пару часов на отладку проблемы, непосредственно связанной с таким поведением. Мой вариант использования - перенаправление вызова API с api.my-example-site.org на www.api.my-example-site.org . Заголовки удалялись при перенаправлении.

Если это предполагаемое поведение (или если оно не будет изменено в ближайшем будущем), можем ли мы хотя бы добавить его в документацию? Я читал и перечитывал документы, пытаясь понять, что я делал неправильно, и даже прочитал весь код в классе Request . Если бы я увидел предупреждение об этом поведении в документации, я бы исправил свою проблему за пару минут (это время, которое потребовалось после того, как я нашел эту ветку). Однако, возможно, мы читали не ту часть документации.

Привет, @ndmeiri , у нас есть кратком руководстве по запросам под заголовком Custom Headers . Если вы считаете, что есть место для этого лучше, мы будем рады рассмотреть любые ваши предложения. Я бы предпочел вынести это в отдельный вопрос или в PR, поскольку это не имеет прямого отношения к этому заявлению. Благодаря!

Привет, @nateprewitt , спасибо, что указали на раздел Custom Headers! Очевидно, я не подумал проверить эту часть документации.

Я думаю, что было бы полезно также включить вызов или ссылку на вызов в раздел аутентификации . Хотя в настоящее время я довольно занят, я подумаю об открытии PR, когда все уляжется, чтобы обновить документы.

Если это предполагаемое поведение

@ndmeiri Да, это действие предназначено для

Похоже, что «доверенные домены» из # 4983 больше не входят в реализацию sessions.py.

В ситуации, когда я делаю запросы к URL-адресу, который я _ знаю_ перенаправляет на конкретный, но безопасный URL-адрес, и я хочу включить перенаправление с сохранением заголовка авторизации, как мне этого добиться?

как мне этого добиться, пожалуйста?

Вы можете пропатчить метод rebuild_auth . Это работает для меня: https://github.com/DHI-GRAS/earthdata-download/blob/master/earthdata_download/download.py#L27 -L49

@ j08lue Спасибо! До того, как появился ваш комментарий, я решил проблему, установив для allow_redirects значение False и добавив код для явного следования нескольким конкретным перенаправлениям, которые ожидаются в моем случае использования. Для меня это краткосрочная ситуация, поэтому я надеюсь, что это адекватное временное решение, но приятно знать, что есть лучший способ сделать это в более долгосрочной перспективе, если это необходимо.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

remram44 picture remram44  ·  4Комментарии

NoahCardoza picture NoahCardoza  ·  4Комментарии

thadeusb picture thadeusb  ·  3Комментарии

ghtyrant picture ghtyrant  ·  3Комментарии

jake491 picture jake491  ·  3Комментарии