unclemeat
unclemeat

Reputation: 5207

AWS Chalice Unexpected Behavior

I am writing a Chalice deployment, and experiencing behavior that I cannot explain.

My root endpoint accepts PUT requests, and verifies some basic authorization credentials.

from chalice import Chalice
from base64 import b64decode

app = Chalice(app_name='test-basic-auth-issue')

@app.route('/', methods=['PUT'])
def index():
    auth = app.current_request.headers['Authorization'].split()

    username, password = b64decode(auth[1]).split(':')

    if username == 'test-user' and password == 'test-password':
        return {username: password}

    else:
        raise Exception('Unauthorized')

Using curl to interface with this API:

curl https://test-user:test-password@<API-URL>/dev/ --upload-file test.txt

I get the following response:

{"message":"Authorization header requires 'Credential' parameter. Authorization header requires 'Signature' parameter. Authorization header requires 'SignedHeaders' parameter. Authorization header requires existence of either a 'X-Amz-Date' or a 'Date' header. Authorization=Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ="}

However - when including any parameters in the URL:

curl https://test-user:test-password@<API-URL>/dev/?ANYTHING --upload-file test.txt

I get the expected response of:

{"test-user": "test-password"}

I'm not sure why specifying parameters is effecting the Authorization.

Upvotes: 0

Views: 994

Answers (1)

Michael - sqlbot
Michael - sqlbot

Reputation: 179364

I have no inside information, so I have no way of knowing whether the following is actually correct, but this seems like a reasonable explanation for the behavior you're seeing.

AWS APIs generally support two alternatives for supplying your credentials: query string parameters, or the Authorization: header..

To the layer in their stack that checks the Authorization: header, your value seems wrong, so they throw an error, since your supplied credentials are not in the correct format...

...unless it sees a query string in the URI... in which case, it could be choosing to allow the request to proceed, on the assumption that the authorization might be done at that layer.

So the request is handed off to a different layer, which is responsible for query string handling. It doesn't find any credentials in the query string, but it is also aware of no credentials having been found while headers were being processed, previously, so the request is processed as an anonymous request if those are allowed.

So, you're slipping through a hole: by adding a query string, any query string, you prevent the Authorization: header from throwing the error.

It's not a security vulnerability, in my estimation, but rather a case where something in the URI changes how headers are interpreted -- specifically, whether a malformed (for its purposes) authorization header will trigger an exception or be allowed to pass.

I think there's a reasonable case for calling this behavior "broken," but at the same time I suspect it may be out of the hands of the API Gateway developers, who are working behind an unnamed front-end component that is common to multiple AWS services. API Gateway is a bit of an exception, in the AWS ecosystem, in that the customer can define how headers are manipulated... so this may very well be simply a platform limitation.

I disagree -- in part, and on a technicality -- with @LorenzodeLara's assertion that API Gateway is not compliant with RFC-7235. There is no requirement that a server respond with WWW-Authenticate: -- from the RFC:

Upon receipt of a request for a protected resource that omits credentials, contains invalid credentials (e.g., a bad password) or partial credentials (e.g., when the authentication scheme requires more than one round trip), an origin server SHOULD send a 401(Unauthorized) response that contains a WWW-Authenticate header field with at least one (possibly new) challenge applicable to the requested resource.

The words SHOULD and RECOMMENDED in RFCs indicate desired behavior for which there may be valid exceptions. They are not mandatory requirements.

It is, on the other hand, fully accurate that API Gateway does not at all support authenticating requests from its perspective with HTTP basic auth... but you are only trying to get it to pass those credentials through to the code running inside, which does appear to work, given that your user agent submits credentials without a challenge... assuming you prevent the front-end system from thwarting you, with the addition of a query string.

That's my analysis, subject to correction by someone with access to more authoritative information. In that light, using basic auth may not be the best plan, since it appears to work somewhat by accident.

I hate to come to that conclusion. Basic auth gets a bad rap, which is not wholly merited when combined with HTTPS -- unlike request signing, it typically "just works," right out of the box, even for a user who doesn't understand the difference between GET and POST, much less how to generate a hex-encoded HMAC digest.

Upvotes: 1

Related Questions