DPDP technical safeguards check

Account Acme Retail (synthetic demo) (123456789012) · regions ap-south-1 · 02 Nov 2026, 11:39 UTC
Technical readiness, not legal advice. This report checks the technical safeguards that can be inspected in the AWS account (encryption, access control, logging, backups, breach detection) against the expectations of India's Digital Personal Data Protection Rules, 2025. Consent, notices, processor contracts and the legal interpretation of the Rules are outside its scope: please have them reviewed by a lawyer.
Baseline score
31/100F
weighted by severity
Controls failing
15 of 25
3 critical · 5 high · 6 medium · 1 low
Controls passing
6
1 not applicable · 0 to review
Coverage
83%
of the baseline could be evaluated

Summary

The account was checked against 25 baseline controls covering identity, logging, data, network and secrets, using read-only access only. It scores 31/100 (grade F). The most urgent finding: S3 Bucket acme-demo-uploads has public access due to bucket policy (DA-02, critical). Public buckets are found by automated scanners within hours. Nothing was changed in the account. Every fix below is code or a command for you to review and apply; a retest scan confirms it.

Top risks

SeverityControlWhat we foundFirst step
critical DA-02 No bucket is public S3 Bucket acme-demo-uploads has public access due to bucket policy. (acme-demo-uploads)
aws s3api put-public-access-block --bucket <bucket> --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
critical NC-01 No SSH, RDP or database port is open to the whole internet Security group api (sg-1c5d8088dba90bc1a) has SSH port 22 open to the Internet. (sg-1c5d8088dba90bc1a)
aws ec2 revoke-security-group-ingress --group-id <sg> --protocol tcp --port 22 --cidr 0.0.0.0/0
critical SP-01 No secrets in Lambda variables, EC2 user data or launch templates Potential secret found in Lambda function acme-invoice-mailer variables -> SMTP Credentials in variable SMTP_PASSWORD, Generic Password in variable SMTP_PASSWORD. (acme-invoice-mailer)
Move each value to Secrets Manager or SSM Parameter Store (SecureString), read it at runtime, then rotate it - it must be treated as exposed.
high ID-02 Every console user has MFA User priya.sharma has Console Password enabled but MFA disabled. (priya.sharma)
aws iam list-virtual-mfa-devices # find users without a device, then enable MFA for each, or move people to IAM Identity Center
high ID-05 Full administrator rights are limited to a break-glass role AWS policy AdministratorAccess is attached and allows '*:*' administrative privileges. (AdministratorAccess)
aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

DPDP technical safeguards at a glance

Safeguard areaControlsStatus
Encryption, masking and secretsDA-01, DA-03, DA-04, SP-01, SP-02 3 failing
Access controlID-01, ID-02, ID-03, ID-04, ID-05, ID-06, ID-07, DA-01, DA-02, DA-04, DA-05, NC-01, NC-02, NC-03, NC-04, SP-03 8 failing
Logging and monitoring (logs kept at least one year)LD-01, LD-03, LD-04 2 failing
Backups and continuityDA-04 no failures
Breach detection and 72-hour responseLD-01, LD-02, LD-05 1 failing
Tested restore within the last 12 monthsmanualask for evidence (service S4 runs a timed restore drill)
A written 72-hour breach runbookmanualtemplate attached (docs/dpdp/breach-runbook-72h.md)

All controls

Identity and access

Not evaluatedID-01 · Root user is protected (MFA on, no access keys)critical

The root user can do everything, including closing the account. A stolen root password or key is a total loss.

not evaluated (the scanner returned no result - see Method)
Prowler: iam_root_mfa_enabled, iam_no_root_access_key
FailID-02 · Every console user has MFAhigh

Passwords get phished and reused. MFA stops most account takeovers.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:iam::123456789012:user/priya.sharmaap-south-1User priya.sharma has Console Password enabled but MFA disabled.
How to fix
aws iam list-virtual-mfa-devices  # find users without a device, then enable MFA for each, or move people to IAM Identity Center
Console: IAM > Users > (user) > Security credentials > Assign MFA device.
Prowler: iam_user_mfa_enabled_console_access · CIS-7.0 2.10
PassID-03 · Access keys are rotated and unused keys removedhigh

