Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/bugfix-auth-59087.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "bugfix",
"category": "auth",
"description": "Require HTTPS when using bearer token authorization"
}
7 changes: 7 additions & 0 deletions awscli/botocore/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
urlunsplit,
)
from botocore.exceptions import (
InvalidEndpointConfigurationError,
NoAuthTokenError,
NoCredentialsError,
UnknownSignatureVersionError,
Expand Down Expand Up @@ -876,6 +877,12 @@ def add_auth(self, request):
if self.auth_token is None:
raise NoAuthTokenError()

# Bearer tokens are sent in plaintext so must only be sent over TLS
if not request.url.startswith('https'):
raise InvalidEndpointConfigurationError(
msg='Bearer authorization requires an HTTPS endpoint.'
)

auth_header = f'Bearer {self.auth_token.token}'
if 'Authorization' in request.headers:
del request.headers['Authorization']
Expand Down
25 changes: 25 additions & 0 deletions tests/functional/botocore/test_bearer_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

import copy

import botocore
import pytest
from botocore.config import Config
from botocore.exceptions import InvalidEndpointConfigurationError

from tests import ClientHTTPStubber, patch_load_service_model
from tests.functional.test_useragent import (
Expand Down Expand Up @@ -253,3 +256,25 @@ def test_user_agent_does_not_have_bearer_service_env_vars_feature_id(
feature_ids = parse_registered_feature_ids(ua_string)

assert "3" not in feature_ids


def test_bearer_auth_rejects_http_endpoint(monkeypatch, patched_session):
monkeypatch.setenv(
"AWS_BEARER_TOKEN_BEARER_SERVICE", "bearer-service-token"
)
http_ruleset = copy.deepcopy(MOCK_RULESET)
http_ruleset["rules"][0]["endpoint"]["url"] = (
"http://bearer-service.us-west-2.amazonaws.com/"
)
patch_load_service_model(
patched_session, monkeypatch, MOCK_SERVICE_MODEL, http_ruleset
)
client = patched_session.create_client(
"bearer-service",
region_name="us-west-2",
)
with ClientHTTPStubber(client) as http_stubber:
http_stubber.add_response()
with pytest.raises(InvalidEndpointConfigurationError):
client.mock_operation(MockOpParam="mock-op-param-value")
assert len(http_stubber.requests) == 0
25 changes: 23 additions & 2 deletions tests/unit/botocore/test_auth_bearer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
import pytest
from botocore.auth import BearerAuth
from botocore.awsrequest import AWSRequest
from botocore.exceptions import NoAuthTokenError
from botocore.exceptions import (
InvalidEndpointConfigurationError,
NoAuthTokenError,
)
from botocore.tokens import FrozenAuthToken

cases = [
Expand Down Expand Up @@ -43,12 +46,26 @@
"token": None,
"expectedException": NoAuthTokenError,
},
{
"documentation": "Signer rejects http endpoints",
"url": "http://example.com",
"headers": {},
"token": "mF_9.B5f-4.1JqM",
"expectedException": InvalidEndpointConfigurationError,
},
{
"documentation": "Signer rejects http endpoints with existing header",
"url": "http://example.com/path?query=value",
"headers": {"Authorization": "Bearer foo"},
"token": "mF_9.B5f-4.1JqM",
"expectedException": InvalidEndpointConfigurationError,
},
]


@pytest.mark.parametrize("test_case", cases)
def test_bearer_auth(test_case):
url = "https://example.com"
url = test_case.get("url", "https://example.com")
headers = test_case.get("headers", {})
request = AWSRequest(method="GET", url=url, headers=headers)

Expand All @@ -69,3 +86,7 @@ def test_bearer_auth(test_case):
elif expected_exception:
with pytest.raises(expected_exception):
bearer_auth.add_auth(request)
# The token must never be attached when signing fails
assert request.headers.get("Authorization") != (
f"Bearer {test_case['token']}"
)
Loading