Back to Blog

AWS Security Gaps Teams Still Miss (And How to Close Them Fast)

Misused SCPs, missing data events, skipped zero-friction guardrails, and click-ops policy. Four AWS security gaps easier to close than to live with.

After working with dozens of cloud security teams, one pattern keeps repeating. The most damaging AWS security gaps are rarely caused by complex architecture, missing budget, or a talent shortage. Most of them sit in controls that already exist, cost little to deploy, and shut down real attack paths.

The common denominator is not a lack of tooling. It is outdated mental models, a vague fear of operational friction, and the habit of treating security configuration as a second-class citizen next to core infrastructure. Teams write thousands of lines of Terraform for networks and compute, then manage their organization-wide security boundaries by hand in the console.

This post covers four architectural habits that keep these AWS security gaps open: misusing Service Control Policies (SCPs) for resource-side problems, rejecting CloudTrail data events over a cost myth, skipping zero-friction guardrails out of fear of breaking production, and leaving policy management to click-ops. For each gap, you will find the specific native control that closes it and the policy code to deploy it.

Key Takeaways

  • SCPs restrict what principals inside your AWS Organization can do. They cannot evaluate or block requests from identities outside your AWS Organization.
  • Resource Control Policies (RCPs) enforce a resource-side perimeter that applies to every request hitting your resources, regardless of who the caller is.
  • CloudTrail data events cost roughly $10 per 10 million events delivered to S3. The real cost trap is routing raw data plane logs into a SIEM, not recording them.
  • A two-tier logging architecture stores all sensitive data events in a low-cost S3 security data lake and forwards only high-fidelity triggers to the SIEM.
  • Guardrails like denying organizations:LeaveOrganization and blocking member account root usage carry zero operational risk and can ship on day one.
  • Managing SCPs and RCPs as code with Terraform takes an afternoon and turns your perimeter into a versioned, peer-reviewed, auditable artifact.

Why AWS Security Gaps Persist in Mature Teams

Mature teams miss these gaps for predictable reasons, and none of them are technical. The controls in question, RCPs, CloudTrail Advanced Event Selectors, Organizational service policies (such as S3, EC2 and Bedrock Policies), and Terraform-managed policies, are all natively-supported by AWS and have been generally available for years.

Three failure modes show up again and again. First, historical momentum: teams keep using the tool they learned in 2019 for problems it was never designed to solve. Second, unquantified fear: “data events will explode our bill” and “org-wide policies will break production” are repeated as facts without anyone running the math or the simulation. Third, priority inversion: infrastructure is treated as code while the boundaries that protect it are treated as a manual chore.

The result is a posture that looks strong on a CSPM dashboard and collapses under a real attack path. As we argued in the prevention gap in visibility-based cloud security, seeing a risk is not the same as making it impossible. The four gaps below are places where prevention is available today and simply not switched on.

SCP vs RCP: The Identity-Side Blind Spot in Your AWS Perimeter

An SCP answers exactly one question: what can principals inside my AWS Organization do? It is an identity-side restriction, and it is blind to the outside world. An SCP cannot evaluate or block a request initiated by an identity outside your AWS Organization. That single sentence explains an entire class of AWS security gaps.

For years, organization-level boundaries in AWS meant Service Control Policies. Because of that momentum, many teams still treat SCPs as an all-purpose shield and keep layering identity-side constraints onto resource-side problems. This misuse creates an illusion of security and two recurring blind spots.

The STS trust relationship trap. A team wants to restrict which external entities can assume IAM roles in their environment, so they write an SCP constraining the STS service. However, external entities calling sts:AssumeRole on your roles will never be evaluated by your SCPs until they start operating with the role they assumed. For example: a third-party SaaS platform, a vendor, or an attacker’s account. Because the caller is external, the SCP never enters the evaluation during the Assume Role action.

The data perimeter illusion. An SCP that denies S3 access outside the corporate IP range only locks down internal developers. If a bucket policy is misconfigured to allow public or cross-account access, an outside attacker pulls data directly over the public internet. The SCP is bypassed entirely.

