Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
SecurityJune 21, 20264 min read

Secure file uploads from the ground up: A developer's guide

Secure file uploads from the ground up require more than basic validation. Learn how to prevent RCE and directory traversal in your production systems.

SecurityWeb DevelopmentBackendNode.jsInfrastructureDevSecOpsWeb
A close-up of CDs and disks on a desk, featuring hands in a tech environment.

Last month, I spent about two days refactoring an image-processing service that had been quietly failing its security audits. The original implementation was a classic "accept and save" pattern: it checked the file extension, ignored everything else, and dumped bits into a public-facing directory. It’s the kind of technical debt that keeps you up at night once you realize how trivial it is to drop a malicious script into a web-accessible folder.

Why Secure file uploads from the ground up matter

When you build a feature that lets users send data to your server, you are essentially opening a door. If you don't build a proper security perimeter, that door becomes an entry point for remote code execution (RCE) or a way to overwrite system files. Most developers focus on the "happy path"—uploading a JPEG, generating a thumbnail, and moving on. But production-grade systems require a defensive mindset.

We initially tried to solve this by just checking the MIME type using standard library headers. It failed immediately because a user simply renamed a .php file to .jpg, and our server blindly trusted the client-side metadata. We had to pivot to a multi-layered verification strategy that treats every byte of incoming data as hostile.

The defensive checklist

To implement secure file uploads from the ground up, you need to stop trusting the client entirely. Here is the stack I now use for every project:

  1. Filename Sanitization: Never use the filename provided by the user. If you do, you're inviting directory traversal attacks. Use a UUID or a hash of the file contents to rename the file before it hits the disk.
  2. Magic Number Validation: Don't rely on extensions. Use a library like filetype (in Go) or python-magic to inspect the file header (the "magic bytes"). This confirms the file is actually an image, not a script masquerading as one.
  3. Isolated Storage: Never store user-uploaded files in the same partition or directory as your source code. Use an object storage service like AWS S3 or a dedicated, unprivileged volume.
  4. Size Limits: Always enforce strict Content-Length limits at the Nginx or application level to prevent DoS attacks that fill up your disk.

A pattern for implementation

When I'm writing the handler, I usually structure the logic to fail fast. Here is a simplified example of how we handle this in a Node.js environment:

JAVASCRIPT
const crypto = require(CE9178">'crypto');
const fileType = require(CE9178">'file-type');

async function handleUpload(req, res) {
  const buffer = req.file.buffer; // Assuming memory storage
  const type = await fileType.fromBuffer(buffer);

  // 1. Validate against a strict allow-list
  const allowed = [CE9178">'image/jpeg', CE9178">'image/png'];
  if (!type || !allowed.includes(type.mime)) {
    return res.status(400).send(CE9178">'Invalid file type');
  }

  // 2. Generate a secure, random filename
  const filename = crypto.randomBytes(16).toString(CE9178">'hex');
  
  // 3. Save to an isolated, non-executable directory
  await saveToStorage(filename, buffer);
}

By decoupling the file's original name from the storage key, you eliminate the risk of a user uploading ../../../etc/passwd.

Keeping the environment clean

Managing the infrastructure around these uploads is just as important as the code itself. If you're using secrets to authenticate with your storage bucket, ensure you aren't hardcoding them. I’ve found that using HashiCorp Vault and External Secrets Operator: Secure Kubernetes Secrets is the most reliable way to handle these credentials. It keeps your storage access keys out of your environment variables and directly within your cluster's secret management lifecycle.

Once the files are stored, remember that your CI/CD pipeline should also be auditing your dependencies. If you use a third-party library to process images (like ImageMagick), you need to ensure it's patched. I often reference DevSecOps: Secure CI/CD Pipelines with Snyk and GitHub Actions to keep my automated scans running against these specific vulnerabilities.

What I’m still thinking about

Even with these layers, there's always a trade-off. For example, if you allow users to upload PDFs, you're opening a much larger attack surface because of the complexity of PDF parsers. We've considered running uploads through a "sanitization" container—a completely isolated, ephemeral environment—before moving them to long-term storage. It adds around 280ms to the latency, but for high-risk applications, it’s worth the overhead.

If I were starting this project over, I’d probably lean even harder into serverless functions for the validation step. Offloading the file inspection to a Lambda function ensures that even if the validation logic is compromised, the attacker is stuck in a highly restricted runtime environment.

Secure file uploads from the ground up aren't about building a perfect fortress; they're about making the cost of an attack higher than the value of the exploit. Don't be discouraged if your first pass at a secure uploader feels overly complicated. It’s supposed to be.

Back to Blog

Similar Posts

A close-up of a padlock securing a wire fence, symbolizing protection and safety.
SecurityJune 21, 20264 min read

Preventing IDOR vulnerabilities in Laravel with attribute-based access control

Prevent IDOR vulnerabilities in your Laravel apps by using attribute-based access control in Eloquent models. Secure your data with clean, automated logic.

Read more
Close-up of colorful programming code displayed on a computer screen, showcasing modern coding concepts.
Security
June 20, 2026
4 min read

XSS prevention strategies: A guide for modern web developers

XSS prevention strategies are essential for securing modern web apps. Learn to identify the three main variants and implement robust, layered defenses today.

Read more
Three syringes arranged on a red surface showcasing medical equipment with copy space.
SecurityJune 20, 20264 min read

Preventing SQL injection in modern frameworks: A practical guide

Preventing SQL injection in modern frameworks starts with understanding how ORMs and parameterized queries protect your database. Learn how to secure your code.

Read more