File Upload Scripts

CGI file upload scripts: parsing, validating, and storing uploaded files safely

How does a CGI script handle file uploads from a browser?

A browser sends a file upload as a multipart/form-data POST request, which encodes each field and each file in a separate boundary-delimited section. A CGI script reads and parses this stream, extracts the filename and content, validates the file type and size, generates a safe storage name, and writes the file to a designated upload directory that is outside the web root or configured to prevent script execution.

Request custom development Getting started

What multipart/form-data means for a CGI script

When an HTML form includes an <input type='file'> element, the browser sets the form's enctype to multipart/form-data and sends a body where each part is separated by a unique boundary string. Each part carries its own Content-Disposition header naming the field and, for files, the original filename and MIME type. Parsing this format by hand is error-prone; use a library: CGI.pm in Perl parses it automatically when you call param(), and Python's cgi module does the same with FieldStorage.

The raw content-type header from the browser is informational and user-controlled, so never trust it as the only check on what was uploaded. A PNG with its first four bytes changed to PHP code is still a PHP script if your server is configured to execute anything in the upload directory. The MIME type the browser sends reflects the extension, not the actual file content.

Validating file types and sizes server-side

Check the file's magic bytes (the first few bytes of content) rather than relying on the browser-supplied MIME type or the filename extension. A JPEG starts with FF D8 FF; a PNG starts with 89 50 4E 47. If you open the first sixteen bytes of the upload and they do not match the expected signature for the types you allow, reject the upload before writing it to disk. Extensions and browser-reported types can be spoofed; magic bytes are harder to fake while keeping the file parseable by the target application.

Set a maximum file size and enforce it in the script, not just in the HTML form's accept attribute. The form attribute is a UI hint only; a script posting directly ignores it. Read Content-Length from the environment variable and reject anything over your limit before reading the body, or set a hard limit in your web server configuration to avoid reading a multi-gigabyte upload into memory.

Safe storage: directory permissions and naming

Store uploaded files outside the document root or in a directory explicitly configured to prohibit script execution. If a user uploads a Perl script disguised as an image and your server executes any file in the upload directory with the .pl extension, you have a remote code execution vulnerability. The safest configuration is a directory the web server user can write to but cannot serve directly, combined with a separate download script that reads and proxies the file.

Never use the original filename as the on-disk name. Filenames from browsers can contain path traversal sequences (../), null bytes, control characters, or names that conflict with existing files. Generate a server-side name: a UUID or a hash of the content plus a sanitized extension is common. Store the original filename separately in a database or metadata file if you need to preserve it for display.

Returning the uploaded file to users

If users need to retrieve uploaded files, write a download CGI script that reads the filename from a request parameter, looks it up in your metadata (never constructs the path directly from the parameter), checks permissions, and streams the file contents with the correct Content-Type and Content-Disposition headers. This keeps the actual storage path hidden and lets you add access control without changing how files are stored.

For public-facing files that do not need access control, it is simpler to serve them directly from a subdirectory, but ensure that directory is configured with Options -ExecCGI (Apache) or the equivalent to prevent execution of uploaded scripts. Confirm this with your host, since shared hosting configurations vary.

What to know

Key points

Get help

Listings and a local agent, when you are ready

Need a CGI script built, debugged, or a hosting environment configured? Forms use a clearly-marked placeholder endpoint until wired to a real system.

Hosting partner CGI-compatible hosting for file uploads

Reserved for an affiliate link to a vetted shared or VPS hosting provider that supports CGI and Perl/Python execution. Operator wires affiliate link here.

Development Need a custom CGI script built?

Self-hosted lead form for custom CGI development inquiries. Placeholder endpoint until wired to the operator's CRM or email handler.

Open inquiry form →

Custom CGI development

No spam. Your inquiry is answered within 1 business day.

Hosting setup help

No spam. Your inquiry is answered within 1 business day.

Questions

Frequently asked questions

How do I parse a file upload in a CGI script?
Use a library rather than parsing multipart/form-data manually. In Perl, CGI.pm's param() method handles file uploads and gives you a filehandle. In Python, cgi.FieldStorage parses the upload and lets you read the file content and filename. Both abstract away the boundary parsing, which is fiddly to get right by hand.
How do I prevent users from uploading malicious files?
Check the file's magic bytes to verify it matches the content type you expect. Store uploaded files outside the web root or in a directory configured to prohibit script execution. Generate a server-side filename rather than using the browser-supplied one. Set a file size limit. Never trust the browser-supplied MIME type alone, since it reflects the extension, not the actual content.
What is path traversal in file uploads?
Path traversal is when an attacker includes sequences like ../ in a filename to write the file to a directory outside the intended upload location, potentially overwriting server files. The fix is to strip directory separators and dot-dot sequences from the filename before using it, and better still to ignore the original filename entirely and generate your own server-side name.

CustomCGI publishes reference information on CGI programming and server-side web scripting for educational and informational purposes. Code examples are provided as-is for learning; always audit and test scripts in a staging environment before deploying to production. Security vulnerabilities in CGI scripts are a real risk; follow current best practices and review the security guide before deploying any user-facing code.