Skip to content

Commit 5c1c804

Browse files
committed
Harden STS endpoint parsing in AWS auth
Replace permissive STS endpoint matching with strict HTTPS host validation so malformed inputs are rejected without regex backtracking risk.
1 parent 337fdda commit 5c1c804

2 files changed

Lines changed: 49 additions & 4 deletions

File tree

lib/vault/api/auth.rb

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# SPDX-License-Identifier: MPL-2.0
33

44
require "json"
5+
require "uri"
56

67
require_relative "secret"
78
require_relative "../client"
@@ -302,7 +303,7 @@ def gcp(role, jwt, path = 'gcp')
302303
# The path to the auth backend to use for the login procedure.
303304
#
304305
# @param [String] name optional
305-
# The named certificate role provided to the login request.
306+
# The named certificate role provided to the login request.
306307
#
307308
# @return [Secret]
308309
def tls(pem = nil, path = 'cert', name: nil)
@@ -328,9 +329,23 @@ def tls(pem = nil, path = 'cert', name: nil)
328329
#
329330
# @return [String] aws region
330331
def region_from_sts_endpoint(sts_endpoint)
331-
valid_sts_endpoint = %r{https:\/\/sts\.?(.*)\.amazonaws\.com}.match(sts_endpoint)
332-
raise "Unable to parse STS endpoint #{sts_endpoint}" unless valid_sts_endpoint
333-
valid_sts_endpoint[1].empty? ? 'us-east-1' : valid_sts_endpoint[1]
332+
uri = URI.parse(sts_endpoint)
333+
334+
unless uri.is_a?(URI::HTTPS) && uri.userinfo.nil?
335+
raise "Unable to parse STS endpoint #{sts_endpoint}"
336+
end
337+
338+
case uri.host
339+
when "sts.amazonaws.com"
340+
"us-east-1"
341+
when /\Asts\.([a-z0-9-]+)\.amazonaws\.com\z/,
342+
/\Asts\.([a-z0-9-]+)\.amazonaws\.com\.cn\z/
343+
Regexp.last_match(1)
344+
else
345+
raise "Unable to parse STS endpoint #{sts_endpoint}"
346+
end
347+
rescue URI::InvalidURIError
348+
raise "Unable to parse STS endpoint #{sts_endpoint}"
334349
end
335350
end
336351
end

spec/unit/auth_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ module Vault
1919
it { is_expected.to eq 'us-gov-west-1' }
2020
end
2121

22+
context 'with a standard regional endpoint' do
23+
let(:sts_endpoint) { "https://sts.us-west-2.amazonaws.com" }
24+
it { is_expected.to eq 'us-west-2' }
25+
end
26+
2227
context 'with no regional endpoint' do
2328
let(:sts_endpoint) { "https://sts.amazonaws.com" }
2429
it { is_expected.to eq 'us-east-1' }
@@ -33,6 +38,31 @@ module Vault
3338
let(:sts_endpoint) { "https://stsXamazonaws.com" }
3439
it { expect {subject}.to raise_exception(StandardError, "Unable to parse STS endpoint https://stsXamazonaws.com") }
3540
end
41+
42+
context 'with a host suffix attack' do
43+
let(:sts_endpoint) { 'https://sts.amazonaws.com.evil.example' }
44+
it { expect { subject }.to raise_exception(StandardError, 'Unable to parse STS endpoint https://sts.amazonaws.com.evil.example') }
45+
end
46+
47+
context 'with a query string' do
48+
let(:sts_endpoint) { 'https://sts.us-west-2.amazonaws.com?foo=bar' }
49+
it { is_expected.to eq 'us-west-2' }
50+
end
51+
52+
context 'with a non-root path' do
53+
let(:sts_endpoint) { 'https://sts.us-west-2.amazonaws.com/foo' }
54+
it { is_expected.to eq 'us-west-2' }
55+
end
56+
57+
context 'with a non-default port' do
58+
let(:sts_endpoint) { 'https://sts.us-west-2.amazonaws.com:8443' }
59+
it { is_expected.to eq 'us-west-2' }
60+
end
61+
62+
context 'with embedded user info' do
63+
let(:sts_endpoint) { 'https://user:pass@sts.us-west-2.amazonaws.com' }
64+
it { expect { subject }.to raise_exception(StandardError, 'Unable to parse STS endpoint https://user:pass@sts.us-west-2.amazonaws.com') }
65+
end
3666
end
3767
end
3868
end

0 commit comments

Comments
 (0)