aws-cdk
aws-cdk copied to clipboard
feat(sns): add sns data protection policy
Issue # (if applicable)
Closes #34136.
Reason for this change
AWS SNS now supports Message Data Protection policies, which allow users to define rules to detect and handle sensitive data in SNS messages. This feature enables users to implement security controls like auditing, masking, redacting, or blocking messages containing sensitive data.
Description of changes
This PR adds support for SNS Data Protection policies to the AWS CDK, including:
Added dataProtectionPolicy property to TopicProps
Adds Core Data Protection:
DataProtectionPolicy- The main construct for defining an SNS data protection policyDataProtectionPolicyStatement- Represents a rule within the policyDataDirection- Enum to specify inbound or outbound message flow- Operations classes (
AuditOperation,MaskOperation,RedactOperation,DenyOperation) - Custom data identifier support via
CustomDataIdentifier
Adds Managed Data Identifier collections:
PersonalIdentifiers- For detecting PII (names, addresses, SSNs, etc.)FinancialIdentifiers- For detecting financial information (credit cards, bank accounts)CredentialsIdentifiers- For detecting sensitive credentials (AWS keys, private keys)DeviceIdentifiers- For detecting device-related information (IP addresses)HealthIdentifiers- For detecting health-related information (PHI)
Updated README with examples
Describe any new or updated permissions being added
Description of how you validated changes
Created unit tests for all new components Created integration test Manually tested deployment using
// Create a CloudWatch log group for SNS data protection findings
const logGroup = new logs.LogGroup(this, 'DataProtectionLogGroup', {
logGroupName: '/aws/vendedlogs/sns-data-protection',
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// Create 10 custom data identifiers (maximum allowed per policy)
const employeeIdPattern = new sns.CustomDataIdentifier({
name: 'EmployeeID',
regex: 'EMP-[0-9]{6}',
});
const projectCodePattern = new sns.CustomDataIdentifier({
name: 'ProjectCode',
regex: 'PRJ-[A-Z]{3}-[0-9]{4}',
});
const customerIdPattern = new sns.CustomDataIdentifier({
name: 'CustomerID',
regex: 'CUST-[0-9]{8}',
});
const orderIdPattern = new sns.CustomDataIdentifier({
name: 'OrderID',
regex: 'ORD-[0-9]{10}',
});
const caseIdPattern = new sns.CustomDataIdentifier({
name: 'CaseID',
regex: 'CASE-[0-9]{7}',
});
const accountIdPattern = new sns.CustomDataIdentifier({
name: 'AccountID',
regex: 'ACC-[0-9]{8}',
});
const productCodePattern = new sns.CustomDataIdentifier({
name: 'ProductCode',
regex: 'PROD-[A-Z]{2}-[0-9]{4}',
});
const contractIdPattern = new sns.CustomDataIdentifier({
name: 'ContractID',
regex: 'CNTR-[0-9]{6}-[A-Z]{2}',
});
const invoiceIdPattern = new sns.CustomDataIdentifier({
name: 'InvoiceID',
regex: 'INV-[0-9]{8}',
});
const ticketIdPattern = new sns.CustomDataIdentifier({
name: 'TicketID',
regex: 'TKT-[A-Z]{3}-[0-9]{6}',
});
// Create a comprehensive data protection policy
const dataProtectionPolicy = new sns.DataProtectionPolicy({
name: 'ComprehensiveProtectionPolicy',
description:
'Complete example of all SNS Message Data Protection capabilities',
statements: [
// Statement 1: Audit for all PII data types
new sns.DataProtectionPolicyStatement({
sid: 'AuditAllPII',
dataDirection: sns.DataDirection.INBOUND,
dataIdentifiers: [
// Basic personal identifiers
sns.PersonalIdentifiers.NAME,
sns.PersonalIdentifiers.EMAIL_ADDRESS,
sns.PersonalIdentifiers.VEHICLE_IDENTIFICATION_NUMBER,
sns.PersonalIdentifiers.ADDRESS,
// Drivers license across multiple regions
sns.PersonalIdentifiers.driversLicense('US'),
sns.PersonalIdentifiers.driversLicense('GB'),
sns.PersonalIdentifiers.driversLicense('CA'),
sns.PersonalIdentifiers.driversLicense('AU'),
sns.PersonalIdentifiers.driversLicense('FR'),
sns.PersonalIdentifiers.driversLicense('DE'),
sns.PersonalIdentifiers.driversLicense('IT'),
sns.PersonalIdentifiers.driversLicense('ES'),
// Phone numbers across multiple regions
sns.PersonalIdentifiers.phoneNumber('US'),
sns.PersonalIdentifiers.phoneNumber('GB'),
sns.PersonalIdentifiers.phoneNumber('FR'),
sns.PersonalIdentifiers.phoneNumber('DE'),
sns.PersonalIdentifiers.phoneNumber('IT'),
sns.PersonalIdentifiers.phoneNumber('ES'),
sns.PersonalIdentifiers.phoneNumber('BR'),
// Passport numbers across multiple regions
sns.PersonalIdentifiers.passportNumber('US'),
sns.PersonalIdentifiers.passportNumber('GB'),
sns.PersonalIdentifiers.passportNumber('CA'),
sns.PersonalIdentifiers.passportNumber('DE'),
sns.PersonalIdentifiers.passportNumber('FR'),
sns.PersonalIdentifiers.passportNumber('IT'),
sns.PersonalIdentifiers.passportNumber('ES'),
// SSNs across regions
sns.PersonalIdentifiers.ssn('US'),
sns.PersonalIdentifiers.ssn('ES'),
],
operation: new sns.AuditOperation({
sampleRate: 99,
findingsDestination: {
cloudWatchLogs: {
logGroup: logGroup.logGroupName,
},
},
}),
}),
// Statement 2: Mask financial information
new sns.DataProtectionPolicyStatement({
sid: 'MaskFinancialData',
dataDirection: sns.DataDirection.INBOUND,
dataIdentifiers: [
// Credit card related
sns.FinancialIdentifiers.CREDIT_CARD_NUMBER,
sns.FinancialIdentifiers.CREDIT_CARD_EXPIRATION,
sns.FinancialIdentifiers.CREDIT_CARD_CVV,
// Bank accounts across multiple regions
sns.FinancialIdentifiers.bankAccountNumber('US'),
sns.FinancialIdentifiers.bankAccountNumber('GB'),
sns.FinancialIdentifiers.bankAccountNumber('DE'),
sns.FinancialIdentifiers.bankAccountNumber('FR'),
sns.FinancialIdentifiers.bankAccountNumber('IT'),
sns.FinancialIdentifiers.bankAccountNumber('ES'),
],
operation: new sns.MaskOperation({
maskWithCharacter: '#',
}),
}),
// Statement 3: Redact custom organization-specific patterns
new sns.DataProtectionPolicyStatement({
sid: 'RedactCustomIdentifiers',
dataDirection: sns.DataDirection.INBOUND,
dataIdentifiers: [
employeeIdPattern,
projectCodePattern,
customerIdPattern,
orderIdPattern,
caseIdPattern,
],
operation: new sns.RedactOperation(),
}),
// Statement 4: Additional redact statement for remaining custom identifiers
new sns.DataProtectionPolicyStatement({
sid: 'RedactMoreCustomIdentifiers',
dataDirection: sns.DataDirection.INBOUND,
dataIdentifiers: [
accountIdPattern,
productCodePattern,
contractIdPattern,
invoiceIdPattern,
ticketIdPattern,
],
operation: new sns.RedactOperation(),
}),
// Statement 5: Block messages containing credentials
new sns.DataProtectionPolicyStatement({
sid: 'BlockCredentials',
dataDirection: sns.DataDirection.INBOUND,
dataIdentifiers: [
// All credential types
sns.CredentialsIdentifiers.AWS_SECRET_KEY,
sns.CredentialsIdentifiers.OPENSSH_PRIVATE_KEY,
sns.CredentialsIdentifiers.PGP_PRIVATE_KEY,
sns.CredentialsIdentifiers.PRIVATE_KEY,
sns.CredentialsIdentifiers.PUTTY_PRIVATE_KEY,
],
operation: new sns.DenyOperation(),
}),
// Statement 6: Block messages containing device information
new sns.DataProtectionPolicyStatement({
sid: 'BlockDeviceInfo',
dataDirection: sns.DataDirection.INBOUND,
dataIdentifiers: [sns.DeviceIdentifiers.IP_ADDRESS],
operation: new sns.DenyOperation(),
}),
// Statement 7: Mask health information in outbound messages
new sns.DataProtectionPolicyStatement({
sid: 'MaskHealthData',
dataDirection: sns.DataDirection.OUTBOUND,
dataIdentifiers: [
// US health identifiers
sns.HealthIdentifiers.DRUG_ENFORCEMENT_AGENCY_NUMBER_US,
sns.HealthIdentifiers.HEALTHCARE_PROCEDURE_CODE_US,
sns.HealthIdentifiers.HEALTH_INSURANCE_CLAIM_NUMBER_US,
sns.HealthIdentifiers.MEDICARE_BENEFICIARY_NUMBER_US,
sns.HealthIdentifiers.NATIONAL_DRUG_CODE_US,
sns.HealthIdentifiers.NATIONAL_PROVIDER_ID_US,
// EU/FR health identifiers
sns.HealthIdentifiers.HEALTH_INSURANCE_CARD_NUMBER_EU,
sns.HealthIdentifiers.HEALTH_INSURANCE_NUMBER_FR,
// GB health identifiers
sns.HealthIdentifiers.NATIONAL_INSURANCE_NUMBER_GB,
sns.HealthIdentifiers.NHS_NUMBER_GB,
// CA health identifiers
sns.HealthIdentifiers.PERSONAL_HEALTH_NUMBER_CA,
],
operation: new sns.MaskOperation({
maskWithCharacter: '*',
}),
}),
],
});
// Create an SNS topic with the comprehensive data protection policy
const protectedTopic = new sns.Topic(this, 'ProtectedTopic', {
topicName: 'protected-data-topic',
dataProtectionPolicy: dataProtectionPolicy,
});
// Output the topic ARN
new cdk.CfnOutput(this, 'ProtectedTopicArn', {
value: protectedTopic.topicArn,
description: 'ARN of the SNS topic with comprehensive data protection',
exportName: 'ProtectedTopicArn',
});
Checklist
- [x] My code adheres to the CONTRIBUTING GUIDE and DESIGN GUIDELINES
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license
AWS CodeBuild CI Report
- CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
- Commit ID: 68c9086649cf8a1294fc14c4a40638cfc75c949f
- Result: SUCCEEDED
- Build Logs (available for 30 days)
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository