Requests-Racer 是一个小型 Python 库,它允许你使用 Requests 库 提交多个请求,这些请求将由目标服务器几乎同时处理,即使这些请求具有不同的目标地址或不同大小的负载。这对于检测和利用 Web 应用程序中的竞态条件漏洞非常有用。(更多信息请参阅 motivation.md。)
Requests(以及 Requests 内部使用的 urllib3)从未打算允许你做类似这样的事。因此,Requests-Racer 不得不借助一些相当糟糕的 hack 来获得对请求提交方式的细粒度控制。
这些 hack 包括篡改某些 urllib3 对象的私有状态,因此,即使 urllib3 的更新在其公共 API 方面向后兼容,也仍然可能破坏 Requests-Racer。因此,我建议在 virtualenv 中使用 Requests-Racer,并先于 Requests 或 urllib3 安装它,以便将这些库的已知兼容版本作为依赖项拉取。
要使用 Requests-Racer,你需要 Python 3.5 或更高版本。首先,创建并激活一个 Python 虚拟环境:
python3 -m venv env
source env/bin/activate
然后下载该库的副本并安装:
git clone https://github.com/nccgroup/requests-racer.git
cd requests-racer
python setup.py install
Requests-Racer 的原理是为 Requests 提供一个名为 SynchronizedAdapter 的替代 传输适配器。它会收集你通过它发出的所有请求,并且仅当调用 finish_all() 方法时才完成这些请求:
import requests
from requests_racer import SynchronizedAdapter
s = requests.Session()
sync = SynchronizedAdapter()
s.mount('http://', sync)
s.mount('https://', sync)
resp1 = s.get('http://example.com/a', params={'hello': 'world'})
resp2 = s.post('https://example.net/b', data={'one': 'two'})
# at this point, the requests have been started but not finished.
# resp1 and resp2 should *not* be used.
sync.finish_all()
print(resp1.status_code)
print(resp2.text)
为了让代码更简洁,你还可以使用 SynchronizedSession,它只是一个 requests.Session 对象,会自动为 HTTP[S] 挂载一个 SynchronizedAdapter,并代理 finish_all() 方法,因此上面的代码可以改写为:
from requests_racer import SynchronizedSession
s = SynchronizedSession()
resp1 = s.get('http://example.com/a', params={'hello': 'world'})
resp2 = s.post('https://example.net/b', data={'one': 'two'})
# at this point, the requests have been started but not finished.
# resp1 and resp2 should *not* be used.
s.finish_all()
print(resp1.status_code)
print(resp2.text)
这里有一些需要注意的注意事项,以及一些更高级的用法:
SynchronizedAdapter 不是线程安全的。SynchronizedAdapter 发出的请求不会更新会话对象(例如,来自 Set-Cookie 头的 cookie 不会添加到会话的 cookie jar 中)。999,并且其 .text 属性中包含一个回溯(traceback)。SynchronizedSession.from_regular_session 从一个 requests.Session 实例构造一个 SynchronizedSession。如果你需要做一些简单的请求(例如登录服务并获取同步请求所需的会话 cookie),这会很有帮助。SynchronizedAdapter 和 SynchronizedSession 接受一个名为 num_threads 的可选参数,它指定适配器将使用的最大线程数。如果未指定,适配器将为每个请求使用一个线程。finish_all() 接受一个名为 timeout 的可选参数,它指定适配器等待线程完成的最长时间(以秒为单位)。有关性能的说明,请参阅 benchmark/。
Copyright (C) 2019 Aleksejs Popovs, NCC Group
本程序是自由软件:你可以根据自由软件基金会发布的 GNU 通用公共许可证的条款,即许可证的第 3 版,或(由你选择)任何更高版本,重新分发和/或修改它。
本程序的分发希望能有用,但不提供任何担保;甚至没有适销性或特定用途适用性的隐含担保。请参阅 GNU 通用公共许可证了解更多细节。
你应该已经收到一份 GNU 通用公共许可证的副本,与本程序一起。如果没有,请参阅 https://www.gnu.org/licenses/。