Safeguard
Application Security•

CWE-140 Improper Neutralization of Delimiters: Causes and Fixes

Unescaped field separators, line breaks and terminators let attackers add records or privileges. Learn the pattern, how to test for it and how to fix it.

Safeguard Security Team
4 min read

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.

  1. Use a real serializer for the format, such as a CSV library, a JSON encoder or a database API, instead of joining strings.
  2. If you must write the format yourself, escape or quote delimiters the way the format specifies, and escape the escape character too.
  3. Reject control characters, especially CR, LF and null, in fields where they have no purpose.
  4. Validate values against an allow list of expected characters and lengths.
  5. 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.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.