Andy Bedinger

Avoid ALBeast - Essentials for Secure AWS ALB Authentication

11/5/2024

I read miggio.io’s post on how they discovered ALBeast with interest when it first came out, and read it again just recently. The core takeaway is: when using the AWS ALB authentication feature, make sure you don’t have ALB targets that allow traffic that bypasses the ALB. That is, in my opinion, the singular effective mitigating control to securely use ALB authentication.

The article stresses the importance of validating that the signer of the ALB JWT is the expected ALB. It provides example Python code from AWS’s documentation demonstrating this. Here is a snippet:

# Step 1: Validate the signer
expected_alb_arn = 'arn:aws:elasticloadbalancing:region-code: \\
account-id:loadbalancer/app/load-balancer-name/load-balancer-id'

encoded_jwt = headers.dict['x-amzn-oidc-data']
jwt_headers = encoded_jwt.split('.')[0]
decoded_jwt_headers = base64.b64decode(jwt_headers)
decoded_jwt_headers = decoded_jwt_headers.decode("utf-8")
decoded_json = json.loads(decoded_jwt_headers)
received_alb_arn = decoded_json['signer']

assert expected_alb_arn == received_alb_arn, "Invalid Signer"

But what prevents an attacker from forging the signer? Nothing. Here’s the code snippet in pseudo-code:

  1. Retrieve the x-amzn-oidc-data header value.
  2. Extract the JWT header (left of the first .).
  3. Base64 decode it, returning a bytes literal.
  4. Decode it to a UTF-8 string.
  5. Parse it as JSON.
  6. Get the value of the signer field in the JSON body.

There is nothing encrypted here—only encoded. So while the check to validate the issuer of the JWT is really your ALB is a control, it doesn’t help if the attacker knows your ALB ARN.

That’s (probably) why AWS’s documentation states:

To ensure security, you must verify the signature before doing any authorization based on the claims

How to Verify the Signature

Modern libraries like PyJWT (Python) or jsonwebtoken (Node.js) can verify JWT signatures. In PyJWT (version 1.5.1+), signature verification is enabled by default. You can handle errors by catching the jwt.InvalidSignatureError exception.

AWS’s blog post on ALB authentication is a bit more up to date than their documentation and includes this comment in the Python snippet:

# If the signature is invalid, it should return an error, such as:
# Cannot decode JWT token: Signature verification failed

However, even signature verification is not enough. The example code in the documentation shows that the app should request the public key from AWS to decrypt the JWT—using an attacker-controlled value! Yes, the public key used comes from the kid field in the JWT header, and all JWTs in an AWS region are signed by the same key. Provided the attacker has minted a JWT (as demonstrated by miggio.io) using AWS’s PKI infrastructure in the same region as the victim ALB, the JWT signature will be valid. Customers should be able to manage the key used to sign their JWTs.

Key Takeaways

This is a good place for a reminder about JWTs: don’t put anything secret in them! As this case highlights, encrypting them doesn’t mean you are the only one who can decrypt them.

Of course, you should be using a modern library that verifies JWT signatures to mitigate tampering, and validating the issuer field matches expectations. But the inability for AWS customers to manage the key used to sign their own JWTs when using ALB authentication means that even if you follow those best practices, inadvertently exposing your ALB targets could still leave you vulnerable to ALBeast.