Malicious files uploaded to your Amazon S3 bucket? You protect your S3 bucket, right? Right!?
To protect against this, it was usually necessary to setting up secure staging buckets, using anti-virus and anti-malware scanning software, and managing a data pipeline and processing architecture.
In this blog post, I explain how to detect malicious file uploads to your Amazon S3 bucket and how you can take action to isolate or eliminate any malware found.
My code examples are written in TypeScript. I use the AWS Cloud Development Kit (CDK), which allows you to define your cloud infrastructure as code in any of the supported programming languages.
Amazon GuardDuty Malware Protection for S3
AWS released Amazon GuardDuty Malware Protection for S3 at re:Inforce 2024.
It's an advanced security feature that extends the capabilities of Amazon GuardDuty. GuardDuty is an intelligent threat detection service that continuously monitors for malicious activity and unauthorized behavior to protect your AWS accounts, workloads, and data. With the addition of Malware Protection for S3, GuardDuty offers comprehensive protection for your S3 buckets.
Amazon GuardDuty Malware Protection uses multiple AWS developed and industry-leading third-party malware scanning engines to provide malware detection.
Code example
For the sake of completeness, I create a customer-managed key and an S3 bucket. If these already exist, you can skip this step.
import { aws_kms, aws_s3 } from 'aws-cdk-lib';
const kmsKey = new aws_kms.Key(this, 'ExampleKmsKey');
const bucket = new aws_s3.Bucket(this, 'ExampleBucket', {
bucketKeyEnabled: true,
encryption: aws_s3.BucketEncryption.KMS,
encryptionKey: kmsKey,
});
To enable Malware Protection for S3 to scan and tag your S3 objects, you can use service roles that have the necessary permissions to perform malware scanning actions on your behalf.
The following permissions are required to perform the malware scan:
- Allow Amazon EventBridge actions to create and manage the EventBridge managed rule so that Malware Protection for S3 can listen to your S3 object notifications.
- Allow Amazon S3 and EventBridge actions to send notiο¬cation to EventBridge for all events in this bucket.
- Allow Amazon S3 actions to access the uploaded S3 object.
- Allow KMS key actions to access the object.
First, create the IAM policy. Don't be surprised about the DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3
EventBridge rules, these are managed by AWS.
import { aws_iam, aws_kms, aws_s3 } from 'aws-cdk-lib';
...
const policy = new aws_iam.Policy(
this,
'GuardDutyMalwareProtectionRolePolicy',
{
statements: [
new aws_iam.PolicyStatement({
sid: 'AllowManagedRuleToSendS3EventsToGuardDuty',
effect: aws_iam.Effect.ALLOW,
actions: [
'events:PutRule',
'events:DeleteRule',
'events:PutTargets',
'events:RemoveTargets',
],
resources: [
`arn:aws:events:${this.region}:${this.account}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*`,
],
conditions: {
StringLike: {
'events:ManagedBy':
'malware-protection-plan.guardduty.amazonaws.com',
},
},
}),
new aws_iam.PolicyStatement({
sid: 'AllowGuardDutyToMonitorEventBridgeManagedRule',
effect: aws_iam.Effect.ALLOW,
actions: ['events:DescribeRule', 'events:ListTargetsByRule'],
resources: [
`arn:aws:events:${this.region}:${this.account}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*`,
],
}),
new aws_iam.PolicyStatement({
sid: 'AllowPostScanTag',
effect: aws_iam.Effect.ALLOW,
actions: [
's3:PutObjectTagging',
's3:GetObjectTagging',
's3:PutObjectVersionTagging',
's3:GetObjectVersionTagging',
],
resources: [bucket.arnForObjects('*')],
}),
new aws_iam.PolicyStatement({
sid: 'AllowEnableS3EventBridgeEvents',
effect: aws_iam.Effect.ALLOW,
actions: ['s3:PutBucketNotification', 's3:GetBucketNotification'],
resources: [bucket.bucketArn],
}),
new aws_iam.PolicyStatement({
sid: 'AllowPutValidationObject',
effect: aws_iam.Effect.ALLOW,
actions: ['s3:PutObject'],
resources: [
`${bucket.bucketArn}/malware-protection-resource-validation-object`,
],
}),
new aws_iam.PolicyStatement({
sid: 'AllowCheckBucketOwnership',
effect: aws_iam.Effect.ALLOW,
actions: ['s3:ListBucket', 's3:GetBucketLocation'],
resources: [bucket.bucketArn],
}),
new aws_iam.PolicyStatement({
sid: 'AllowMalwareScan',
effect: aws_iam.Effect.ALLOW,
actions: ['s3:GetObject', 's3:GetObjectVersion'],
resources: [bucket.arnForObjects('*')],
}),
new aws_iam.PolicyStatement({
sid: 'AllowDecryptForMalwareScan',
effect: aws_iam.Effect.ALLOW,
actions: ['kms:GenerateDataKey', 'kms:Decrypt'],
resources: [kmsKey.keyArn],
conditions: {
StringLike: {
'kms:ViaService': `s3.${this.region}.amazonaws.com`,
},
},
}),
],
},
);
Once the IAM policy is defined, the IAM role can be created and then added to the policy.
import { aws_iam, aws_kms, aws_s3 } from 'aws-cdk-lib';
...
const role = new aws_iam.Role(this, 'GuardDutyMalwareProtectionPassRole', {
assumedBy: new aws_iam.ServicePrincipal(
'malware-protection-plan.guardduty.amazonaws.com',
),
description:
'An iam pass role for guardduty malware protection service to assume',
});
policy.attachToRole(role);
Finally, create a new malware protection plan for the protected resource. The tagging of S3 objects is also enabled. More on this in a moment.
import { aws_guardduty, aws_iam, aws_kms, aws_s3 } from 'aws-cdk-lib';
...
const malwareProtectionPlan = new aws_guardduty.CfnMalwareProtectionPlan(
this,
'GuardDutyMalwareProtection',
{
protectedResource: {
s3Bucket: {
bucketName: bucket.bucketName,
},
},
role: role.roleArn,
actions: {
tagging: {
status: 'ENABLED',
},
},
},
);
Everything looks good - the deployment is running and then suddenly fails π€―
The request was rejected because provided IAM role does not have the required permissions to validate S3 bucket ownership
Sometimes the AWS CloudFormation stack is attempting to create the CfnMalwareProtectionPlan
resource before the IAM role has been fully created and propagated across AWS services. You can fix the problem by defining the order of the dependencies.
malwareProtectionPlan.node.addDependency(policy);
The deployment should now be successful and GuardDuty is ready to use. π
Tags
Now you can upload an object to the S3 bucket. After a short time, the object should be tagged. Each newly uploaded object that is scanned, will have an associated tag in the following key-value pair format:
GuardDutyMalwareScanStatus:Scan-Result-Status
For more information about the tag values, see π S3 object potential scan status and result status.
Take action
There are several ways to proceed from here. I would like to briefly present two possible solutions.
Tag-based access control (TBAC)
Because the S3 objects are tagged, a tag-based Access Control (TBAC) resource policy can be used.
As long as an object does not have a tag or the tag does not match GuardDutyMalwareScanStatus:NO_THREATS_FOUND
, access to it can be prevented.
For more information, see π Using tag-based access control (TBAC) with Malware Protection for S3.
Copy S3 objects
Another solution would be to copy the S3 objects that have been scanned by Amazon GuardDuty to a different S3 bucket.
An EventBridge rule is configured to listen for events that match a scan result of NO_THREATS_FOUND
. A Lambda function is then called to copy the S3 object to another S3 bucket.
For more information, see π Amazon GuardDuty copy S3 object solution overview.
If you have any kind of feedback, suggestions or ideas - feel free to comment this post!
Top comments (0)