Dutch PHP Conference 2027

How the taint mark is propagated

The taint mark is a single bit stored on the string itself, not on the variable holding it. Assigning, passing or otherwise sharing a tainted string keeps the mark. String concatenation and interpolation propagate it as well:

Operators which propagate the taint mark
= (assignment, including list()/array destructuring)
. (concatenation)
.= (concatenating assignment)
"{$var}" (string interpolation, including the ROPE fast path)

In addition, taint understands a fixed set of string functions: when any of the relevant string arguments is tainted, the returned string is marked tainted too. Both the regular call and, on PHP 8.4+, the frameless fast-path call are covered.

Functions which propagate the taint mark
trim(), rtrim(), ltrim()
substr(), strstr()
str_replace(), str_ireplace()
str_pad(), strtolower(), strtoupper(), strval()
explode() (every element of the resulting array)
implode()/join() (a tainted separator taints the result as well)
sprintf(), vsprintf() (only the %s specifier carries the mark; sprintf("%d", $t) returns a clean string)
dirname(), basename(), pathinfo()

Any function taint does not explicitly understand returns a fresh, unmarked string — including escaping helpers such as htmlspecialchars(), htmlentities() or mysqli_real_escape_string(). This is deliberate: taint over-reports rather than trying to decide whether a value is safe for a particular output context. Use untaint() to clear the mark on values you have validated yourself.

add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top