Long-lived keys leak through laptops, CI logs and git history. Datadog found active keys over a year old at 59% of IAM users.

6 items checked, all passing
Prowler: iam_rotate_access_key_90_days, iam_user_accesskey_unused, iam_user_console_access_unused · CIS-7.0 2.11, CIS-7.0 2.12
FailID-04 · People get permissions through groups, roles or SSO - not attached to usersmedium

Permissions attached user by user drift and are never reviewed. Groups, roles and IAM Identity Center keep access auditable.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:iam::123456789012:user/ci-deployap-south-1User ci-deploy has the policy AdministratorAccess attached.
How to fix
aws iam detach-user-policy --user-name <user> --policy-arn <arn>   # after adding the user to a group with the same rights
Console: Prefer IAM Identity Center: one login per person, permission sets per job, no long-lived keys.
Prowler: iam_policy_attached_only_to_group_or_roles · CIS-7.0 2.13
FailID-05 · Full administrator rights are limited to a break-glass rolehigh

Every identity with AdministratorAccess is a way to lose the whole account. Most teams need two or three, not ten.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:iam::aws:policy/AdministratorAccessap-south-1AWS policy AdministratorAccess is attached and allows '*:*' administrative privileges.
How to fix
aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
Console: Replace AdministratorAccess with job-based policies (PowerUserAccess, ReadOnlyAccess, or service-scoped). Keep one MFA-protected break-glass role.
Prowler: iam_aws_attached_policy_no_administrative_privileges, iam_customer_attached_policy_no_administrative_privileges, iam_inline_policy_no_administrative_privileges · CIS-7.0 2.14
PassID-06 · Password policy requires long, non-reused passwordslow

Only matters for IAM users with console access, but auditors and customer questionnaires ask for it.

2 items checked, all passing
Prowler: iam_password_policy_minimum_length_14, iam_password_policy_reuse_24 · CIS-7.0 2.8, CIS-7.0 2.9
Not evaluatedID-07 · IAM Access Analyzer watches for external accessmedium

It flags buckets, roles and keys shared with other accounts or the public - for free.

not evaluated (the scanner returned no result - see Method)
Prowler: accessanalyzer_enabled

Logging and detection

PassLD-01 · CloudTrail records every region, with tamper-evident, private logscritical

Without CloudTrail there is no record of who did what - no investigation, and no basis for a breach report.

3 items checked, all passing
Prowler: cloudtrail_multi_region_enabled, cloudtrail_log_file_validation_enabled, cloudtrail_logs_s3_bucket_is_not_publicly_accessible · CIS-2.0 3.3, CIS-7.0 4.1, CIS-7.0 4.2
FailLD-02 · GuardDuty threat detection is onhigh

GuardDuty spots stolen credentials, crypto-mining and unusual API calls. Costs a few dollars a month for small accounts.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:guardduty:ap-south-1:123456789012:detector/unknownap-south-1GuardDuty is not enabled.
How to fix
Terraform: module.security_baseline: guardduty = true
aws guardduty create-detector --enable --finding-publishing-frequency FIFTEEN_MINUTES
Prowler: guardduty_is_enabled
FailLD-03 · AWS Config records configuration changesmedium

Config keeps a history of how each resource was configured - the evidence auditors and investigators ask for.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:config:ap-south-1:123456789012:recorderap-south-1AWS Config recorder 123456789012 is disabled.
How to fix
Terraform: module.security_baseline: config_recorder = true
Console: AWS Config > Get started > record all resources > deliver to the CloudTrail logs bucket.
Prowler: config_recorder_all_regions_enabled · CIS-7.0 4.3
FailLD-04 · Log groups keep logs for at least a yearmedium

