Requests-Racer 是一个小型 Python 库,可让您使用Requests 库提交多个请求,这些请求将由其目标服务器在大约相同的时间进行处理,即使这些请求具有不同的目标或具有不同大小的有效负载也是如此。这对于检测和利用 Web 应用程序中的竞争条件漏洞非常有用。(有关更多信息,请参阅motivation.md
。)
免责声明
Requests(以及 Requests 内部使用的 urllib3)从未打算允许您执行此类操作。因此,Requests-Racer 被迫诉诸一些相当糟糕的黑客手段来对请求的提交方式进行细粒度控制。
这些黑客行为包括干扰某些 urllib3 对象的私有状态,因此与其公共 API 向后兼容的 urllib3 更新仍可能破坏 Requests-Racer。因此,我建议在虚拟环境中使用 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 的工作原理是为请求提供一个名为的替代传输适配器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
,它只是一个自动为 HTTP[S]requests.Session
挂载并代理方法的对象,因此上面的代码可以重写如下: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
不会更新会话对象(例如,来自标头的 cookie 将不会添加到会话的 cookie jar 中)。Set-Cookie
- 重定向可能不会被遵循,请尝试避免它们。
- 如果在启动请求时发生异常,则会像常规请求一样重新引发该异常。但是,如果在完成请求时发生异常,则不会重新引发该异常。相反,对请求的响应将具有状态代码
999
并在其属性中包含回溯.text
。 SynchronizedSession.from_regular_session
SynchronizedSession
从实例构造一个requests.Session
。如果您需要发出一些简单的请求(例如登录服务并获取同步请求所需的会话 cookie),这将非常有用。SynchronizedAdapter
并SynchronizedSession
接受一个名为的可选参数num_threads
,该参数指定适配器将使用的最大线程数。如果未指定,适配器将为每个请求使用一个线程。finish_all()
接受一个名为的可选参数timeout
,它给出适配器等待线程完成的最长时间(以秒为单位)。
请参阅benchmark/
有关性能的说明。