Requests: 会话的授权标头未在重定向时发送

创建于 2015-12-28  ·  35评论  ·  资料来源: psf/requests

我正在使用请求来访问developer-api.nest.com,并设置带有承载令牌的Authorization标头。 在某些请求下,该API会响应307重定向。 发生这种情况时,我仍然需要在后续请求中发送Authorization标头。 我尝试使用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的解决方法。

一种是通过access_token传递auth参数,而不是使用Authorization标头。 我在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-i​​ad01.dapi.production.nest.com

是的,这有点故意:我们在重定向到新主机时剥离授权标头方面非常积极。 这是应对CVE 2014-1829的一项安全功能,该功能是由我们在脱机重定向中持久保留标头引起的。

但是,从某种角度来看,这里仍然存在一个错误,因为您在Session而非请求上设置了Authorization标头。 原则上,这意味着“我不在乎重定向的位置,添加标题”。 我仍然_想_有这种方法,该方法至少可以确保我们不会受到任何形式的攻击,即使这使此特定实例变得有些棘手。 但是,我很愿意相信我们在这里过于偏执。

但是,我很愿意相信我们在这里过于偏执。

我不太相信别人,但愿意听。

也就是说,可以编写一个单独的Auth机制来在_allowed_域中持久保留此类标头,这将使我们有必要在rebuild_auth进行一些工作。

我不会就它现在如何工作的安全性争论。 不过,最好有某种机制选择加入“不安全”行为。 也许设置一个基本域以将这些标头持久保存到(在这种情况下,为nest.com),或者可以确定要将其发送到的域的列表。

是的,这比请求的核心要复杂得多。 这就是为什么我想知道一个单独的Auth类/处理程序是否最适合这种情况。 我不确定它是否会奏效,因为我相当确定我们不会无条件调用prepare_auth

它在标准模型中不起作用,因为我们不会无条件调用prepare_auth 。 但是,可以使用传输适配器来完成此任务,即使该API的使用略有不同也是如此。

我认为,电讯局长绝对是错误的建议。

  • 如果将auth提供给会话,则应为该会话发出的每个请求发送auth。
  • 也许我们应该删除session.auth 。 它不是特别有用。

如果将auth提供给会话,则应为该会话发出的每个请求发送auth。

我从根本上不同意。 会话不用于单个域,如果有的话,我对此不会有任何问题。

也许我们应该删除session.auth。 它不是特别有用。

我认为这很有用。 我认为,如果不允许分配元组会更好。 我宁愿看到一个Auth类,该类指定要对其使用的域。 我们可以只采用了AuthHandler从请求,工具区,允许使用请求时,人们为一个域指定凭据。 这为处理基于会话的身份验证提供了一种更为安全的方法。 缺点是,它要求用户选择加入这种身份验证。

我还需要修复此问题,以便可以使标头保持重定向。

@jtherrmann如果这是auth标头,则解决此问题的最简单方法是设置一个会话级auth处理程序,该处理程序始终将所需的标头放在请求中。

是否对此有任何进展或其他考虑?
我遇到了同样的问题。

@ethanroy在您的正上方的注释中,除了我建议使用会话级身份验证处理程序之外,没有其他考虑。

相关:如果会话重定向并剥离身份验证,则再次调用get重新应用身份验证并使用缓存的重定向。 所以敲两次就可以了。预期的行为?

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

<Response [403]>

>>> s.get(url)

<Response [200]>

@GregBakker是的,ish。 这是预期行为的融合。 但是,此错误指出原始403不应该发生。

@Lukasa当您说“解决此问题的最简单方法是设置会话级身份处理程序”时,今天是否可以使用? 根据我在代码中看到的内容,答案是否定的,但您的措辞使我怀疑我是否缺少某些内容。 您正在谈论设置会话auth属性,对吗?

是的,应该工作。

@jwineinger,所以您最终如何解决这个问题? 它似乎仍然表现相同。

有两种针对Nest的解决方法。

一种是通过access_token传递auth参数,而不是使用Authorization标头。 我在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)

我遇到了同样的问题,并通过在自定义requests.Session实现中重写rebuild_auth方法来解决此问题:

from requests import Session

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

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

@ sigmavirus24 @ gabriel-loo的解决方案出了什么问题? 安全问题?

@ j08lue是的。 请阅读主题。 在将任意重定向到新域之前,有CVE与剥离身份验证相关联。 这样考虑问题:

我向api.github.com发出请求,攻击者设法使我跟随他们控制的another-domain.com重定向,并随同我的令牌一起传递了对我的存储库(包括请求)的写访问权,然后看起来好像我正在对请求进行提交,而实际上它们是通过API进行的。 它们可以在请求中包含代码,这会削弱其安全性,并可能对您造成积极伤害。 当您无条件发送每次重定向的身份验证凭据时,可能会发生这种情况。

