Summary
OAuth: Cross-origin token-request redirects can expose signed request metadata
Cross-origin OAuth token-request redirects can expose signed request metadata
When an application uses OAuth::Consumer to request OAuth 1.0 request tokens or
access tokens, the token request helper follows 300..399 redirects returned by
the OAuth server. In affected versions, OAuth::Consumer#token_request parses the
raw Location header, follows the redirect recursively, and can mutate the
consumer's configured site when the redirect points to a different host with
the same path.
The result is a cross-origin signed-request disclosure primitive: if an OAuth
server token endpoint returns a redirect whose target an attacker controls, the
client can re-sign the token request and send OAuth 1.0 request metadata,
including the OAuth signature, nonce, timestamp, consumer key, and any request
parameters included in the signature base string, to the attacker-controlled
host. The same behavior can also be used as an SSRF or confused-deputy primitive
because the application server follows the redirect and sends the next request
from its own network position.
Affected
oauthv1.1.5 and prior versions back to and including v0.5.5.- The cross-host token redirect behavior was introduced by
https://github.com/ruby-oauth/oauth/commit/d74b767f - The behavior is documented in the v0.5.5 changelog as "Allow redirect to
different host but same path".
- The cross-host token redirect behavior was introduced by
- The vulnerable behavior is in
OAuth::Consumer#token_request, which is used by
the documented request-token and access-token flows. - The issue is not specific to a Ruby engine or platform. It is caused by the
gem's redirect handling and recursive token request behavior.
Patched version: oauth v1.1.6.
Vulnerable code
lib/oauth/consumer.rb
at tag v1.1.5:
def token_request(http_method, path, token = nil, request_options = {}, *arguments)
request_options[:token_request] ||= true
response = request(http_method, path, token, request_options, *arguments)
case response.code.to_i
when (200..299)
# parse token response
when (300..399)
# Parse redirect to follow
uri = URI.parse(response["location"])
our_uri = URI.parse(site)
# Guard against infinite redirects
response.error! if uri.path == path && our_uri.host == uri.host
if uri.path == path && our_uri.host != uri.host
options[:site] = "#{uri.scheme}://#{uri.host}"
@http = create_http
end
token_request(http_method, uri.path, token, request_options, arguments)
when (400..499)
raise OAuth::Unauthorized, response
else
response.error!
end
end
The vulnerable behavior has several parts:
response["location"]is trusted as the next token request target.- Redirects are followed for every
300..399response. - There is no general redirect counter or maximum redirect limit.
- Cross-host redirects with the same path can mutate
options[:site]and rebuild
the underlying HTTP client. - The recursive call continues the token request flow and signs the next request
for the redirected destination.
Reachable in production
The vulnerable path is reachable through the normal OAuth 1 token exchange:
consumer = OAuth::Consumer.new(
consumer_key,
consumer_secret,
site: "https://provider.example"
)
request_token = consumer.get_request_token
If https://provider.example/oauth/request_token returns a redirect to an
attacker-controlled host, the library follows that redirect as part of the token
request flow. A realistic trigger is an OAuth provider, gateway, or reverse proxy
that emits Location based on user-controlled or tenant-controlled input, or a
malicious tenant-controlled OAuth endpoint in a multi-tenant integration.
No application-level redirect handling is required. The redirect is followed
inside the gem before the application receives the token response.
Reproduction
A vulnerable application is one that uses OAuth::Consumer to perform an OAuth
1 token exchange against a token endpoint that can be made to return a redirect
to another origin.
Example shape:
consumer = OAuth::Consumer.new(
consumer_key,
consumer_secret,
site: "https://provider.example"
)
consumer.get_request_token
If https://provider.example/oauth/request_token responds with a 30x redirect
whose Location points to https://attacker.example/..., affected versions may
follow that redirect as part of the token request flow. The redirected request is
then generated and signed by the application server for the new destination.
Depending on the configured OAuth request scheme, OAuth 1 parameters may be sent
in the Authorization header, request body, or query string.
Expected vulnerable behavior:
- the token request leaves the configured OAuth provider origin;
- the redirected host receives a signed OAuth 1 token request;
- the application does not get a chance to inspect or approve the redirect target
before the redirected request is sent.
Expected patched behavior:
- same-origin redirects continue to work, subject to a redirect limit;
- cross-origin token endpoint redirects are rejected by default;
- applications that intentionally require cross-origin token redirects must opt
in explicitly withtoken_request_cross_origin_redirects.
Workarounds
Until a patched release is available, applications can reduce exposure by doing
one or more of the following:
- Ensure configured OAuth token endpoints are fixed absolute URLs controlled by a
trusted provider. - Do not use tenant-controlled OAuth token endpoint URLs unless the tenant is
trusted to receive signed OAuth token requests. - Block outbound application-server traffic to internal metadata services and
other sensitive internal addresses at the network layer. - Place a trusted proxy in front of OAuth providers that rejects token endpoint
redirects to a different origin.
These mitigations reduce exploitability but do not remove the vulnerable redirect
logic from the gem.
Credit
Found during the follow-up audit for GHSA-pp92-crg2-gfv9.
Reporter/coordinator: Peter H. Boling (pboling).
References
- Related OAuth 2 advisory: https://github.com/ruby-oauth/oauth2/security/advisories/GHSA-pp92-crg2-gfv9
- Fixed version:
oauthv1.1.6 - Behavior-introducing commit: https://github.com/ruby-oauth/oauth/commit/d74b767f
Impact
A consumer that calls OAuth::Consumer#get_request_token,OAuth::Consumer#get_access_token, or lower-level token request helpers against
an OAuth server whose token endpoint redirect target can be influenced may lose
three security properties:
- Cross-origin signed-request metadata disclosure. The redirected request is
signed for the attacker-controlled endpoint. Depending on the request method,
scheme, and parameters, the attacker may receive OAuth 1.0 parameters such asoauth_consumer_key,oauth_signature_method,oauth_timestamp,oauth_nonce,oauth_version, andoauth_signature. - SSRF from the application server. The OAuth client follows the redirect on
behalf of the application, so the redirected host is contacted from the
application server's network position. - Confused-deputy behavior. A malicious or compromised token endpoint can
cause an otherwise trusted application to initiate signed requests to an
unintended origin.
The disclosed OAuth 1 signature is not equivalent to an OAuth 2 bearer token: it
is bound to the signed request, timestamp, nonce, HTTP method, and request URL.
However, it can still disclose sensitive integration metadata, may be replayable
within the receiver's accepted nonce/timestamp window in some deployments, and
can expose application-server reachability to attacker-selected hosts.
Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside. Typical impact: access to internal metadata services, internal APIs, or cloud credentials.
CVE-2026-54605 has a CVSS score of 7.2 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (1.1.6); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Reject cross-origin token endpoint redirects by default and require explicit
opt-in for integrations that intentionally depend on that behavior.
The fix released in v1.1.6 does the following:
current_uri = token_request_uri(path)
redirected_uri = token_request_redirect_uri(current_uri, response)
response.error! unless redirected_uri
redirect_count = request_options[:token_request_redirect_count].to_i + 1
response.error! if redirect_count > token_request_max_redirects(request_options)
response.error! if token_request_cross_origin?(current_uri, redirected_uri) &&
!token_request_cross_origin_redirects?(request_options)
redirect_options = request_options.merge(token_request_redirect_count: redirect_count)
token_request(http_method, token_request_redirect_path(current_uri, redirected_uri), token, redirect_options, *arguments, &block)
The fix intentionally preserves same-origin redirect compatibility while making
cross-origin token endpoint redirects an explicit choice. It also avoids placing
internal redirect state in request_options passed to OAuth signing.
Frequently Asked Questions
- What is CVE-2026-54605? CVE-2026-54605 is a high-severity server-side request forgery (SSRF) vulnerability in oauth (rubygems), affecting versions >= 0.5.5, <= 1.1.5. It is fixed in 1.1.6. Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside.
- How severe is CVE-2026-54605? CVE-2026-54605 has a CVSS score of 7.2 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of oauth are affected by CVE-2026-54605? oauth (rubygems) versions >= 0.5.5, <= 1.1.5 is affected.
- Is there a fix for CVE-2026-54605? Yes. CVE-2026-54605 is fixed in 1.1.6. Upgrade to this version or later.
- Is CVE-2026-54605 exploitable, and should I be worried? Whether CVE-2026-54605 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-54605 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-54605? Upgrade
oauthto 1.1.6 or later.