← Back to Tutorials
Tutorial 05 Intermediate

IAM Security Best Practices: Preventing Insider Threats

AWS Identity and Access Management (IAM) is your first and most critical line of defense. Unlike external attackers who must breach your perimeter, malicious insiders already have legitimate access. Learn how to implement least privilege, monitoring, and policy conditions to protect against insider threats.

25 min implementation
12 min read
Identity & Access Management

Why IAM Misconfigurations Are an Insider Threat Gateway

IAM misconfigurations turn legitimate access into a weapon, allowing insiders to escalate privileges beyond their intended role, access sensitive data across multiple AWS services, create persistent backdoors for continued access, operate undetected for extended periods, and cover their tracks by modifying logs and policies.

The Three Most Common IAM Vulnerabilities

1

Excessive Permissions

Users with broader access than required for their role, enabling data exfiltration and privilege escalation attacks. This is the most common IAM vulnerability.

2

Unmonitored Access

Lack of comprehensive logging and alerting on privileged actions, allowing insider threats to operate undetected for months.

3

Policy Misconfigurations

Overly permissive IAM policies, trust relationships, and missing conditions that create unintended access paths.

The Three Types of Insider Threats

  • Malicious Insiders: Employees, contractors, or partners who intentionally misuse privileges—exploiting excessive permissions, creating hidden users/roles, modifying policies for additional access
  • Negligent Insiders: Well-intentioned employees who inadvertently cause incidents through misconfigured policies, shared credentials, or social engineering
  • Compromised Insiders: Employees whose credentials have been stolen via phishing, malware, or credential stuffing—attackers then operate as insider threats
⚠️
Critical Reality: Most cybersecurity professionals are concerned with malicious insiders, yet only a minority feel equipped to handle insider threats effectively. Proper IAM configuration is your first line of defense.
1

Implement Least Privilege Access

~8 minutes

The principle of least privilege ensures users have only the minimum permissions necessary to perform their job functions. This dramatically reduces the potential impact of insider threats and compromised accounts.

Console Steps

1.1 Audit Current IAM Permissions

  • Navigate to IAM → Users in the AWS Console
  • Click on each user to review their attached policies
  • Document users with AdministratorAccess or overly broad permissions
  • Use IAM Access Analyzer to identify unused permissions
Audit IAM Permissions via CLI
# List all users
aws iam list-users --query 'Users[*].[UserName]' --output table

# For each user, check attached policies
aws iam list-attached-user-policies --user-name USERNAME
aws iam list-user-policies --user-name USERNAME

# Find users with AdministratorAccess
aws iam list-entities-for-policy \
    --policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
    --query 'PolicyUsers[*].UserName' --output table

# Check roles with AdministratorAccess
aws iam list-entities-for-policy \
    --policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
    --query 'PolicyRoles[*].RoleName' --output table

1.2 Create Role-Based Access Groups

  • Go to IAM → User groups
  • Create groups for specific job functions (e.g., Developers, DBAdmins, ReadOnlyUsers)
  • Attach minimal required policies to each group
  • Move users from individual policy attachments to appropriate groups
