Uploading files is a feature that almost every application eventually needs. It might be profile pictures, invoices, PDF documents or spreadsheets. Regardless of the file type, there's a good chance those uploads will end up in an Amazon S3 bucket.
That's exactly how most applications are designed, and for good reason. Amazon S3 is durable, highly available and integrates naturally with almost every service in AWS.
While I was experimenting with some AWS security services, I realized there was one part of this workflow that I had never explored in depth.
What happens if one of those uploaded files contains malware?
Amazon S3 will store the object as usual because that's exactly what it's supposed to do. Its responsibility is storing data, not inspecting its contents.
If malware detection is required, that responsibility needs to be implemented somewhere else. That's what led me to Amazon GuardDuty Malware Protection for S3.
At first, I thought I was simply going to test another AWS security feature. After finishing the lab, I realized the most interesting part wasn't the malware scan itself. It was how easily the scan result could become the starting point for an automated remediation workflow.
In this article, we'll build that workflow from beginning to end.
We'll configure Amazon GuardDuty Malware Protection for S3, validate it using the EICAR test file and then automate the response using Amazon EventBridge, AWS Lambda and Amazon SNS. By the end of the lab, malicious files will be automatically moved into a quarantine bucket without any manual intervention.
What you'll build
The final solution consists of a simple but practical serverless security pipeline. Whenever a new file is uploaded to Amazon S3, GuardDuty automatically scans the object for malware. If a threat is detected, Amazon EventBridge receives the GuardDuty finding and invokes an AWS Lambda function.
The Lambda function then moves the suspicious file into a dedicated quarantine bucket and publishes a notification through Amazon SNS. Instead of simply detecting malware, the automation is capable of responding automatically.
The complete architecture looks like this:
This is a basic architecture, but it demonstrates a pattern that can easily be expanded in production environments by integrating services such as AWS Security Hub, AWS Step Functions or even external incident response platforms.
Prerequisites
To reproduce this lab, you'll need:
- An AWS account
- Amazon GuardDuty enabled
- Two Amazon S3 buckets
- One upload bucket
- One quarantine bucket
- Basic familiarity with AWS Lambda
- An Amazon SNS topic
- The EICAR test file for validation
The implementation itself is straightforward and doesn't require provisioning any infrastructure for malware scanning. That's one of the biggest advantages of using GuardDuty Malware Protection instead of managing your own antivirus servers.
Understanding the problem
Before configuring any AWS service, it's worth understanding why malware scanning matters in the first place.
Imagine an application where customers upload documents. The upload itself isn't the problem. The problem starts after the upload.
That same file might later be downloaded by another customer, processed by a Lambda function, indexed by a search engine or imported into another internal system.
If the uploaded object contains malware, every workload interacting with that file becomes part of the attack surface. This isn't a weakness in Amazon S3.
Amazon S3 is doing exactly what it was designed to do. It stores objects, but it doesn't inspect whether those objects are malicious. That responsibility belongs to the application or to another security layer built around it. This is exactly the gap GuardDuty Malware Protection for S3 helps fill.
How GuardDuty Malware Protection works
Before building this lab, I had a completely different expectation of how GuardDuty worked.
I assumed the workflow would be something like this:
- A malicious file is uploaded.
- GuardDuty detects the malware.
- GuardDuty removes the file from the bucket.
That isn't what happens.
GuardDuty performs the malware scan after the object is uploaded and records the result of the analysis. The important detail is that the object remains inside the bucket.
- Nothing is deleted.
- Nothing is moved.
- Nothing is blocked.
Initially, I thought this was a limitation. After spending some time testing the feature, I changed my mind.
Imagine an online retail store receiving customer documents or a hospital receiving medical exams. Automatically deleting uploaded files could remove evidence that may be required later for investigations, audits or compliance purposes.
Instead of forcing a remediation strategy, GuardDuty focuses exclusively on detection. Everything that happens after malware is detected is left to the customer.
That design turns out to be much more flexible because every organization has different security requirements.
Configuring GuardDuty Malware Protection
With the architecture in mind, it's time to configure the service. I started by creating two S3 buckets. The first bucket receives every uploaded file from the application. The second bucket will later be used to isolate malicious files detected during the malware scan.
Keeping the quarantine bucket separate makes the remediation process much safer because downstream applications only interact with files that remain in the original upload bucket.
Once the buckets were created, I opened the Amazon GuardDuty console and enabled Malware Protection for Amazon S3.
The setup wizard is relatively simple, but there are two configuration options that deserve attention.
The first is the IAM service role.
Unless your organization has a custom permission model, allowing AWS to create the required service role is usually the simplest option.
The second is Object Tagging.
This option is easy to overlook because it looks like just another checkbox during the configuration process. In practice, it's one of the most important parts of the entire solution.
Every time GuardDuty finishes scanning an object, it writes the result directly into the object's tags. That means your applications don't need to query GuardDuty APIs to understand what happened.
Instead, they can simply inspect the object's metadata and immediately know whether the file is clean or malicious. That small detail becomes the foundation for the automation we'll build in the next section.
At this point, the automation is already capable of detecting malware. The only thing missing is deciding what should happen after a threat is found.
Validating the solution
With GuardDuty Malware Protection configured, it's time to verify that the service is working as expected.
Instead of using a real malicious file, we'll use the EICAR test file, an standard file designed to safely simulate malware detection without posing any security risk.
After downloading the EICAR file, simply upload it to your S3 upload bucket.
Within a few moments, GuardDuty scans the object and generates a malware finding.

