This check flags SQS queue policies that grant access to principals in other AWS accounts. Cross-account access is sometimes intentional, but unscoped grants let outside accounts read, send, or delete your messages. Lock the policy down to specific accounts, roles, and actions, and add a condition on aws:SourceArn or aws:SourceAccount.
Amazon SQS is the glue in a lot of event-driven systems. It buffers work between producers and consumers, decouples microservices, and absorbs traffic spikes so downstream services do not fall over. Because queues sit in the middle of so many data flows, the access policy attached to a queue is worth scrutinizing. The SQS Queue Allows Cross-Account Access check looks at your queue policies and tells you when one of them hands access to a principal that lives in a different AWS account.
That is not automatically a problem. Plenty of legitimate architectures need an SNS topic, an S3 bucket, or a partner account to drop messages into a queue. The issue is how the grant is written. A tight grant to one role for one action is fine. A wildcard principal with a wildcard action is a way for someone else to read your data or empty your queue.
What this check detects
Every SQS queue can have a resource-based policy, separate from IAM identity policies. The sqs_crossaccount check parses that policy document and inspects each Allow statement. It raises a finding when the Principal resolves to an AWS account ID, root, role, or user that is not the account owning the queue.
Concretely, it catches policies that look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOtherAccount",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::222233334444:root"
},
"Action": "sqs:*",
"Resource": "arn:aws:sqs:us-east-1:111122223333:orders-queue"
}
]
}
Account 111122223333 owns the queue, but the statement grants every SQS action to all of account 222233334444. The check also flags the more dangerous variants: a Principal of "*" with no conditions, or a star principal paired with a weak condition that does not actually restrict the caller.
Note: Resource policies on SQS are evaluated independently of IAM. A queue policy granting cross-account access works even if the other account has no matching IAM permissions referencing your queue. The queue owner is fully responsible for what the queue policy allows.
Why it matters
SQS messages frequently carry sensitive payloads: order details, user IDs, internal event metadata, sometimes credentials or tokens that should never have been there in the first place. When a queue policy grants broad cross-account access, you have created a few distinct risks.
Message theft and reconnaissance
If an external principal has sqs:ReceiveMessage, it can poll your queue and read messages. Even if the data is not classified as secret, the volume and timing of messages reveal a lot about how your business operates. An attacker who has compromised the other account inherits that visibility for free.
Message deletion and denial of service
With sqs:DeleteMessage or sqs:PurgeQueue, an outside party can drop work before your consumers process it. In an order processing pipeline that means lost orders. In a billing pipeline that means lost revenue events. The damage is silent, since the messages simply never arrive at the consumer.
Injection into your pipeline
With sqs:SendMessage, an external account can push arbitrary messages into your system. Your consumers trust the queue, so injected messages flow straight into whatever logic sits downstream. If a consumer deserializes the payload or treats queue contents as authoritative, this becomes a path to data corruption or, in bad cases, code execution.
Warning: A wildcard principal ("Principal": "*") without a condition does not just mean "other accounts in my organization." It means every AWS account on Earth. This is the single most common way SQS queues get exposed, and it is almost never what the author intended.
The confused deputy problem
When AWS services like SNS or S3 deliver to your queue, you grant access to a service principal. Without an aws:SourceArn condition, any resource of that service type, even one owned by a stranger, could be configured to deliver to your queue. This is the classic confused deputy: a trusted intermediary tricked into acting on behalf of an attacker.
How to fix it
The fix is almost always the same shape: replace broad grants with narrow ones, and add conditions that pin the access to the exact source you expect.
Step 1: Find the current policy
aws sqs get-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/111122223333/orders-queue \
--attribute-names Policy \
--query 'Attributes.Policy' \
--output text | jq .
Read the statements carefully. Note every cross-account principal, every action, and whether any conditions are present.
Step 2: Decide whether the access is intentional
If no other account should be touching this queue, the fix is to remove the cross-account statement entirely. If the access is legitimate, rewrite it to be specific.
Step 3: Write a least-privilege policy
Scope the principal to a single role or account, scope the action to only what is needed, and add a source condition where a service is involved. Here is a tight policy that lets one specific role in another account send messages, and nothing else:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPartnerProducer",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::222233334444:role/order-producer"
},
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:us-east-1:111122223333:orders-queue"
}
]
}
For a queue that receives from an SNS topic in another account, scope on the topic ARN instead:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSNSDelivery",
"Effect": "Allow",
"Principal": { "Service": "sns.amazonaws.com" },
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:us-east-1:111122223333:orders-queue",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "arn:aws:sns:us-east-1:222233334444:order-events"
}
}
}
]
}
Step 4: Apply the policy
Save the new document to a file and set it on the queue.
Danger: set-queue-attributes replaces the entire policy, it does not merge. If your queue legitimately serves multiple consumers or producers, make sure your new document contains every statement you still need before you apply it. A wrong policy here can break message delivery for production services.
aws sqs set-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/111122223333/orders-queue \
--attributes file://queue-policy.json
The file://queue-policy.json should contain a JSON object with a Policy key whose value is the policy document as a string. Many teams find it easier to manage this in Terraform, shown below.
Step 5: Verify
aws sqs get-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/111122223333/orders-queue \
--attribute-names Policy \
--query 'Attributes.Policy' --output text | jq .
Confirm there are no wildcard principals and that every cross-account grant has a specific principal and, where relevant, a source condition.
How to prevent it from happening again
One-off fixes drift back over time. The durable approach is to define queue policies as code and gate them in CI.
Define the policy in Terraform
Keep the queue and its policy in version control so every change goes through review:
resource "aws_sqs_queue" "orders" {
name = "orders-queue"
}
data "aws_iam_policy_document" "orders" {
statement {
sid = "AllowPartnerProducer"
effect = "Allow"
actions = ["sqs:SendMessage"]
resources = [aws_sqs_queue.orders.arn]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::222233334444:role/order-producer"]
}
}
}
resource "aws_sqs_queue_policy" "orders" {
queue_url = aws_sqs_queue.orders.id
policy = data.aws_iam_policy_document.orders.json
}
Catch bad policies before merge
Run a policy-as-code scanner against your Terraform in CI. Checkov, for example, flags wildcard principals on SQS:
checkov -d . --framework terraform \
--check CKV_AWS_166,CKV2_AWS_71
Fail the pipeline when a finding appears so the change never reaches production. For a custom rule, OPA Conftest lets you reject any SQS policy with a star principal:
deny[msg] {
input.resource.aws_sqs_queue_policy[name]
policy := json.unmarshal(input.resource.aws_sqs_queue_policy[name].policy)
policy.Statement[_].Principal == "*"
msg := sprintf("SQS queue policy %s uses a wildcard principal", [name])
}
Tip: Pair CI gates with continuous monitoring. Lensix runs the sqs_crossaccount check across all your accounts on a schedule, so a queue created by hand in the console, outside Terraform, still gets caught. CI covers what goes through the pipeline; runtime scanning covers everything else.
Use SCPs and Access Analyzer
An organization-wide guardrail catches mistakes the pipeline misses. IAM Access Analyzer can be configured to alert on any resource policy that grants access outside your organization, and it understands SQS resource policies natively.
aws accessanalyzer create-analyzer \
--analyzer-name org-external-access \
--type ORGANIZATION
Best practices
- Never use a wildcard principal. If you genuinely need broad access, scope it to your organization with the
aws:PrincipalOrgIDcondition instead of"*". - Grant one action, not
sqs:*. A producer needsSendMessage. A consumer needsReceiveMessage,DeleteMessage, andGetQueueAttributes. Nobody outside your account needsPurgeQueue. - Always add a source condition for service principals. Use
aws:SourceArnfor SNS and S3 deliveries to prevent confused deputy attacks. - Prefer roles over account root in principals. Granting to
:roottrusts every identity in that account. Granting to a specific role narrows the blast radius. - Encrypt sensitive queues with SSE-KMS and add a key policy condition. Even if the queue policy slips, a tight KMS key policy limits who can decrypt messages.
- Keep payloads minimal. Put secrets and large data behind a reference (an S3 key or a Secrets Manager ARN) rather than in the message body, so a leaked message reveals less.
- Review cross-account grants on a cadence. Partner integrations end, projects wind down, and the access that justified a grant disappears while the grant lingers.
Cross-account access on SQS is a tool, not a flaw. Treat each grant the way you would treat a key to your office: hand it to a specific person, for a specific door, and take it back when they no longer need it.