Resource Control Policies Close the Resource-Side Gap

AWS introduced Resource Control Policies specifically to shift the question from “who is doing the action?” to “what is being done to my resources?” An RCP applies a maximum permission boundary at the selected Organization level that evaluates every request hitting your resources, regardless of where the caller’s account lives.

Without RCPs, your data perimeter is decentralized. It depends on every developer configuring every S3 bucket, KMS key, and Secrets Manager policy perfectly, forever. With an RCP, one policy makes cross-organization access structurally impossible. The following example policy enforces identity perimeter on a few of the RCP-supported services and excludes AWS Service Principals access pattern (which needs to be addressed a bit differently):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EnforceOrgIdentityPerimeter",
      "Effect": "Deny",
      "Principal": "*",
      "Action": [
        "s3:*",
        "sts:AssumeRole",
        "kms:*",
        "secretsmanager:*",
        "sqs:*"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:PrincipalOrgID": "o-example12345"
        },
        "BoolIfExists": {
          "aws:PrincipalIsAWSService": "false"
        }
      }
    }
  ]
}
QuestionSCPRCP
What does it restrict?Actions of principals inside your organizationAccess to resources inside your organization
Blocks external callers?No, external principals are never evaluatedYes, every request to the resource is evaluated
Typical useDeny risky actions, enforce regions, protect baselinesIdentity perimeter, block cross-org data access
Failure mode when misusedBlind spots for external accessNot applicable to identity-side restrictions

To build a modern perimeter, stop solving resource-side vulnerabilities with identity-side policies. Pair the two. Our essential AWS guardrails baseline for reducing blast radius walks through the SCP and RCP pairs we deploy first.

One note on that topic: many popular services are supported by RCP, but if you use an unsupported service resource that relies on its own access policies, you will need to manage access at the resource level for now—a more complex process, but a viable workaround.

CloudTrail Data Events: The Cost Myth That Blinds Incident Response

Most teams enable CloudTrail management events and stop there. As a result, they log control plane activity but not data plane activity. They can see that someone changed an S3 bucket policy, but not who read objects from it. They see that a Lambda function was modified, but miss the runtime access behavior around it.

The universal excuse is cost: “we cannot enable data events everywhere, it will explode our CloudTrail bill.” This fear rarely survives contact with the actual CloudTrail pricing math. Delivering 10 million data events to S3 costs about $10. One hundred million events costs about $100. Recording is rarely the budget killer.

The real financial trap is downstream. Blindly routing millions of raw data plane events into Splunk or Datadog is what produces the horror-story invoices. The answer is a two-tier architecture, not blindness:

TierDestinationWhat goes thereWhy
1. Security data lakeLow-cost, isolated S3 bucketAll production S3 reads, DynamoDB item activity, KMS decryptsNear-zero storage cost, full forensic record for blast-radius analysis
2. Real-time SIEMSplunk, Datadog, or equivalentHigh-fidelity triggers only: access to restricted secrets, data plane calls from outside the approved networkKeeps SIEM ingest costs proportional to actual signal

CloudTrail Advanced Event Selectors do the filtering natively. You select which resources, event types, and API calls forward to which destination, so the SIEM only receives what an analyst would act on.

When an incident hits, the difference is decisive. Without data events, your responders know a credential was exposed but cannot say what it touched. With a forensic lake, blast-radius analysis is a query, not a guess. Do not reject data plane visibility because of a myth. Model the cost, secure the forensic lake, and ingest with intention.

Zero-Friction AWS Guardrails You Can Deploy on Day One

There is a class of preventive guardrails that carry zero operational risk because no legitimate workload or human engineer should ever perform the actions they block. Deploying them requires no log analysis, no behavioral simulation, and no negotiation with engineering. Skipping them is pure posture paralysis.