即使这样,可以说重定向不是恶意的,您实际上是否愿意将服务的凭据泄漏给另一家公司或服务? 原始服务可能会为您,您的客户或其他东西存储机密数据。 即使您已重定向到的新域不使用您的凭据,但有可能将它们记录为意外数据,但是攻击它们并可以检索这些日志的人也可以对原始域使用您的凭据,前提是他们可以弄清楚在哪里他们属于。 您真的愿意冒险吗?

感谢您的说明,@ sigmavirus24。 如果此问题最终禁止转发敏感标头以进行重定向,那么为什么该线程仍然打开? 我没有想到比您得到的错误(403)更合适的错误,因此这里没有需要采取任何措施的错误,是吗? 或@Lukasa,您在想什么?

我最近在使用非公开API时遇到了这个问题。 出于安全考虑,将重定向上的身份验证剥离出去的原因是有道理的。 我认为,如果人们认为自己处于足够安全的环境中,可以考虑使用类似@ gabriel-loo的解决方案。 或会话级别的处理程序。 或者,如果可能的话,也可以按照上述建议完全跳过重定向,找到其他解决方法。 因此,与视图一致,这并不是一个错误。

但是,我花了比我可能要困惑的时间更多的时间来思考为什么少数其他非Python HTTP客户端_did_传递auth标头,并且在不这样做时可以正常工作。 一个建议:在此处通过warnings发出警告可能更好,以使调用者在标头存在和被剥离时更清楚。 我以为很少有人会对此发出警告,而不会被警告。

@tlantz通常看起来很合理。 作为项目的请求(以及urllib3,它的依赖项之一)在通过警告模块或日志记录发出任何类型的警告时引起了极大的愤怒。 此外,警告模块用于人们应该采取行动的事情,例如,不要使用针对最新版本的OpenSSL编译的Python版本。

在大多数情况下,此行为不会像例如无法验证TLS连接的证书那样有问题。 显然,这对您或对这个问题表示真正和有效的挫败感的任何人都无济于事。 考虑到这一点,我想知道尝试以DEBUG级别进行记录是否更好。 如果有人正在使用日志记录(通常是一种体面的做法)并启用该级别,则会为他们显示。 此外,考虑到“请求”本身的日志很少,这将作为调试日志相当突出。 这似乎是一个公平的权衡吗?

是的,这似乎是一个完全公平的权衡。 在warnings周围进行推理对我来说很有意义。 我认为,到您困惑了30分钟左右时,您通常会在logging周围为自己的东西添加DEBUG ,所以我认为一条DEBUG消息在人们试图弄清楚什么不起作用的情况下,这种情况会达到95%。

我使用一个会话来保存Authorization标头,但未在重定向中发送该标头
请求(2.18.4)

我和一个同事花了至少几个小时来调试与该行为直接相关的问题。 我的用例是将API调用从api.my-example-site.org重定向到www.api.my-example-site.org 。 标头在重定向中被剥离。

如果这是预期的行为(或者在不久的将来不会更改),我们是否可以至少将其添加到文档中? 我阅读并重新阅读了文档,试图找出我做错了什么,甚至阅读了Request类中的所有代码。 如果我在文档中看到有关此行为的警告,那么我会在几分钟之内解决我的问题(这是我发现此线程后所花费的时间)。 但是,也许我们在阅读文档的错误部分。

@ndmeiri ,您好,我们确实在“自定义标题”标题下的“请求快速入门”指南中对此进行了标注。 如果您觉得有个更好的选择,我们很乐意审查您的任何建议。 我希望我们将其移至单独的问题或PR,因为它与该票证没有直接关系。 谢谢!

@nateprewitt ,您好,感谢您指出“自定义标题”部分! 显然,我没有想到要检查文档的那部分。

我认为将callout或对callout的引用也包含在Authentication部分中会很有帮助。 尽管我目前很忙,但在情况稳定下来以更新文档时,我会考虑打开PR。

如果这是预期的行为

@ndmeiri是的,这是为了使您的敏感身份验证凭据不泄露给可能不受信任的来源而已。 (只是要清楚)

似乎#4983中的“受信任域”不再在session.py的实现中。

在我向_know_重定向到特定但又安全的URL的URL发出请求的情况下,我想在Authorization标头的持久性下启用重定向,请问该如何实现?

我将如何实现呢?

您可以修补rebuild_auth方法。 这对我有用: https :

@ j08lue谢谢! 在您发表评论之前,我通过将allow_redirectsFalse来解决该问题,并添加了代码以显式遵循我的用例中预期的一些特定重定向。 这对我来说是一个短期的情况,所以我希望这是一个适当的临时解决方案,但是很高兴知道,如果需要,从长远来看,还有更好的方法。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

xsren picture xsren  ·  3评论

mitar picture mitar  ·  4评论

cnicodeme picture cnicodeme  ·  3评论

eromoe picture eromoe  ·  3评论

avinassh picture avinassh  ·  4评论