This check flags SQS queue policies that combine Effect=Allow, Principal=*, and a wildcard action, which lets anyone on the internet read, write, or delete your messages. Replace the wildcard with specific AWS principals and scope the actions down to only what each consumer needs.
Amazon SQS is the workhorse behind a huge number of decoupled systems. Order pipelines, event fan-outs, async job processors, and dead-letter queues all run through it. Because queues so often sit quietly in the background, their access policies tend to get written once and never reviewed again. That is exactly how a wildcard principal sneaks into production and stays there.
The SQS Queue Policy Has Wildcard Principal check (sqs_policywildcards) looks for one of the more dangerous misconfigurations a queue can have: a resource policy statement that grants broad actions to everyone.
What this check detects
An SQS queue can have a resource-based policy attached to it, separate from any IAM identity policies. This check parses that policy and raises a finding when it sees a single statement that contains all three of the following:
- Effect set to Allow — the statement grants access rather than denying it.
- Principal set to
*— the access applies to any AWS account and, in practice, anyone on the internet. - A wildcard action — typically
sqs:*orSQS:*, meaning every queue operation is permitted.
Here is the kind of policy that trips the check:
{
"Version": "2012-10-17",
"Id": "WideOpenPolicy",
"Statement": [
{
"Sid": "AllowEveryone",
"Effect": "Allow",
"Principal": "*",
"Action": "sqs:*",
"Resource": "arn:aws:sqs:us-east-1:123456789012:orders-queue"
}
]
}
Note: An SQS principal of "*" or {"AWS": "*"} means "any AWS principal." Without a Condition block restricting the source (for example, a specific account, VPC endpoint, or source ARN), this is effectively public access.
Why it matters
A wildcard principal with a wildcard action is one of the broadest grants you can write. It does not just allow reading messages. sqs:* covers SendMessage, ReceiveMessage, DeleteMessage, PurgeQueue, and SetQueueAttributes. Any one of those is a problem on its own.
Message theft and data exposure
Queues frequently carry sensitive payloads: customer records, order details, internal event data, sometimes even credentials passed between services. With ReceiveMessage open to the world, an attacker can poll your queue and drain those messages before your real consumers ever see them. The messages disappear from the queue after a successful receive and delete, so you may not even notice data is leaking.
Message injection
With SendMessage open, an attacker can push arbitrary messages into your queue. If a downstream worker trusts queue contents and acts on them, for example by writing to a database, triggering a Lambda, or kicking off a payment, you now have an unauthenticated path straight into your application logic.
Danger: A public sqs:PurgeQueue grant lets anyone wipe every message in the queue in a single call. For a payment or order pipeline, that is an instant denial of service with permanent data loss for any in-flight messages.
Cost and abuse
SQS bills per request. An open queue can be hammered with millions of requests, running up your bill and potentially being used as a relay in someone else's abuse campaign.
The real-world version of this almost always starts innocently. A developer is debugging a cross-account integration, swaps in Principal: "*" to "just make it work," and forgets to tighten it back down. Six months later it is still wide open and nobody remembers it is there.
How to fix it
The fix is to replace the wildcard principal and wildcard action with the specific principals and operations each consumer actually needs.
Step 1: Find the queues with open policies
List your queues and pull each policy attribute:
aws sqs list-queues --query 'QueueUrls[]' --output text
# For a given queue, fetch the policy
aws sqs get-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/orders-queue \
--attribute-names Policy \
--query 'Attributes.Policy' \
--output text | jq .
Step 2: Identify who actually needs access
Before you change anything, figure out which principals legitimately use the queue. Common cases:
- A specific IAM role on the same account (a worker service or Lambda execution role).
- A trusted second account that produces or consumes messages.
- An AWS service such as S3 or SNS sending events to the queue.
Step 3: Write a scoped policy
Here is the same queue locked down so only a specific consumer role can receive and delete, and only a specific producer role can send:
{
"Version": "2012-10-17",
"Id": "ScopedOrdersPolicy",
"Statement": [
{
"Sid": "AllowConsumer",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/orders-worker"
},
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
],
"Resource": "arn:aws:sqs:us-east-1:123456789012:orders-queue"
},
{
"Sid": "AllowProducer",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/orders-api"
},
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:us-east-1:123456789012:orders-queue"
}
]
}
Step 4: Apply the new policy
Warning: Replacing the policy takes effect immediately. If you miss a legitimate principal, that consumer will start getting AccessDenied errors and messages may back up. Confirm your producer and consumer ARNs before applying, and watch the queue depth after the change.
Save the scoped policy to a file, then set it:
cat > queue-policy.json <<'EOF'
{ ... your scoped policy from Step 3 ... }
EOF
aws sqs set-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/orders-queue \
--attributes Policy="$(cat queue-policy.json | jq -c . | jq -Rs .)"
Note: The set-queue-attributes call expects the policy as a JSON string value, which is why the example double-encodes it with jq. If you prefer, build the attributes file with the policy already escaped and pass --attributes file://attributes.json.
If you only need same-account access
Many queues do not need a resource policy at all. If every producer and consumer lives in the same account, you can grant access through IAM identity policies on the roles instead and remove the queue policy entirely:
aws sqs set-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/orders-queue \
--attributes Policy=""
With an empty queue policy, access falls back to IAM, which is the simplest model to reason about for single-account setups.
How to prevent it from happening again
Fixing one queue is easy. Keeping wildcard principals out of every queue across every account is the real work, and it belongs in automation rather than memory.
Codify queue policies in IaC
Define queues and their policies in Terraform or CloudFormation so the access model is reviewed in pull requests, not edited live in the console. A Terraform example:
data "aws_iam_policy_document" "orders" {
statement {
sid = "AllowConsumer"
effect = "Allow"
actions = ["sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes"]
principals {
type = "AWS"
identifiers = [aws_iam_role.orders_worker.arn]
}
resources = [aws_sqs_queue.orders.arn]
}
}
resource "aws_sqs_queue_policy" "orders" {
queue_url = aws_sqs_queue.orders.id
policy = data.aws_iam_policy_document.orders.json
}
Gate it in CI/CD
Run a policy-as-code check against your Terraform plans before they merge. Tools like Checkov, tfsec, and OPA/Conftest can fail a build when a queue policy contains a wildcard principal. A minimal Conftest rule:
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_sqs_queue_policy"
policy := json.unmarshal(resource.change.after.policy)
statement := policy.Statement[_]
statement.Effect == "Allow"
statement.Principal == "*"
msg := sprintf("SQS queue policy %s uses a wildcard principal", [resource.address])
}
Tip: Pair the CI gate with continuous detection in Lensix. The sqs_policywildcards check runs against live AWS state, so it catches drift, manual console edits, and queues created outside your IaC pipeline that a plan-time check would never see.
Use SCPs as a backstop
For organization-wide enforcement, a Service Control Policy can deny the creation of SQS policies with public principals across every account, so even a developer with admin rights in a sandbox cannot open a queue to the world.
Best practices
- Default to no resource policy. If access can be granted through IAM identity policies in the same account, do that instead of attaching a queue policy.
- Name explicit principals. Use full role or account ARNs. Avoid
Principal: "*"entirely unless you have a hard requirement and a tightConditionblock. - Scope actions to intent. Producers get
sqs:SendMessage. Consumers getsqs:ReceiveMessageandsqs:DeleteMessage. Nobody outside an admin role needssqs:*. - Constrain with conditions. When you must allow a service or broad principal, add a
Conditiononaws:SourceArn,aws:SourceAccount, oraws:SourceVpceso only the intended caller can use the grant. - Encrypt sensitive queues. Enable SSE with a KMS key so that even if a policy slips, the KMS key policy adds a second gate on who can decrypt messages.
- Review policies on a schedule. Resource policies rot. Treat them as something to audit regularly, not set and forget.
A wildcard principal on an SQS queue is a small line in a JSON document with an outsized blast radius. Scope it down, move the policy into version-controlled IaC, and put a gate in front of every change so the next "just make it work" edit never reaches production.