The paralysis is understandable. For complex application-level boundaries, the fear of breaking production is justified, and enforcement should follow simulation. The mistake is applying that caution uniformly. Separate your boundaries into two buckets: those that need behavioral simulation, and those that are fundamental cloud hygiene. Deploy the hygiene bucket immediately.

Two examples shut down structural attack paths on day one:

  1. Prevent accounts from leaving the organization. If an attacker compromises administrative credentials in a member account, their cleanest escape is organizations:LeaveOrganization. Severing the account instantly strips your SCPs, RCPs, and centralized logging. No workload ever needs to call this.
  2. Block member account root usage. Day-to-day administration should happen through IAM Identity Center. The root user of a member account is a legacy liability, and production applications should never run as root in the first place. You can choose if you want to exclude AssumedRoot access patterns if you rely on root users for common tasks.

Both fit in one SCP:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyLeaveOrganization",
      "Effect": "Deny",
      "Action": "organizations:LeaveOrganization",
      "Resource": "*"
    },
    {
      "Sid": "DenyRootUserActions",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "ArnLike": {
          "aws:PrincipalArn": "arn:aws:iam::*:root"
        },
        "Null": {
          "aws:AssumedRoot": "true"
        }
      }
    }
  ]
}

For boundaries beyond the hygiene tier, the answer is simulation before enforcement rather than avoidance. That is how guardrails scale without slowing DevOps: you prove a policy is safe against real activity data, then enforce it.

Policy as Code: Move AWS Guardrails Out of Click-Ops

There is a strange irony in modern cloud engineering. Organizations write thousands of lines of pristine Terraform for networks, compute, and databases, yet manage the policies governing all of it by clicking through the AWS Console. Infrastructure is code; the security perimeter is a manual chore.

Click-ops policy management costs you two capabilities. The first is plain-text visibility: with policies in a repository, anyone can read exactly what boundaries are enforced in seconds instead of clicking through nested console menus. The second is a historical ledger of why. When policies live in Git, every change is versioned, tied to a peer-reviewed pull request, and documented with the business context behind each exception. That prevents the classic failure where an emergency exception added during an incident is forgotten three months later.

The hesitation usually comes from an assumption that this requires a custom delivery platform. It does not. You declare policies as plain JSON files and map them to Organizational Units with the standard aws_organizations_policy resource:

resource "aws_organizations_policy" "baseline_guardrails" {
  name    = "baseline-guardrails"
  type    = "SERVICE_CONTROL_POLICY"
  content = file("${path.module}/policies/baseline-guardrails.json")
}

resource "aws_organizations_policy_attachment" "workloads" {
  policy_id = aws_organizations_policy.baseline_guardrails.id
  target_id = var.workloads_ou_id
}

Setup takes an afternoon. In return, your security boundaries become a visible, auditable, sustainable engineering discipline instead of a fragile layer of hidden console configuration.

How Blast Operationalizes These AWS Guardrails

Most teams do not struggle because AWS lacks controls. They struggle to discover which guardrails are missing, prove that enforcement will not break production, and keep policies accurate as the environment changes. That operational gap, not the policy syntax, is why the four gaps above stay open for years.

Blast’s Preemptive Cloud Defense Platform addresses this through a lifecycle of discovery, simulation, enforcement, exception management, and continuous validation. Discovery maps which SCP and RCP protections exist and which attack paths remain open. Simulation replays real activity against a proposed boundary, so “will this break production?” gets a data-backed answer instead of a guess. Enforcement ships policies through code, and exception management keeps every deviation documented and time-bound instead of forgotten.

The zero-friction guardrails in this post are exactly where that lifecycle starts. They are the fastest proof that prevention does not have to mean friction. If you want to see which of these gaps are open in your own organization, the Cloud Prevention Assessment maps them in days.

Conclusion: Close the Gaps Before Attackers Map Them

