Secure your SQS queues at rest and in transit
Posted: | Tags: cloud aws tilAWS allows you to enable server-side encryption (SSE) for you data at rest in your SQS queue. Disabling this option also has an effect on your encryption in transit as well. From the SQS documentation:
All requests to queues with SSE enabled must use HTTPS and Signature Version 4.
In other words disabling SSE also means you can now communicate to the SQS without TLS.
I wrote a Python script to send a message to a queue using the HTTP API and botocore
without using the higher level abstractions of boto3
. Note the endpoint used uses http
and not https
.
After creating a standard SQS queue with SSE disabled the script has no issues sending the message to the queue. We receive the MessageId
(below) which we can confirm by polling the queue.
{
"MD5OfMessageBody": "981b7dae8fee37caab68009b00f4c25f",
"MessageId": "6ffadb14-adeb-401f-8d80-8082da9b5d28"
}
Repeating the test with the same SQS queue but with SSE enabled1 returns the following error:
{
"__type": "com.amazonaws.sqs#InvalidSecurity",
"message": "All requests to this queue must use HTTPS and SigV4."
}
This confirms the excerpt from the documentation I quoted above. Now that SSE is enabled we must use HTTPS. Changing http
to https
in the endpoint allows us to successfully send messages to the queue again.
The SQS documentation (as well as a few AWS blog posts) cover the use of access policies. The DenyUnsecureTransport
policy statement can be used to enforce TLS connections to the queue. The policy denies any non-TLS connections by checking the aws:SecureTransport
property.
With this policy statement applied to our queue and disabling SSE again, running the test now returns a new error message:
{
"__type": "com.amazon.coral.service#AccessDeniedException",
"Message": "User: arn:aws:iam::<account_id>:user/<user> is not authorized to perform: sqs:sendmessage on resource: arn:aws:sqs:<region>:<account_id>:<queue_name> with an explicit deny in a resource-based policy"
}
The access policy works!
Takeaways
If you haven’t already enabled SSE on your queue, do it. Regardless if you have SSE enabled or not it doesn’t hurt to add the DenyUnsecureTransport
policy statement to your access policy.
You can apply the policy in several ways:
- Directly through the console on your queue
- Through CDK as described in the this AWS blog post
- Through Terraform as done in this aws-sample2
Python script
from botocore import crt
import botocore.session
import requests
from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
import json
account_id = ''
queue_name = ''
region = ''
session = botocore.session.Session()
signer = crt.auth.CrtS3SigV4Auth(session.get_credentials(), 'sqs', region)
endpoint = f'http://sqs.{region}.amazonaws.com'
headers = {
'Content-Type': 'application/x-amz-json-1.0',
'X-Amz-Target': 'AmazonSQS.SendMessage',
'Connection': 'Keep-Alive'
}
data = {
"QueueUrl": f"http://sqs.{region}.amazonaws.com/{account_id}/{queue_name}/",
"MessageBody": "Just a test message",
}
data = json.dumps(data)
request = AWSRequest(method='POST', url=endpoint, data=data, headers=headers)
request.context['payload_signing_enabled'] = True
signer.add_auth(request)
prepped = request.prepare()
response = requests.post(prepped.url, headers=prepped.headers, data=data)
print(response.text)