Back to blog
AWSBest PracticesCloud SecurityOperations & ComplianceServerless

SQS Queue Not Encrypted: Why It Matters and How to Fix It

Learn why unencrypted Amazon SQS queues are a security risk and how to enable SSE-SQS or SSE-KMS encryption with CLI, Terraform, and CI/CD policy gates.

TL;DR

This check flags SQS queues without server-side encryption (SSE), which leaves message payloads readable at rest. Enable SSE with an SQS-managed or KMS key in one update to close the gap.

Amazon SQS quietly moves a huge amount of data between services. Order events, password reset tokens, internal API payloads, and PII often flow through queues that nobody thinks about after they are created. The SQS Queue Not Encrypted check looks at whether a queue has server-side encryption turned on. If it does not, every message sitting in that queue is stored on disk in plaintext as far as your encryption posture is concerned.

It is a small setting with an outsized blast radius, and it is one of the easiest misconfigurations to fix once you know it is there.


What this check detects

The check inspects each SQS queue in your AWS account and reports any queue that does not have server-side encryption enabled. Concretely, it looks at two queue attributes:

  • SqsManagedSseEnabled — whether SSE-SQS (encryption with an Amazon-owned key) is active.
  • KmsMasterKeyId — whether the queue is encrypted with an AWS KMS key (either the AWS-managed alias/aws/sqs key or a customer-managed key).

If neither is set, the queue is flagged as unencrypted. Encryption in SQS covers data at rest, meaning the message bodies stored on Amazon's infrastructure while they wait to be consumed. Data in transit to and from SQS is already protected by TLS regardless of this setting.

Note: SQS has supported SSE-SQS (managed encryption with zero configuration) since 2022. Queues created before then, or created with Infrastructure as Code that never specified an encryption attribute, are the usual culprits behind this finding.


Why it matters

Encryption at rest is not just a checkbox for auditors. It changes what an attacker, or a careless insider, can do with access they should not have.

The realistic risk scenarios

  • Sensitive payloads in plaintext. Queues frequently carry data that you would never store unencrypted in a database: customer emails, tokens, signed URLs, internal identifiers. Without SSE, that data lives at rest without the protection your compliance framework almost certainly assumes is there.
  • KMS gives you an access control layer. When you encrypt with a customer-managed KMS key, reading messages requires both SQS permissions and kms:Decrypt on the key. That second gate means a compromised IAM role with broad SQS access still cannot read the data unless it also has KMS access. An unencrypted queue gives you no such defense in depth.
  • Audit and key rotation. KMS records every decrypt call in CloudTrail and rotates keys on a schedule. An unencrypted queue gives you none of that visibility.

Frameworks including PCI DSS, HIPAA, SOC 2, and FedRAMP expect data at rest to be encrypted. An unencrypted queue carrying regulated data is a finding waiting to be written up, and worse, a genuine gap if anyone gets hold of the underlying storage or a misconfigured snapshot pathway.

Warning: Encryption protects data at rest, not access. A queue can be fully encrypted and still be wide open if its access policy allows the wrong principals to call ReceiveMessage. Treat this check as one layer, and review your queue policies separately.


How to fix it

The good news: enabling encryption on an existing queue does not delete or recreate it, and it does not affect messages currently in flight. You are updating an attribute on a live resource.

Option 1: SSE-SQS (simplest, no extra cost)

If you just need encryption at rest with no additional key management, turn on SSE-SQS. It uses an Amazon-owned key and carries no KMS charges.

aws sqs set-queue-attributes \
  --queue-url https://sqs.us-east-1.amazonaws.com/123456789012/my-queue \
  --attributes SqsManagedSseEnabled=true

Option 2: SSE-KMS with a customer-managed key (recommended for sensitive data)

For queues carrying regulated or sensitive data, use a customer-managed KMS key so you control the key policy, rotation, and audit trail.

aws sqs set-queue-attributes \
  --queue-url https://sqs.us-east-1.amazonaws.com/123456789012/my-queue \
  --attributes KmsMasterKeyId=arn:aws:kms:us-east-1:123456789012:key/abcd1234-...,KmsDataKeyReusePeriodSeconds=300

The KmsDataKeyReusePeriodSeconds attribute controls how long SQS caches a data key before calling KMS again. A higher value (up to 86400 seconds) reduces KMS request volume and cost; a lower value tightens the security window. The default is 300 seconds.

Warning: SSE-KMS adds KMS API costs. Every encrypt and decrypt operation outside the data key reuse window is a billable KMS request. On high-throughput queues this can add up, so tune KmsDataKeyReusePeriodSeconds deliberately rather than leaving it at the minimum.

