DEV Community

andreagomez
andreagomez

Posted on

Multipart Form Filenames Need One Parser and One Policy

Multipart Form Filenames Need One Parser and One Policy

multipart/form-data is the common way for a browser to send a file with a form. RFC 7578 defines parts, boundaries, field names, and an optional filename parameter. It also gives a clear instruction to receivers: do not use the file name blindly. Check it and resolve it against local storage conventions, and do not use directory path information that may be present.

The standard is careful because the filename is metadata supplied by the sender. It is not a trusted local path, and it is not a stable identifier. Deployed systems may send UTF-8 directly, percent-encoded names, or another charset associated with the form. RFC 7578's percent-encoding option and its compatibility notes create room for different producers to describe the same visible name in different bytes.

Parsing decisions happen in several places

A gateway may inspect the multipart body before an application framework parses it. A WAF may have a different boundary parser, filename decoder, or maximum part count. The application may use a streaming parser while the upload service uses a buffered parser. A request that one component treats as one file can be interpreted as several parts by another component.

The research on parser discrepancies in web application firewalls shows that this is not only a theoretical concern. The exact request may be accepted by a proxy and interpreted differently by the application. Security controls should not depend on two parsers agreeing about ambiguous syntax.

Choose one parser and one policy for names, sizes, part counts, and duplicate fields. Normalize the accepted field names before authorization. Reject duplicate file fields unless the endpoint's contract explicitly supports multiple files. Set limits before the parser writes to disk: total body size, per-part size, number of parts, header size, and processing time. A valid-looking upload should not be able to exhaust memory or fill a temporary directory.

The filename is presentation data

Store an internal file identifier generated by the application. Keep the original filename in a separate, quoted metadata field for display. Use a normalized extension only for content-type heuristics, and decide the real content type from approved inspection rather than the client-supplied part header. Never construct a filesystem path by concatenating an upload directory with the received filename.

If a user-facing download needs the original name, encode it in a Content-Disposition header using a library that follows the current quoting rules. Do not echo raw control characters, path separators, or Unicode bidi controls. Reject names that normalize to reserved device names, empty names, or names longer than the storage policy allows.

Test the boundaries

Send a part with no filename, a filename containing ../, a Windows drive path, a percent-encoded separator, a quoted semicolon, and a non-ASCII name. Send two parts with the same field name and different content types. Send a part whose declared content type conflicts with its bytes. Send a body that is one byte over each configured limit. Confirm that the gateway, application, and storage service all record the same decision.

The upload path should also have an audit record: the internal file ID, owning tenant, uploader, declared name, detected type, size, hash, and retention state. The filename can be useful evidence, but it cannot carry authorization. One parser and one policy make the boundary reviewable; silent normalization differences make it impossible to explain which file was actually accepted.

References

Top comments (0)