AWS S3 · virus & malware scanning

Stop malware from
living in your S3 buckets.

Every file that lands in S3 gets scanned. Clean files move through. Infected files get quarantined. Plug it into Lambda, your app code, or anything that can make an HTTP call.

The pattern

It’s three steps.
Not an architecture diagram.

1

Upload

A file lands in S3 — however you already do uploads. Presigned URLs, direct PUTs, SDK uploads, Transfer Family. Whatever you use today.

2

Scan

AttachmentScanner checks the object against multiple commercial antivirus engines. Seconds for common file sizes. A single JSON verdict back.

3

Decide

Clean → approve (move to final bucket, notify the user). Infected → quarantine or delete. Your rules, with a full audit log.

Your choice of integration

Three ways to wire it up.
Pick whichever matches how you already run.

Serverless

Lambda function

You already have s3:ObjectCreated events firing. Drop in a small Lambda that calls AttachmentScanner, then moves the clean file to the final bucket (or quarantines the rest).

Best for: existing S3-event-driven flows.
Direct

Your app code

Call AttachmentScanner from the same handler that receives the upload. Scan before the file ever hits S3 — reject bad files at the edge instead of cleaning up after.

Best for: user uploads, form submissions, API endpoints.
Flexible

Anything that speaks HTTP

EventBridge, Step Functions, SQS consumers, a scheduled worker sweeping an existing bucket. It’s one POST request — anything that can make HTTP calls can drive it.

Best for: batch sweeps, orchestrated pipelines, bring-your-own runtime.
What you get

Clean buckets.
Audit-ready evidence.

Clean buckets, no exceptions

Every object that makes it to your production bucket has been scanned by multiple commercial antivirus engines. No silent passes, no “we scan most of them”.

Quarantine with an audit trail

Infected files go to a separate bucket, the origin gets deleted, and every verdict is logged. You know what was flagged, when, and by which engine.

Pen-test and audit ready

When the report asks for evidence of file-upload antivirus scanning, you have it — engine names, timestamps, request IDs. The things auditors actually ask for.

Multi-region, no cross-region egress

Scan in the region the bucket already lives in. No residency headaches, no surprise transfer costs. us-east-1, eu-west-1, ap-southeast-2.

Here’s what it looks like

The whole Lambda, in a dozen lines.

No custom runtime, no extra dependencies. Presign, POST, branch on the verdict. Examples in every major language are in the docs.

handler.js Node.js · AWS SDK v3
const presigned = await getSignedUrl(s3,
  new GetObjectCommand({ Bucket, Key }), { expiresIn: 900 });

const scan = await fetch(`${SCANNER_URL}/v1.0/scans`, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_TOKEN}` },
  body: JSON.stringify({ url: presigned }),
}).then(r => r.json());

if (scan.status === "found") await quarantine(Bucket, Key);
else await moveToFinal(Bucket, Key);
Full integration walkthrough — How to scan user uploads for malware or browse examples in Python, Go, Ruby, Elixir, .NET and PHP .
Regions

Scan where your bucket lives.

Regional clusters in three AWS regions. Dedicated clusters and AWS PrivateLink available on request for compliance workloads.

operational
United States
us-east-1
operational
Europe
eu-west-1
operational
Asia-Pacific
ap-southeast-2
Security

Fits your AWS security posture.

Scoped IAM

AttachmentScanner only needs a short-lived presigned GET URL per object. No bucket-wide access, no long-lived AWS credentials held by us.

No persistent copies

File bytes are discarded as soon as the scan finishes. We keep metadata (filename, hash, verdict, timestamp) for your scan history — not the file.

PrivateLink available

Keep scan traffic off the public internet for compliance workloads. Works cleanly with SSE-S3 and SSE-KMS without flattening your encryption story.

Full security overview →
Questions

Questions we get about S3.

01 Does it work with S3 Object Lock and versioning? +
Yes. Object Lock and versioning live at the bucket level; scanning happens on an individual object version. You generate a presigned URL for the version you want to scan and pass it in — the lock and retention state are untouched. A common pattern is to scan on upload, then apply or skip Object Lock based on the verdict.
02 How do I handle large files (>100MB)? +
Use async mode. Pass async: true plus a callback_url and we’ll POST the verdict back when scanning completes, so your Lambda doesn’t have to stay hot. For very large archives, scan on upload and gate downstream access on the async result rather than blocking the request.
03 Can I scan via a Lambda trigger on s3:ObjectCreated? +
That’s the recommended integration. Hook s3:ObjectCreated:* to a small Lambda that presigns a GET URL, calls /v1.0/scans, and copies the object into a clean bucket (or a quarantine bucket) based on the verdict. EventBridge works the same way if you prefer routing through an event bus.
04 Do you need bucket-wide access? +
No. Presigned URLs are the native fit. Your code generates a short-lived GET URL (we recommend 15 minutes) and passes it in the url field. AttachmentScanner fetches the object over HTTPS with just that URL — we never hold AWS credentials or need broader bucket permissions.
05 What happens to infected files? +
That part is yours to choose. The sync flow rejects the upload before it hits your production bucket: copy to quarantine (or just DeleteObject) and surface an error to the client. The async flow means the object is already stored by the time the verdict lands — your callback handler copies to a quarantine bucket, deletes the origin, revokes any shared links, and (typically) notifies affected users. Don’t silently delete: keep an audit record of what was flagged and why.

Scan every S3 upload.

14-day free trial. No credit card. Multi-engine scanning in three regions from day one.