Logs kept for days or weeks are gone when an incident is found months later. One year is the minimum many customers - and India's DPDP Rules - expect for security logs. ('Never expire' passes here; the cost check LOGS-001 flags its price.)

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:logs:ap-south-1:123456789012:log-group:/acme/api:*ap-south-1Log Group /acme/api has less than 365 days retention period (7 days).
How to fix
bash aws-foundation/fix-pack/scripts/set-log-retention.sh --days 365   # dry run first; add --apply to change
Prowler: cloudwatch_log_group_retention_policy_specific_days_enabled
PassLD-05 · Spend alerts would catch abuse within a daymedium

A stolen key used for crypto-mining shows up first on the bill. Budgets with email alerts are free.

1 item checked, all passing
Native check: spend_alerts

Data protection

Not evaluatedDA-01 · S3 Block Public Access is on for the whole accountcritical

One account-wide switch stops any bucket from being made public by mistake - the cause of many data leaks.

not evaluated (the scanner returned no result - see Method)
Prowler: s3_account_level_public_access_blocks
FailDA-02 · No bucket is publiccritical

Public buckets are found by automated scanners within hours.

1 failing item, 11 passing
Evidence (1)
ResourceRegionFinding
arn:aws:s3:::acme-demo-uploadsap-south-1S3 Bucket acme-demo-uploads has public access due to bucket policy.
How to fix
aws s3api put-public-access-block --bucket <bucket> --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
# if the bucket serves a website, put CloudFront with origin access control in front instead
Prowler: s3_bucket_public_access, s3_bucket_policy_public_write_access, s3_bucket_public_list_acl, s3_bucket_public_write_acl · CIS-7.0 2.21
FailDA-03 · Data is encrypted by default (S3 and EBS)medium

Default encryption is free and protects disks, snapshots and buckets that get copied or shared by mistake.

1 failing item, 3 passing
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:volumeap-south-1EBS Default Encryption is not activated.
How to fix
Terraform: module.security_baseline: ebs_encryption_by_default = true
aws ec2 enable-ebs-encryption-by-default --region <region>
Prowler: s3_bucket_default_encryption, ec2_ebs_default_encryption · CIS-1.5 2.1.1, CIS-7.0 6.1.1
PassDA-04 · Databases are encrypted, private, backed up and protected from deletionhigh

An RDS instance reachable from the internet, without backups, is one mistake away from a data leak or permanent loss.

4 items checked, all passing
Prowler: rds_instance_storage_encrypted, rds_instance_no_public_access, rds_instance_backup_enabled, rds_instance_deletion_protection · CIS-7.0 3.2.1, CIS-7.0 3.2.3
PassDA-05 · No snapshot is publiccritical

A public EBS or RDS snapshot hands a full copy of the disk or database to anyone.

4 items checked, all passing
Prowler: ec2_ebs_public_snapshot, rds_snapshots_public_access

Network and compute

FailNC-01 · No SSH, RDP or database port is open to the whole internetcritical

Open admin and database ports are scanned constantly; one weak password or unpatched service is enough.

1 failing item, 4 passing
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:security-group/sg-1c5d8088dba90bc1aap-south-1Security group api (sg-1c5d8088dba90bc1a) has SSH port 22 open to the Internet.
How to fix
aws ec2 revoke-security-group-ingress --group-id <sg> --protocol tcp --port 22 --cidr 0.0.0.0/0
# use SSM Session Manager (no open port) or a VPN/allow-list instead
Prowler: ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22, ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389, ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306, ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432, ec2_securitygroup_allow_ingress_from_internet_to_all_ports · CIS-7.0 6.3, CIS-7.0 6.4
No resourcesNC-02 · Default security groups allow no trafficlow

Resources launched without a chosen group land in the default one; it should block everything.

no matching resources in the scanned regions
Prowler: ec2_securitygroup_default_restrict_traffic
FailNC-03 · Instances require IMDSv2high

