Separation of Concerns vs Least Privilege and Why Security Engineers Confuse These All the Time

6 min read

I was reviewing an AWS IAM policy last week when a colleague asked me why we were “separating concerns” by splitting read and write permissions across different roles. I paused mid-sentence, realizing they’d just conflated two completely different security principles that get mixed up constantly in our field.

The truth is, I’ve made this same mistake. Early in my career, I’d throw around “separation of concerns” and “least privilege” interchangeably, thinking they were just different ways of saying “split things up for security.” It wasn’t until I designed a system that spectacularly failed an audit that I understood these aren’t just different approaches—they’re solving fundamentally different problems.

The Incident That Taught Me Everything

Picture this: I’m tasked with building the IAM structure for a new microservices architecture. Being a diligent security engineer, I carefully separated each service into its own AWS account. The payment service got one account, user management got another, and so on. I was proud of my “separation of concerns”—each service was beautifully isolated.

Then I created the IAM roles. The payment service role? It could read from every S3 bucket, write to any DynamoDB table, and invoke any Lambda function within its account. After all, it was “separated” from other services, so it was secure, right?

Wrong. So very wrong.

When the security review came, the auditor took one look at the payment service IAM policy and asked the question that still makes me cringe: “Why does your payment processor need to read your compliance logs?”

That’s when it clicked. I had implemented separation of concerns at the architecture level but completely ignored least privilege at the permissions level.

What Separation of Concerns Actually Means

Separation of concerns is an architectural principle. It’s about organizing your system so that different components handle different responsibilities. In AWS terms, this might look like:

  • Compute separation: Lambda functions for business logic, separate from data storage
  • Network separation: Different VPCs or subnets for different tiers
  • Account separation: Different AWS accounts for dev, staging, and production
  • Service separation: Microservices instead of monoliths

The goal isn’t security but it’s maintainability, scalability, and clarity. When your payment processing logic is separate from your user authentication logic, developers can work on one without breaking the other. When your database is in a different subnet from your web servers, you can apply different network rules to each tier.

Here’s what proper separation of concerns looks like in AWS:

# Different services, different concerns
PaymentService:
  Responsibilities: 
    - Process transactions
    - Handle payment webhooks
    - Generate payment reports
  
UserService:
  Responsibilities:
    - User authentication
    - Profile management
    - Session handling

NotificationService:
  Responsibilities:
    - Send emails
    - Push notifications
    - SMS messaging

Each service has a clear, single responsibility. They’re separated not for security, but for architectural clarity.

What Least Privilege Really Means

Least privilege, on the other hand, is purely a security principle. It states that every component should have only the minimum permissions necessary to perform its function. No more, no less.

This applies within each separated concern. Your payment service might be architecturally separate, but it still needs to follow least privilege for every permission it requires:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:PutItem"
      ],
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/payments"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::payment-config/prod/config.json"
    }
  ]
}

Notice what’s NOT in this policy:

  • No s3:* permissions
  • No access to other DynamoDB tables
  • No unnecessary services like EC2 or RDS
  • No administrative permissions

The payment service can do exactly what it needs to do, nothing more.

Why The Confusion Happens

The confusion between these principles is understandable because they often work together. Good security architecture typically involves both:

  1. Separate your concerns so that different components have different responsibilities
  2. Apply least privilege so that each component has only the permissions it needs for its specific responsibility

But here’s where people get tripped up: you can have perfect separation of concerns with terrible least privilege, and vice versa.

I’ve seen monolithic applications (poor separation of concerns) with incredibly granular IAM policies (least privilege). I’ve also seen beautifully architected microservices (great separation of concerns) where every service has admin permissions (terrible least privilege).

The Real-World Implementation

Let me show you how these principles work together in practice. Consider an e-commerce platform:

Separation of Concerns:

Each service handles one concern. Payment doesn’t know about user profiles, user service doesn’t handle inventory, etc.

Least Privilege (Permissions):

// Payment Service Role
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["dynamodb:GetItem", "dynamodb:PutItem"],
      "Resource": "arn:aws:dynamodb:*:*:table/payments"
    },
    {
      "Effect": "Allow",
      "Action": ["stripe:charge"],
      "Resource": "*"
    }
  ]
}

// User Service Role  
{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["dynamodb:*"],
      "Resource": "arn:aws:dynamodb:*:*:table/users"
    },
    {
      "Effect": "Allow",
      "Action": ["ses:SendEmail"],
      "Resource": "*"
    }
  ]
}

Each service gets exactly the permissions it needs for its separated concern.

The Practical Checklist

When designing your AWS architecture, ask these questions:

For Separation of Concerns:

  • Does each component have a single, clear responsibility?
  • Can I modify one service without affecting others?
  • Are my deployment pipelines separate?
  • Do different teams own different services?

For Least Privilege:

  • What’s the minimum this role needs to function?
  • Can I remove any permissions and still have it work?
  • Am I using wildcards where I should be specific?
  • When did I last audit these permissions?

A Security Engineer’s Perspective

As security engineers, we need both principles, but for different reasons:

Separation of concerns helps us limit blast radius when something goes wrong, makes security reviews manageable by creating clear boundaries between components, and enables team autonomy without compromising security. When services are properly separated, teams can work independently while maintaining clear security boundaries.

Least privilege helps us minimize attack surface by reducing unnecessary permissions, prevents privilege escalation by limiting what compromised credentials can access, ensures compliance with regulatory requirements that mandate minimal access controls, and reduces the impact when credentials do get compromised.

The Bottom Line

The next time someone mentions “separating concerns” in the context of IAM permissions, gently remind them that they probably mean “least privilege.” And if they’re talking about “least privilege” when discussing microservices architecture, they might actually mean “separation of concerns.”

Both principles are crucial for building secure, maintainable systems in AWS. But understanding the distinction will make you a better security engineer and save you from the kind of audit embarrassment that still makes me wince.

Trust me on this one—the auditors definitely know the difference.

AWS IAM Security Architecture Best Practices