CWE-140, Improper Neutralization of Delimiters, applies when the product does not neutralize, or incorrectly neutralizes, delimiters. A delimiter is a character or sequence that separates parts of data: a comma, a pipe, a newline, a space, a boundary string. If an attacker can put one into a value, the next parser sees extra structure that the author never intended.
It is language-neutral. Any code that writes values into a flat file, a header, a log, a message or a protocol line can be affected. It is a child of the broader CWE-138.
How it happens
Imagine a user file where each line holds a name, a password hash and a role, separated by pipes. The code appends a new line from a signup form.
# Vulnerable: fields are joined without neutralizing the delimiter
def add_user(name, pw_hash):
with open("users.db", "a") as f:
f.write(name + "|" + pw_hash + "|user\n")
If the name is bob|x|admin\nmallory, the file now contains a record that grants an admin role. The parser trusted the delimiters it found, and it cannot tell which came from the program and which came from the attacker.
MITRE's observed examples follow exactly this shape.
- CVE-2003-0307: an attacker inserts a field separator into input to specify admin privileges.
- CVE-2001-0527: an attacker inserts carriage returns and pipe field separator characters to add a new user with privileges.
- CVE-2002-0267: a linebreak in a field of a PHP script allows admin privileges when written to a data file.
- CVE-2000-0293: multiple internal spaces and insufficient quoting, where the program does not use a proper delimiter between values.
MITRE splits the family by delimiter type. They include parameter and argument delimiters (CWE-141), value delimiters (CWE-142), record delimiters (CWE-143), line delimiters (CWE-144), section delimiters (CWE-145), expression and command delimiters (CWE-146), and input terminators (CWE-147).
What an attacker gains
MITRE lists the consequence as an integrity impact in the form of an unexpected state. In plain terms, the data takes on a structure that was not planned. As the examples show, that structure can include new records, changed fields and elevated privileges. Where the delimiter is a command separator, the impact can rise to code or command execution, which MITRE covers under CWE-146.
How to find it
The record has no detection section. These checks fit the pattern.
- List every place the application writes external values into a delimited format: CSV or TSV exports, flat files, config lines, mail headers, log lines, key-value protocols.
- Send each text field the delimiters of that format, such as a pipe, comma, quote, newline, carriage return and null byte, and read the output back.
- Confirm that a value containing a delimiter still parses as a single value.
- Review any code that builds structured output with string concatenation.
Click through it at your own pace, or open it full screen.
How to fix it
The record lists no mitigations. These are standard steps for this weakness.
- Use a real serializer for the format, such as a CSV library, a JSON encoder or a database API, instead of joining strings.
- If you must write the format yourself, escape or quote delimiters the way the format specifies, and escape the escape character too.
- Reject control characters, especially CR, LF and null, in fields where they have no purpose.
- Validate values against an allow list of expected characters and lengths.
- Keep privilege and role data out of files that user input is written into, or protect it with a separate signed or structured store.
# Fixed: a CSV writer quotes delimiters and newlines correctly
import csv
def add_user(name, pw_hash):
with open("users.csv", "a", newline="") as f:
csv.writer(f).writerow([name, pw_hash, "user"])
Quoting handles the parser, but an application should still validate that a name is a name. The role in the example is set by code, never taken from input.
Quick check
What does the attacker control in a delimiter attack?
They control a value, and by including a delimiter in it they change how the next parser splits the data, which can add fields or whole records.
Why is a serializer better than escaping by hand?
Serializers implement the format's quoting rules completely and are tested against edge cases. Hand-written escaping usually misses a character or a combination.