arrow_backBack to Knowledge Base
Cloud Architecture·Aug 05, 2025·10 MIN READ

Designing Secure File Upload Systems on AWS

Implementing virus scanning, pre-signed URLs, strict bucket policies, and MIME validation to prevent malicious payloads in S3.

awss3securityiam

01.Never Upload Through Your Server

Routing file uploads through your API server is a performance anti-pattern and a security risk. Instead, issue pre-signed S3 URLs directly to the client. The client uploads directly to S3, your server never touches the raw bytes, and you get fine-grained control over what can be uploaded.

typescript
const command = new PutObjectCommand({
  Bucket: process.env.S3_BUCKET,
  Key: `uploads/${userId}/${uuid()}.pdf`,
  ContentType: 'application/pdf',
  ContentLength: fileSize, // enforce size limit
});
const url = await getSignedUrl(s3Client, command, { expiresIn: 300 });

02.MIME Validation — Don't Trust the Extension

File extensions are user-controlled and meaningless for security. Always validate the actual MIME type by inspecting the file's magic bytes (first 8 bytes). A file named 'invoice.pdf' could contain an executable. Use the 'file-type' library to detect real MIME types server-side.

03.Virus Scanning With ClamAV on Lambda

Run ClamAV on a Lambda function triggered by S3 object creation events. If the scan finds a threat, delete the object immediately and notify the uploader. If clean, move the file to a 'clean' prefix and make it accessible. Never serve files that haven't been scanned.

  • Trigger: S3 ObjectCreated event → Lambda
  • Scan with ClamAV (packaged in Lambda layer)
  • Tag object: scan-status=CLEAN or scan-status=INFECTED
  • Infected files deleted within milliseconds of upload

04.Bucket Policy — Least Privilege

Your S3 bucket should deny all public access by default. Access should only be granted via pre-signed URLs or through CloudFront with origin access identity. Never grant s3:GetObject to '*'. Separate buckets for uploads (write-only), processing (Lambda access), and clean files (CloudFront access).