Developer Group Policy (Restricted Example)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstances",
                "ec2:DescribeImages",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::dev-bucket/*",
                "arn:aws:ec2:us-east-1:ACCOUNT-ID:instance/i-dev*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:RequestedRegion": ["us-east-1", "us-west-2"]
                }
            }
        },
        {
            "Effect": "Deny",
            "Action": [
                "iam:*",
                "organizations:*",
                "account:*"
            ],
            "Resource": "*"
        }
    ]
}
⚠️
Best Practice: Never assign AdministratorAccess to regular users. Instead, create custom policies with only the required permissions. For emergency access, use AWS IAM Identity Center (SSO) with time-limited sessions.

1.3 Implement Permission Boundaries

Permission boundaries set maximum permissions for IAM entities, preventing privilege escalation:

Apply Permission Boundary
# Create a permission boundary policy
aws iam create-policy \
    --policy-name DeveloperBoundary \
    --policy-document file://developer-boundary-policy.json

# Apply boundary to a user
aws iam put-user-permissions-boundary \
    --user-name DeveloperUser \
    --permissions-boundary arn:aws:iam::ACCOUNT-ID:policy/DeveloperBoundary

# Apply boundary to a role
aws iam put-role-permissions-boundary \
    --role-name DeveloperRole \
    --permissions-boundary arn:aws:iam::ACCOUNT-ID:policy/DeveloperBoundary
Security Win! Implementing least privilege reduces your insider threat attack surface significantly and limits the potential damage from compromised accounts.
2

Set Up IAM Access Monitoring

~6 minutes

Comprehensive monitoring of IAM activities is crucial for detecting insider threats in real-time. Most insider attacks go undetected for extended periods—proper monitoring reduces detection time to hours.

Console Steps

2.1 Enable CloudTrail for IAM Events

  • Navigate to CloudTrail service
  • Create a new trail named "security-audit-trail"
  • Enable logging for all regions
  • Include global service events (IAM, STS, CloudFront)
  • Enable log file validation
Create CloudTrail for IAM Monitoring
# Create CloudTrail for comprehensive IAM monitoring
aws cloudtrail create-trail \
    --name security-audit-trail \
    --s3-bucket-name my-security-logs-bucket \
    --include-global-service-events \
    --is-multi-region-trail \
    --enable-log-file-validation

# Start logging
aws cloudtrail start-logging --name security-audit-trail

2.2 Create IAM Anomaly Detection Alarms

Create CloudWatch metric filters for suspicious IAM activities:

CloudWatch Metric Filter for IAM Policy Changes
# Metric filter for IAM policy changes
aws logs put-metric-filter \
    --log-group-name CloudTrail/SecurityAuditTrail \
    --filter-name IAM-Policy-Changes \
    --filter-pattern '{ ($.eventName = DeleteGroupPolicy) || ($.eventName = DeleteRolePolicy) || ($.eventName = DeleteUserPolicy) || ($.eventName = PutGroupPolicy) || ($.eventName = PutRolePolicy) || ($.eventName = PutUserPolicy) || ($.eventName = CreatePolicy) || ($.eventName = DeletePolicy) || ($.eventName = CreatePolicyVersion) || ($.eventName = DeletePolicyVersion) || ($.eventName = AttachRolePolicy) || ($.eventName = DetachRolePolicy) || ($.eventName = AttachUserPolicy) || ($.eventName = DetachUserPolicy) || ($.eventName = AttachGroupPolicy) || ($.eventName = DetachGroupPolicy) }' \
    --metric-transformations \
        metricName=IAMPolicyChanges,metricNamespace=SecurityMetrics,metricValue=1

# Create alarm for IAM policy changes
aws cloudwatch put-metric-alarm \
    --alarm-name "Suspicious-IAM-Policy-Changes" \
    --alarm-description "Alert on IAM policy modifications" \
    --metric-name IAMPolicyChanges \
    --namespace SecurityMetrics \
    --statistic Sum \
    --period 300 \
    --threshold 1 \
    --comparison-operator GreaterThanOrEqualToThreshold \
    --evaluation-periods 1 \
    --alarm-actions arn:aws:sns:REGION:ACCOUNT:security-alerts

2.3 High-Risk IAM Actions to Monitor

Set up specific alerts for these critical actions:

  • CreateUser, DeleteUser, CreateRole, DeleteRole
  • AttachUserPolicy, AttachRolePolicy, PutUserPolicy
  • AssumeRole (especially cross-account)
  • CreateAccessKey, DeleteAccessKey
  • ChangePassword, CreateLoginProfile
  • AddUserToGroup, RemoveUserFromGroup
Set Up SNS Notifications
# Create SNS topic for IAM security alerts
aws sns create-topic --name iam-security-alerts

# Subscribe security team email
aws sns subscribe \
    --topic-arn arn:aws:sns:REGION:ACCOUNT:iam-security-alerts \
    --protocol email \
    --notification-endpoint EMAIL ADDRESS
Detection Improvement! Proper IAM monitoring reduces insider threat detection time from months to hours, preventing most potential data exfiltration.
3

Create Policy Boundaries and Conditions

~7 minutes

Policy conditions provide defense-in-depth by limiting when, where, and how IAM permissions can be used, even if credentials are compromised.

Console Steps

3.1 Implement Time-Based Access Controls

Add time-based conditions to sensitive IAM policies to restrict access to business hours:

Time-Based Access Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::sensitive-data/*",
            "Condition": {
                "DateGreaterThan": {
                    "aws:CurrentTime": "2026-01-01T08:00:00Z"
                },
                "DateLessThan": {
                    "aws:CurrentTime": "2026-12-31T18:00:00Z"
                },
                "ForAllValues:StringEquals": {
                    "aws:RequestedRegion": ["us-east-1"]
                }
            }
        }
    ]
}

3.2 Enforce MFA for Sensitive Operations

Require MFA for administrative actions with age restrictions:

MFA-Required Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:CreateUser",
                "iam:DeleteUser",
                "iam:AttachUserPolicy",
                "iam:DetachUserPolicy"
            ],
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                },
                "NumericLessThan": {
                    "aws:MultiFactorAuthAge": "3600"
                }
            }
        }
    ]
}

3.3 Implement IP-Based Access Restrictions

Restrict administrative access to corporate IP ranges:

IP-Restricted Access Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "203.0.113.0/24",
                        "198.51.100.0/24"
                    ]
                }
            }
        },
        {
            "Effect": "Deny",
            "Action": [
                "iam:*",
                "organizations:*"
            ],
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "203.0.113.0/24",
                        "198.51.100.0/24"
                    ]
                }
            }
        }
    ]
}

3.4 Secure Cross-Account Role Assumptions

Use external IDs and conditions for cross-account access:

Secure Cross-Account Trust Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::TRUSTED-ACCOUNT:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:ExternalId": "unique-external-id-12345"
                },
                "IpAddress": {
                    "aws:SourceIp": "203.0.113.0/24"
                }
            }
        }
    ]
}
💡
Security Note: Always use condition keys to limit the scope of permissions. A policy without conditions is a security vulnerability waiting to be exploited by insider threats.
4

Enable Privileged Session Monitoring

~4 minutes

Monitor privileged user sessions to detect unusual behavior patterns that may indicate insider threat activity using CloudTrail Insights and GuardDuty.

Console Steps

4.1 Enable AWS CloudTrail Insights

  • Navigate to CloudTrail → Trails
  • Select your security audit trail
  • Enable CloudTrail Insights for unusual activity patterns
  • Configure insights for both read and write events
Enable CloudTrail Insights
# Enable CloudTrail Insights
aws cloudtrail put-insight-selectors \
    --trail-name security-audit-trail \
    --insight-selectors '[{"InsightType": "ApiCallRateInsight"}, {"InsightType": "ApiErrorRateInsight"}]'

4.2 Set Up AWS GuardDuty

  • Navigate to GuardDuty service
  • Enable GuardDuty in all regions
  • Configure threat intelligence feeds
  • Set up automated response for high-severity findings
Enable GuardDuty
# Enable GuardDuty
aws guardduty create-detector --enable --finding-publishing-frequency FIFTEEN_MINUTES

# Get detector ID
DETECTOR_ID=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text)

# Create filter for IAM-related threats
aws guardduty create-filter \
    --detector-id $DETECTOR_ID \
    --name "IAM-Threats" \
    --finding-criteria '{
        "Criterion": {
            "type": {
                "Eq": [
                    "UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom",
                    "UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B",
                    "Persistence:IAMUser/UserPermissions"
                ]
            }
        }
    }'

4.3 Use IAM Access Analyzer

IAM Access Analyzer helps identify resources shared with external entities:

Enable IAM Access Analyzer
# Create an analyzer
aws accessanalyzer create-analyzer \
    --analyzer-name account-analyzer \
    --type ACCOUNT

# List findings for external access
aws accessanalyzer list-findings \
    --analyzer-arn arn:aws:access-analyzer:REGION:ACCOUNT:analyzer/account-analyzer \
    --filter '{"status": {"eq": ["ACTIVE"]}}'
Monitoring Active! You now have comprehensive insider threat detection covering behavioral anomalies, unusual access patterns, and automated response capabilities.

Validate Your Configuration

Complete these checks to ensure your IAM security configuration effectively prevents insider threats:

Validation Script

iam-security-validation.sh
#!/bin/bash
# IAM Security Validation Script

echo "=== IAM Security Validation ==="
echo ""

# Check for users with AdministratorAccess
echo "Checking for users with administrative access..."
ADMIN_USERS=$(aws iam list-entities-for-policy \
    --policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
    --query 'PolicyUsers[*].UserName' --output text)
if [ -n "$ADMIN_USERS" ]; then
    echo "✗ WARNING: Users with AdministratorAccess:"
    echo "$ADMIN_USERS"
else
    echo "✓ No users have AdministratorAccess directly attached"
fi
echo ""

# Check for users without MFA
echo "Checking for users without MFA..."
for user in $(aws iam list-users --query 'Users[*].UserName' --output text); do
    MFA=$(aws iam list-mfa-devices --user-name $user --query 'MFADevices[0].SerialNumber' --output text)
    if [ "$MFA" == "None" ] || [ -z "$MFA" ]; then
        echo "✗ User without MFA: $user"
    fi
done
echo ""

# Verify CloudTrail is enabled
echo "Verifying CloudTrail logging..."
TRAILS=$(aws cloudtrail describe-trails --query 'trailList[?IsMultiRegionTrail==`true`].Name' --output text)
if [ -n "$TRAILS" ]; then
    echo "✓ Multi-region CloudTrail enabled: $TRAILS"
else
    echo "✗ No multi-region CloudTrail found"
fi
echo ""

# Check GuardDuty status
echo "Checking GuardDuty status..."
DETECTORS=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text)
if [ -n "$DETECTORS" ] && [ "$DETECTORS" != "None" ]; then
    echo "✓ GuardDuty is enabled"
else
    echo "✗ GuardDuty is not enabled"
fi
echo ""

# Check Access Analyzer
echo "Checking IAM Access Analyzer..."
ANALYZERS=$(aws accessanalyzer list-analyzers --query 'analyzers[?status==`ACTIVE`].name' --output text)
if [ -n "$ANALYZERS" ]; then
    echo "✓ Access Analyzer active: $ANALYZERS"
    
    # Check for active findings
    FINDINGS=$(aws accessanalyzer list-findings \
        --analyzer-arn "arn:aws:access-analyzer:$(aws configure get region):$(aws sts get-caller-identity --query Account --output text):analyzer/$ANALYZERS" \
        --filter '{"status": {"eq": ["ACTIVE"]}}' \
        --query 'findings | length(@)')
    echo "  Active findings: $FINDINGS"
else
    echo "✗ No active Access Analyzer found"
fi
echo ""

echo "IAM security validation complete!"

Common Mistakes to Avoid

Using wildcard (*) permissions in production. This gives unlimited access and is a major insider threat vector. Always specify exact actions and resources.

Not implementing permission boundaries. Without boundaries, users with IAM permissions can escalate their own privileges through policy modifications.

Using shared service accounts. This eliminates accountability and makes insider threat detection impossible. Use individual IAM users or roles.

Not monitoring cross-account role assumptions. These are prime targets for insider threats seeking to expand their access across accounts.

Ignoring IAM access patterns. Regular access pattern analysis helps identify insider threats before they cause damage.

No MFA on privileged accounts. MFA should be required for all users, especially those with administrative or sensitive access.

Stop Managing IAM Security Manually

With hundreds of users, roles, and policies, manual IAM reviews become impossible. AWSight automatically monitors your IAM security posture 24/7, detects excessive permissions, tracks privilege escalation attempts, and provides real-time alerts on suspicious activity.

References