IMDSv1 lets an SSRF bug in a web app read the instance's cloud credentials (the Capital One breach). Datadog found IMDSv2 enforced on only 49% of instances.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:instance/i-2d70a6f89d6a8d1bdap-south-1EC2 Instance i-2d70a6f89d6a8d1bd has IMDSv2 disabled or not required.
How to fix
Terraform: module.security_baseline: ec2_imdsv2_default = true # new instances
bash aws-foundation/fix-pack/scripts/require-imdsv2.sh --regions <region>   # dry run lists instances; --apply after testing one
aws ec2 modify-instance-metadata-options --instance-id <id> --http-tokens required --http-put-response-hop-limit 2
Prowler: ec2_instance_imdsv2_enabled, ec2_instance_account_imdsv2_enabled · CIS-7.0 6.7
FailNC-04 · Only intended instances have public IP addressesmedium

Every public instance is attack surface (and costs $3.65 a month per IPv4 address). Most belong behind a load balancer.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:instance/i-2d70a6f89d6a8d1bdap-south-1EC2 Instance i-2d70a6f89d6a8d1bd has a Public IP: 54.214.72.161 (ec2-54-214-72-161.ap-south-1.compute.amazonaws.com).
How to fix
Console: Move workloads to private subnets behind a load balancer; reach them with SSM Session Manager. Keep a public IP only where it is intended, and note it in the report.
Prowler: ec2_instance_public_ip

Secrets and pipelines

FailSP-01 · No secrets in Lambda variables, EC2 user data or launch templatescritical

Anyone with read-only access - including auditors and many SaaS tools - can read these fields.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:lambda:ap-south-1:123456789012:function:acme-invoice-mailerap-south-1Potential secret found in Lambda function acme-invoice-mailer variables -> SMTP Credentials in variable SMTP_PASSWORD, Generic Password in variable SMTP_PASSWORD.
How to fix
Console: Move each value to Secrets Manager or SSM Parameter Store (SecureString), read it at runtime, then rotate it - it must be treated as exposed.
Prowler: awslambda_function_no_secrets_in_variables, ec2_instance_secrets_user_data, ec2_launch_template_no_secrets
FailSP-02 · Secrets Manager secrets rotate automaticallymedium

A secret that never changes stays valid after every leak.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:secretsmanager:ap-south-1:123456789012:secret:acme/prod/db-zTNKUhap-south-1SecretsManager secret acme/prod/db has rotation disabled.
How to fix
aws secretsmanager rotate-secret --secret-id <id> --rotation-lambda-arn <arn> --rotation-rules AutomaticallyAfterDays=90
Prowler: secretsmanager_automatic_rotation_enabled
FailSP-03 · CI/CD logs in with short-lived OIDC credentials, not stored keyshigh

Keys stored in CI were stolen in the tj-actions, Trivy and Checkmarx incidents. OIDC tokens expire in minutes.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:iam::123456789012:user/ci-deployglobalIAM user 'ci-deploy' looks like a CI/CD identity and has an active access key (created 2026-11-02); replace it with an OIDC role
How to fix
Terraform: gha-hardening-kit/terraform/github-oidc-role (service S2)
Console: Create an OIDC role trusted only by your repository's main branch or a protected environment, switch the workflow to role-to-assume, then delete the old access key.
Native check: ci_oidc
FailSP-04 · Container images are scanned on push and tags are immutablelow

Scanning catches known vulnerabilities before deployment; immutable tags stop a pushed image being silently replaced.

3 failing items
Evidence (3)
ResourceRegionFinding
arn:aws:ecr:ap-south-1:123456789012:registry/123456789012ap-south-1ECR registry 123456789012 has BASIC scanning without automated scanning enabled.
arn:aws:ecr:ap-south-1:123456789012:repository/acme/apiap-south-1ECR repository acme/api has scan on push disabled.
arn:aws:ecr:ap-south-1:123456789012:repository/acme/apiap-south-1Repository acme/api does not have immutability configured.
How to fix
Terraform: module.security_baseline: ecr_scan_on_push = true
aws ecr put-image-tag-mutability --repository-name <repo> --image-tag-mutability IMMUTABLE
Prowler: ecr_registry_scan_images_on_push_enabled, ecr_repositories_scan_images_on_push_enabled, ecr_repositories_tag_immutability

Method and limits