Q20 of 24 · Security

How do you test file upload functionality for security vulnerabilities?

SecuritySeniorsecurityfile-uploadpath-traversalcontent-typeowasptestingsenior

Short answer

Short answer: Test: uploading a file with a mismatched extension and Content-Type, an executable disguised as an image, a filename containing path traversal sequences, a file exceeding the size limit, and a file with malicious content (polyglot). Verify uploaded files are served from a separate non-executable domain or storage, not from the application server.

Detail

File upload is one of the richest attack surfaces in web applications. A QA test plan should cover:

Content-type validation:

  • Upload an executable (.php, .jsp, .exe, .sh) with the Content-Type set to image/jpeg.
  • Upload a valid-looking JPEG file that contains embedded executable content (a JPEG/PHP polyglot).
  • Assert: both are rejected, or are stored without execute permissions and served with a safe Content-Type.

Extension validation:

  • The server must validate the file extension server-side, not rely on the Content-Type header (which the client controls).
  • Bypass attempts: evil.php%00.jpg (null byte truncation — older servers may strip the .jpg suffix), evil.phtml, evil.PHP (case variation).

Filename path traversal:

  • Submit a filename containing ../../../etc/passwd or ..\..\Windows\System32\.
  • Assert: the server sanitises the filename and stores it safely, it does not save the file at the traversal path, and the response does not reveal server-side file system structure.

Size limits:

  • Upload a file that exceeds the configured maximum size.
  • Assert: rejected with a 413 Payload Too Large before server-side processing completes (important for DoS prevention — the server should not process a 10 GB file before checking the limit).

Storage validation:

  • After a successful upload, retrieve the file URL. Verify the URL does not serve from the application server (which might execute PHP/JSP files). Files should be served from a CDN or object storage (S3, GCS) with no execution capability.

// EXAMPLE

file-upload-security.test.ts

test('rejects executable files disguised as images', async ({ request }) => {
  const evilContent = Buffer.from('<?php system($_GET["cmd"]); ?>');
  const response = await request.post('/api/upload', {
    multipart: {
      file: {
        name: 'photo.jpg',
        mimeType: 'image/jpeg',
        buffer: evilContent,
      },
    },
  });
  // Must reject — not accept and serve
  expect([400, 415, 422]).toContain(response.status());
});

// WHAT INTERVIEWERS LOOK FOR

Covers content-type mismatch, extension bypass, path traversal, size limits, and storage validation as distinct test cases. Notes files must not be served from the application server.