Summary
mechanize leaked credentials to the redirect target when an HTTP redirect crossed to another host. Credentials set through Mechanize#requestheaders= leaked even when they were Authorization.
Details
Two defects, both in lib/mechanize/http/agent.rb.
1. Mechanize#requestheaders= bypassed the redirect strip entirely. #requestaddheaders copied @requestheaders onto every request unconditionally, with no host check, including the request issued after a redirect. The strip in #responseredirect mutated only the per-request headers hash and never touched agent state. Because requestheaders= is the documented way to set a default credential for every request, the header the code explicitly protected — Authorization — was the one most likely to leak.
2. The strip list omitted Proxy-Authorization and Cookie2. Only CREDENTIALHEADERS = ['Authorization'] and COOKIEHEADERS = ['Cookie'] were removed from the per-request headers hash on a cross-host redirect.
Cookies held in Mechanize#cookiejar and credentials held in Mechanize::HTTP::AuthStore are not affected. Both are looked up per-URI, so they never follow a redirect to a foreign host. The exposure was limited to headers the caller set by hand.
PoC
ruby agent = Mechanize.new agent.requestheaders = { 'Authorization' => 'Bearer secret' } agent.get('https://example.test/redirects-to-attacker') the request to the attacker's host carries "Authorization: Bearer secret"
Impact
An attacker who controls a redirect target — through an open redirect on the site being fetched, an attacker-supplied fetch URL, DNS rebinding, or MITM — captures bearer tokens and session cookies from any mechanize agent that sets credentials through requestheaders= or the per-request headers argument. Disclosure only; no integrity or availability impact.
Patches
Fixed in mechanize v2.14.1.
- Credentials and cookies are withheld from a request that follows a redirect across an origin, from both header sources: the per-request headers argument and Mechanize#requestheaders=. - CREDENTIALHEADERS gains Proxy-Authorization; COOKIEHEADERS gains Cookie2.
Proxy-Authorization is withheld here although curl does not withhold it, because Net::HTTP tunnels https: requests with CONNECT, so a caller-supplied Proxy-Authorization travels inside the tunnel to the origin server rather than to the proxy.
Headers set through Mechanize#requestheaders= no longer reach a redirect target on another origin when they are credentials. Callers that relied on the previous behavior will see those headers withheld.
What this fix does not cover
Only the headers in CREDENTIALHEADERS and COOKIEHEADERS are withheld. A caller-supplied header that carries a credential under some other name — X-API-Key, X-Vault-Token, or any bespoke token header — still follows a redirect to another origin, matching the behavior of curl's CURLOPTHTTPHEADER. If you set such a header, do not enable redirect following for requests that carry it, or scope it to a single request whose destination you control.
Workarounds
Set Mechanize#redirectok = false and handle redirects explicitly, or avoid requestheaders= for credentials and pass them per-request only to hosts you intend to authenticate to.
Credit
Reported by @SnailSploit.
The Mechanize library is used for automating interaction with websites. Prior to 2.14.1, Mechanize::HTTP::Agent#responseredirect treats redirects as same-origin when the host matches without consistently comparing scheme and port. A same-host HTTPS-to-HTTP redirect can send Authorization and Cookie headers over cleartext, while a same-host redirect to another port can send a caller-supplied Cookie header to a different service. Cookies in Mechanize#cookiejar remain scoped separately; the issue affects caller-supplied headers and can disclose credentials without affecting integrity or availability. This issue is fixed in version 2.14.1.