At this stage, GuardDuty has successfully detected the malicious file. However, notice that the object is still stored in the original bucket.
This is expected behavior. GuardDuty focuses on detection, leaving remediation decisions to your application or security workflow.
Building the remediation workflow
To automate the response, I created a simple serverless workflow using Amazon EventBridge, AWS Lambda and Amazon SNS.
Whenever GuardDuty identifies a malicious object, it publishes an event to EventBridge. That event triggers a Lambda function responsible for handling the remediation process.
The Lambda performs four actions:
- Verifies that the scan result is
THREATS_FOUND. - Copies the object to a dedicated quarantine bucket.
- Deletes the original object.
- Publishes an Amazon SNS notification.
The function uses two environment variables:
| Variable | Description |
|---|---|
QUARANTINE_BUCKET |
Destination bucket for malicious files |
SNS_TOPIC_ARN |
SNS topic used to notify the security team |
Below is the Lambda function used in this lab.
import boto3
import json
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
s3 = boto3.client('s3')
sns = boto3.client('sns')
def lambda_handler(event, context):
logger.info(f"Evento recebido: {json.dumps(event)}")
try:
detail = event['detail']
scan_status = detail['scanResultDetails']['scanResultStatus']
bucket = detail['s3ObjectDetails']['bucketName']
key = detail['s3ObjectDetails']['objectKey']
logger.info(f"Arquivo: {key} | Bucket: {bucket} | Status: {scan_status}")
if scan_status != 'THREATS_FOUND':
logger.info("Nenhuma ameaça encontrada. Nenhuma ação necessária.")
return {
'statusCode': 200,
'body': f'Arquivo limpo: {key}'
}
quarantine_bucket = os.environ['QUARANTINE_BUCKET']
sns_topic_arn = os.environ['SNS_TOPIC_ARN']
quarantine_key = f"quarentena/{key}"
# Copia para bucket de quarentena
logger.info(f"Copiando {key} para quarentena...")
s3.copy_object(
Bucket=quarantine_bucket,
CopySource={'Bucket': bucket, 'Key': key},
Key=quarantine_key,
TaggingDirective='COPY'
)
# Deleta do bucket original
logger.info(f"Deletando {key} do bucket original...")
s3.delete_object(Bucket=bucket, Key=key)
# Monta mensagem de alerta
threats = detail.get('scanResultDetails', {}).get('threats', [])
threat_names = [t.get('name', 'Desconhecido') for t in threats]
mensagem = f"""
⚠️ ALERTA DE SEGURANÇA — Malware Detectado
Arquivo: {key}
Bucket de origem: {bucket}
Ameaças identificadas: {', '.join(threat_names) if threat_names else 'Não especificado'}
Ação tomada: Arquivo movido para quarentena
Local na quarentena: s3://{quarantine_bucket}/{quarantine_key}
Este alerta foi gerado automaticamente pelo pipeline de proteção contra malware.
""".strip()
# Publica no SNS
logger.info("Publicando alerta no SNS...")
sns.publish(
TopicArn=sns_topic_arn,
Subject='⚠️ [ALERTA] Malware detectado no S3',
Message=mensagem
)
logger.info("Pipeline de quarentena executado com sucesso.")
return {
'statusCode': 200,
'body': f'Arquivo {key} movido para quarentena com sucesso.'
}
except KeyError as e:
logger.error(f"Campo ausente no evento: {e}")
raise
except Exception as e:
logger.error(f"Erro no pipeline de quarentena: {e}")
raise
One implementation detail worth highlighting is that the object is copied using:
TaggingDirective='COPY'
This preserves the tags added by GuardDuty, allowing future workflows or investigations to access the original malware scan information.
Connecting EventBridge
The final step is creating an EventBridge rule that listens for GuardDuty Malware Protection findings.
Whenever a malicious object is detected, EventBridge automatically invokes the Lambda function.
At this point, the entire remediation process is fully automated without requiring any manual intervention.
End to end validation
With all the components in place, it's time to test the complete workflow. I uploaded the EICAR test file to the S3 upload bucket once again.
A few moments later, GuardDuty detected the file and published an event to Amazon EventBridge. The Lambda function was automatically invoked, moved the object to the quarantine bucket and sent an SNS notification.
The original upload bucket was left clean, while the malicious file was safely isolated for further analysis.
From the user's perspective, the entire process happened automatically without any manual intervention.
Possible improvements
Although this lab demonstrates a basic malware detection and remediation workflow, there are several ways to extend it depending on your requirements.
Some ideas include:
- Sending notifications to Slack or Microsoft Teams.
- Creating AWS Security Hub findings for centralized visibility.
- Triggering AWS Step Functions for approval workflows.
- Storing remediation logs in Amazon DynamoDB.
- Building Amazon CloudWatch dashboards to monitor malware detections over time.
Because the solution is event driven, adding new actions doesn't require changing the GuardDuty configuration. New consumers can simply subscribe to the events generated during the workflow.
Cost considerations
One of the aspects I liked most about this architecture is that it relies almost entirely on managed services.
There are no virtual machines to maintain, antivirus databases to update or scanning infrastructure to provision.
For environments with occasional uploads, the operational overhead remains very low, making this approach suitable for prototypes, development environments and many production workloads.
As always, remember to review the current pricing for Amazon GuardDuty, AWS Lambda, Amazon EventBridge, Amazon SNS and Amazon S3 before deploying the solution in your own AWS account.
Conclusion
Before building this lab, I expected Amazon GuardDuty Malware Protection to automatically quarantine malicious files.
Instead, I discovered that GuardDuty focuses on detection, leaving remediation decisions to the customer. After implementing the automation, that design choice made much more sense.
Every organization has different security requirements. Some may want to quarantine files, others may notify a security team, while others might integrate with ticketing systems or incident response platforms.
By combining Amazon GuardDuty, Amazon EventBridge, AWS Lambda and Amazon SNS, it's possible to build a flexible remediation workflow without managing antivirus servers or additional infrastructure.
I also found that the implementation required surprisingly little code. Most of the solution comes from connecting AWS managed services rather than building complex application logic.
Hopefully this walkthrough gives you a practical starting point for adding automated malware detection and response to your own Amazon S3 workloads.









Top comments (0)