Safeguard
Application Security•

CWE-116 Improper Output Encoding: The Root of Many Injections

Missing or wrong output encoding lets data change the structure of messages between components. CWE-116 explained, with context-aware fixes.

Safeguard Security Team
4 min read

CWE-116, Improper Encoding or Escaping of Output, sits above many familiar injection bugs. The product prepares a structured message for communication with another component, but encoding or escaping of the data is missing or done incorrectly, so the intended structure of the message is not preserved.

Every product that talks to a database, a browser, a shell, a mail server or another service is exposed. MITRE lists web servers, database servers and AI/ML among the platforms.

How it happens

MITRE gives a clear picture: structured messages contain raw data mixed with metadata or control information. "GET /index.html HTTP/1.1" holds a command (GET), an argument (/index.html) and protocol metadata (HTTP/1.1). If an application builds such a message from attacker supplied input without proper encoding or escaping, the attacker can change the commands that are sent and insert malicious ones instead.

# Vulnerable: data and structure share one string
html = "<p>Hello, " + request.args["name"] + "</p>"
query = "SELECT * FROM users WHERE name = '" + name + "'"

The fix differs by destination, and that is the heart of this weakness. The same value needs HTML encoding in a page, parameter binding in SQL, and different escaping in a shell command or LDAP filter.

MITRE's observed examples:

  • CVE-2021-41232: a chain where an authentication routine in a Go based agile development product did not escape the user name, allowing LDAP injection.
  • CVE-2008-4636: OS command injection in backup software using shell metacharacters in a filename.
  • CVE-2008-0769 and CVE-2008-0005: a web application and a program that did not set the charset when sending a page, allowing XSS when a browser chose an unexpected encoding.

The last pair is the subtle case. Nothing was missing in the escaping logic. The encoding was never declared, so the browser guessed.

What an attacker gains

MITRE lists these consequences: communications between components can be modified in unexpected ways, unexpected commands can be executed, bypassing other security mechanisms, and incoming data can be misinterpreted. That covers integrity, confidentiality, availability and access control.

How to find it

MITRE says this weakness can often be detected with static analysis, where modern tools use data flow analysis or constraint based techniques to limit false positives. Dynamic tools and fuzzing with many diverse inputs also help. Review by hand any code that builds a string for another system, and test with the special characters of the destination.

Click through it at your own pace, or open it full screen.

How to fix it

MITRE's guidance:

  1. Use a vetted library or framework with encoding built in, such as the ESAPI Encoding control or a similar tool. Wrap built-in functions in case they turn out to have a flaw.
  2. Use parameterization where available: structured mechanisms that separate data from code and handle quoting, encoding and validation for you. Stored procedures and bound parameters for SQL are the standard example.
  3. Understand the context. Study the protocols and data representations involved and encode for the destination, especially where outputs contain several encodings at once, such as web pages or multipart mail.
  4. Use a strict allowlist when output encoding is not enough. If users must supply limited control information, such as formatting tags in a wiki, allow only a tight set and verify the resulting structure.
  5. Declare the encoding. Specify an output encoding the receiver can handle, so it does not guess.
# Fixed
import html
safe = "<p>Hello, " + html.escape(name) + "</p>"
cur.execute("SELECT * FROM users WHERE name = %s", (name,))

Encode at the last moment, as close as possible to the point where the message is built, so the context is known.

Quick check

Why is there no single escape function that fixes everything?

Each destination treats different characters as special. HTML, SQL, shells and LDAP each need their own encoding for their own context.

How did a missing charset lead to XSS?

With no declared encoding, the browser chose one on its own, and bytes that looked harmless became special characters, as in CVE-2008-0769.

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.