The most resilient cloud environments are not defined by the sophistication of their monitoring. They are defined by how cleanly they eliminate structural blind spots. Closing the four AWS security gaps in this post requires no new headcount and no new platform, just four architectural decisions:

  1. Enforce the resource-side perimeter with RCPs instead of stretching SCPs past their design.
  2. Record data plane events into a cheap forensic lake and ingest selectively into the SIEM.
  3. Deploy zero-friction hygiene guardrails on day one, without waiting for simulations they do not need.
  4. Manage every organization policy as versioned, peer-reviewed code.

In every case, closing the gap is simpler than managing the fallout of leaving it open. Prevention-first architecture is not a bigger investment than reactive cleanup; it is a smaller one, made earlier. That is the premise Blast is built on.

Glossary

TermDefinition
Service Control Policy (SCP)An AWS Organizations policy that sets the maximum permissions for principals inside member accounts. SCPs only evaluate requests made by identities within the organization.
Resource Control Policy (RCP)An AWS Organizations policy that sets the maximum available permissions on resources in member accounts, evaluated for every caller, including identities outside the organization.
Data perimeterA set of preventive controls that keep trusted identities accessing trusted resources from expected networks, enforced at the organization level rather than per resource.
CloudTrail management eventsLog records of control plane operations, such as creating a role or changing a bucket policy.
CloudTrail data eventsLog records of data plane operations, such as S3 object reads, DynamoDB item activity, and KMS decrypt calls.
Advanced Event SelectorsCloudTrail configuration that filters which data events are recorded and delivered, by resource, event name, or other fields.
Security data lakeA low-cost, isolated storage tier, typically S3, that retains high-volume security telemetry for forensic and blast-radius analysis.
Policy as codeManaging security policies as version-controlled files deployed through tooling like Terraform, rather than manual console configuration.
Blast radiusThe full scope of resources, data, and identities an attacker can reach after compromising a given credential or workload.

FAQ

1. What is the difference between an SCP and an RCP in AWS?

An SCP restricts what principals inside your AWS Organization can do; it never evaluates requests from external identities. An RCP restricts what can be done to your resources, and it applies to every request regardless of who the caller is. Identity-side and resource-side perimeters solve different problems, and a complete boundary needs both.

2. Why can’t an SCP block external access to my S3 buckets?

Because SCPs only apply to principals inside your organization. If a bucket policy accidentally grants public or cross-account access, the external caller’s request is never evaluated against your SCPs. Only a resource-side control, such as an RCP with an aws:PrincipalOrgID condition or a correct bucket policy, blocks that path.

3. How much do CloudTrail data events actually cost?

Delivery to S3 costs about $1.00 per 100,000 data events, so 10 million events cost roughly $10 and 100 million roughly $100. Storage in S3 adds very little. Costs become significant only when raw data events are routed into a SIEM, which is why a two-tier lake-plus-selective-ingest architecture is the right default.

4. Which AWS guardrails can I deploy without breaking production?

Start with actions no legitimate workload performs: deny organizations:LeaveOrganization, deny member account root usage, and deny disabling CloudTrail or GuardDuty. These need no behavioral simulation because healthy environments generate zero matching activity. Boundaries that touch application workflows should go through simulation against real activity data before enforcement, which is the approach Blast’s platform automates.

5. How do I manage SCPs and RCPs as code?

Store each policy as a JSON file in Git and deploy it with Terraform’s aws_organizations_policy and aws_organizations_policy_attachment resources mapped to your Organizational Units. Every change then flows through a pull request with review and context. A minimal setup takes an afternoon and requires no custom pipeline.

You might also like

AWS Guardrails Cloud Governance Native Controls
10 min read

AWS Sign-In Conditions vs. Microsoft Conditional Access: What They Mean for Data Perimeter Protection

How AWS Sign-In policies compare to Microsoft Entra Conditional Access, and what each layer contributes to a real data perimeter strategy.
AWS Guardrails Cloud Governance Native Controls
2 min read

AWS Just Made Console Access Part of the Security Perimeter. Now Teams Need a Safe Way to Manage It

AWS Sign-In now enforces network-based console access. What changed, and how to enforce it safely across the organization without locking anyone out.