Console steps

  1. Open the Amazon SQS console and select the queue.
  2. Choose Edit.
  3. Scroll to the Encryption section and set Server-side encryption to Enabled.
  4. Pick Amazon SQS key (SSE-SQS) or AWS KMS key, then choose your key.
  5. Choose Save.

Don't forget the KMS key policy

If you use a customer-managed key, the key policy must let the principals producing and consuming messages use it. A producer needs kms:GenerateDataKey and kms:Decrypt; a consumer needs kms:Decrypt. If services suddenly start failing after you enable SSE-KMS, this is almost always why.

{
  "Sid": "AllowSQSProducersAndConsumers",
  "Effect": "Allow",
  "Principal": {
    "AWS": [
      "arn:aws:iam::123456789012:role/order-producer",
      "arn:aws:iam::123456789012:role/order-consumer"
    ]
  },
  "Action": [
    "kms:GenerateDataKey",
    "kms:Decrypt"
  ],
  "Resource": "*"
}

Note: If your queue is an event source for SNS, S3, EventBridge, or Lambda, those services need kms:GenerateDataKey and kms:Decrypt too. Grant access via a key policy statement scoped to the service principal (for example sns.amazonaws.com) rather than opening the key broadly.


Fixing it in Infrastructure as Code

Manual fixes drift. Bake encryption into the template so every new queue is born encrypted.

Terraform

# SSE-SQS (managed)
resource "aws_sqs_queue" "orders" {
  name              = "orders"
  sqs_managed_sse_enabled = true
}

# SSE-KMS (customer-managed key)
resource "aws_sqs_queue" "payments" {
  name                              = "payments"
  kms_master_key_id                 = aws_kms_key.sqs.arn
  kms_data_key_reuse_period_seconds = 300
}

CloudFormation

{
  "Type": "AWS::SQS::Queue",
  "Properties": {
    "QueueName": "orders",
    "SqsManagedSseEnabled": true
  }
}

Tip: Create a shared Terraform module for queues that sets encryption by default and refuses to disable it. Teams then get a compliant queue for free without having to remember the attribute on every resource.


How to prevent it from happening again

One-time remediation closes today's gap. Preventing the next unencrypted queue requires a control that runs before or right after deployment.

Policy as code in CI/CD

Add a check to your pipeline that fails the build when a queue is missing encryption. With Checkov, the relevant rule is CKV_AWS_27 (SQS queue encryption):

checkov -d . --check CKV_AWS_27

For a custom OPA / Conftest gate against a Terraform plan:

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_sqs_queue"
  not resource.change.after.sqs_managed_sse_enabled
  not resource.change.after.kms_master_key_id
  msg := sprintf("SQS queue %s has no server-side encryption", [resource.address])
}

Detective controls in the account

  • Enable the AWS Config managed rule sqs-queue-encrypted to continuously flag noncompliant queues.
  • Use an SCP or AWS Config remediation to auto-enable SSE-SQS on any queue created without it, so drift self-heals.
  • Run Lensix on a schedule so the sqs_unencrypted check catches queues created out-of-band, including those made by other teams, scripts, or third-party tools that your IaC scanner never sees.

Tip: CI scanners only see the code that runs through them. Plenty of queues get created by the console, the CLI, or SDK scripts during incidents. A continuous scanner that reads the live account is the only way to catch those, which is exactly the gap this Lensix check fills.


Best practices

  • Encrypt by default, decide on the key later. Turn SSE on for every queue. Use SSE-SQS as the floor and upgrade to SSE-KMS for queues carrying sensitive or regulated data.
  • Use customer-managed keys where access control matters. They give you a separate permission boundary, key rotation, and per-decrypt audit logging in CloudTrail.
  • Tune the data key reuse period. Balance KMS cost against your security requirements rather than accepting defaults blindly.
  • Pair encryption with a tight access policy. Encryption at rest does nothing if the queue policy lets unknown principals receive messages. Audit both together.
  • Scope KMS grants to the services that need them. Avoid "Principal": "*" on key policies. Name the roles and service principals explicitly.
  • Encrypt dead-letter queues too. DLQs hold the messages that failed processing, which often include the exact payloads you most want protected. They are easy to forget.

Encryption on SQS is one of the rare security wins that costs almost nothing, breaks almost nothing, and closes a real gap. Enable it everywhere, gate new queues in CI, and let a continuous scan catch the ones that slip through.