[DOC-CVS] [doc-en] master: Revise the Taint extension documentation (#5802)
[email protected] (Xinchen Hui via GitHub)
| Newsgroups | php.doc.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: Xinchen Hui (laruence)
Committer: GitHub (web-flow)
Pusher: lacatoire
Date: 2026-08-26T12:09:34+02:00
Commit: https://github.com/php/doc-en/commit/befe2b9fbe37b0925498f336632742651386b151
Raw diff: https://github.com/php/doc-en/commit/befe2b9fbe37b0925498f336632742651386b151.diff
Revise the Taint extension documentation (#5802)
* Revise the Taint extension documentation
Rewrite all taint reference pages against the current extension source
(3.x) and README:
- book.xml: rewrite the introduction to explain the model (user input
marked tainted at request start, mark tracked through string ops,
warning at dangerous sinks) and that taint is a dev/audit tool, not
a runtime defense; update the example output to the actual
"main() [echo]: ..." warning format
- detail.xml: rebuild as "Propagation and Checked Sinks"; correct the
propagation-function list and the full sink list (output, filesystem,
SQL, command, header/cookie, unserialize/mail); remove the obsolete
"functions which untaint" table (escaping helpers no longer clear the
mark in 3.x); move basename/dirname/pathinfo from sinks to propagation
- functions/: document real signatures (taint/untaint take variadic
by-reference strings, is_tainted takes a value), always-true/false
return semantics, notes and runnable examples
- ini.xml: fix taint.error_level default to 512 (E_USER_WARNING) and
expand both directives' descriptions
- setup.xml/configure.xml: split installation into configure.xml, add
PECL/source-build instructions and the GitHub repository, add
requirements and resources sections
Facts verified against taint.c (arginfo, zend_parse_parameters,
php_taint_fcall_check) and live reflection/warning output.
* Remove trailing whitespace in taint reference.xml
* Match the from-source installation wording to the yac docs
* Move Notes sections after Examples to satisfy section-order QA
Changed paths:
M reference/taint/book.xml
M reference/taint/configure.xml
M reference/taint/detail.xml
M reference/taint/functions/is-tainted.xml
M reference/taint/functions/taint.xml
M reference/taint/functions/untaint.xml
M reference/taint/ini.xml
M reference/taint/reference.xml
M reference/taint/setup.xml
Diff:
diff --git a/reference/taint/book.xml b/reference/taint/book.xml
index b302976087a6..74ead3a7848e 100644
--- a/reference/taint/book.xml
+++ b/reference/taint/book.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- $Revision$ -->
+<!-- $Revision$ -->
<book xml:id="book.taint" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<?phpdoc extension-membership="pecl" ?>
@@ -8,49 +8,56 @@
<preface xml:id="intro.taint">
&reftitle.intro;
- <para>
- Taint is an extension, which is used for detecting XSS codes (tainted
- string).
- And also can be used to spot sql injection vulnerabilities, and shell
- inject, etc.
- </para>
- <para>
- When taint is enabled, if you pass a tainted string (comes from <varname>$_GET</varname>,
- <varname>$_POST</varname> or <varname>$_COOKIE</varname>) to some functions, taint will warn you about that.
- </para>
+ <simpara>
+ Taint is an extension for detecting XSS code (tainted strings). It can
+ also be used to spot SQL injection, command injection, file path
+ injection and similar vulnerabilities.
+ </simpara>
+ <simpara>
+ When taint is enabled, strings received from user input —
+ <varname>$_GET</varname>, <varname>$_POST</varname> and
+ <varname>$_COOKIE</varname> — are marked as tainted at request startup,
+ and the mark is tracked through string operations. When a tainted
+ string reaches a dangerous sink (output, SQL query, shell command,
+ file path, ...), taint raises a warning pointing at that spot. See
+ <link linkend="taint.detail">Propagation and Checked Sinks</link> for
+ the complete lists.
+ </simpara>
+ <simpara>
+ Taint is a development and auditing tool, not a runtime defense: it
+ only reports possible problems and never blocks or alters data. It is
+ deliberately conservative and may over-report, so a clean run means
+ only "nothing taint could see", never "provably secure". Do not enable
+ it in production environments.
+ </simpara>
<example>
- <title><function>Taint</function>example</title>
+ <title>Taint example</title>
<programlisting role="php">
<![CDATA[
<?php
$a = trim($_GET['a']);
-$file_name = '/tmp' . $a;
+$file_name = '/tmp/' . $a;
$output = "Welcome, {$a} !!!";
-$var = "output";
-$sql = "Select * from " . $a;
-$sql .= "ooxx";
+$sql = "SELECT * FROM users WHERE name = " . $a;
echo $output;
-
-print $$var;
-
+print $output;
include $file_name;
-
-mysql_query($sql);
+mysqli_query($link, $sql);
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
-Warning: main() [function.echo]: Attempt to echo a string that might be tainted
+Warning: main() [echo]: Attempt to echo a string that might be tainted in /path/to/script.php on line 9
-Warning: main() [function.echo]: Attempt to print a string that might be tainted
+Warning: main() [print]: Attempt to print a string that might be tainted in /path/to/script.php on line 10
-Warning: include() [function.include]: File path contains data that might be tainted
+Warning: main() [include]: File path contains data that might be tainted in /path/to/script.php on line 11
-Warning: mysql_query() [function.mysql-query]: SQL statement contains data that might be tainted
+Warning: main() [mysqli_query]: SQL statement contains data that might be tainted in /path/to/script.php on line 12
]]>
</screen>
</example>
diff --git a/reference/taint/configure.xml b/reference/taint/configure.xml
index d7fca2ac8984..fe737e0327a0 100644
--- a/reference/taint/configure.xml
+++ b/reference/taint/configure.xml
@@ -1,17 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- $Revision$ -->
+<!-- $Revision$ -->
<section xml:id="taint.installation" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
&reftitle.install;
- <para>
+ <simpara>
&pecl.info;
- <link xlink:href="&url.pecl.package;taint">&url.pecl.package;taint</link>
+ <link xlink:href="&url.pecl.package;taint">&url.pecl.package;taint</link>.
+ </simpara>
+
+ <simpara>
+ Install it with <acronym>PECL</acronym>:
+ </simpara>
+ <para>
+ <screen>
+<![CDATA[
+$ pecl install taint
+]]>
+ </screen>
</para>
-
-</section>
+ <para>
+ The source code is hosted on
+ <link xlink:href="&url.git.hub;laruence/taint">GitHub</link>. To build the
+ extension from source:
+ <screen>
+<![CDATA[
+$ git clone https://github.com/laruence/taint.git
+$ cd taint
+$ phpize
+$ ./configure
+$ make
+$ sudo make install
+]]>
+ </screen>
+ </para>
+ <simpara>
+ Then enable the extension by adding
+ <literal>extension=taint.so</literal> (or <literal>extension=php_taint.dll</literal>
+ on Windows) to &php.ini;, and set
+ <link linkend="ini.taint.enable">taint.enable</link> to
+ <literal>1</literal>.
+ </simpara>
+
+ <warning>
+ <simpara>
+ Taint is a development and auditing tool. Do not enable it in
+ production environments: the instrumentation slows every request down
+ and disables the OPcache JIT, and the warnings may leak request data
+ into logs.
+ </simpara>
+ </warning>
+</section>
<!-- Keep this comment at the end of the file
Local variables:
diff --git a/reference/taint/detail.xml b/reference/taint/detail.xml
index fbac3b097a15..1c2cc8e0fb45 100644
--- a/reference/taint/detail.xml
+++ b/reference/taint/detail.xml
@@ -1,319 +1,275 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- $Revision$ -->
+<!-- $Revision$ -->
<chapter xml:id="taint.detail" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
- <title>More Details</title>
+ <title>Propagation and Checked Sinks</title>
<section xml:id="taint.detail.basic">
- <title>Functions and Statements which will spread the tainted mark of a
- tainted string</title>
+ <title>How the taint mark is propagated</title>
+ <simpara>
+ 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:
+ </simpara>
<para>
<table>
- <title></title>
- <tgroup cols="2">
- <colspec colname="name"/>
- <colspec colname="version"/>
- <thead>
- <row>
- <entry>Function/Statement</entry>
- <entry>Since</entry>
- </row>
- </thead>
+ <title>Operators which propagate the taint mark</title>
+ <tgroup cols="1">
<tbody>
<row>
- <entry>= (assign)</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>. (concat)</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>"{$var}" (variable substitution)</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>.= (assign concat)</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>strval</entry>
- <entry>0.3.0</entry>
- </row>
- <row>
- <entry>explode/split</entry>
- <entry>0.3.0</entry>
- </row>
- <row>
- <entry>implode/join</entry>
- <entry>0.3.0</entry>
+ <entry><literal>=</literal> (assignment, including <literal>list()</literal>/<literal>array destructuring</literal>)</entry>
</row>
<row>
- <entry>sprintf</entry>
- <entry>0.3.0</entry>
+ <entry><literal>.</literal> (concatenation)</entry>
</row>
<row>
- <entry>vsprintf</entry>
- <entry>0.3.0</entry>
+ <entry><literal>.=</literal> (concatenating assignment)</entry>
</row>
<row>
- <entry>trim</entry>
- <entry>0.4.0</entry>
+ <entry><literal>"{$var}"</literal> (string interpolation, including the <literal>ROPE</literal> fast path)</entry>
</row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ <simpara>
+ 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.
+ </simpara>
+ <para>
+ <table>
+ <title>Functions which propagate the taint mark</title>
+ <tgroup cols="1">
+ <tbody>
<row>
- <entry>rtrim</entry>
- <entry>0.4.0</entry>
+ <entry><function>trim</function>, <function>rtrim</function>, <function>ltrim</function></entry>
</row>
<row>
- <entry>ltrim</entry>
- <entry>0.4.0</entry>
+ <entry><function>substr</function>, <function>strstr</function></entry>
</row>
<row>
- <entry>strstr</entry>
- <entry>0.5.0</entry>
+ <entry><function>str_replace</function>, <function>str_ireplace</function></entry>
</row>
<row>
- <entry>str_pad</entry>
- <entry>0.5.0</entry>
+ <entry><function>str_pad</function>, <function>strtolower</function>, <function>strtoupper</function>, <function>strval</function></entry>
</row>
<row>
- <entry>str_replace</entry>
- <entry>0.5.0</entry>
+ <entry><function>explode</function> (every element of the resulting array)</entry>
</row>
<row>
- <entry>substr</entry>
- <entry>0.5.0</entry>
+ <entry><function>implode</function>/<function>join</function> (a tainted separator taints the result as well)</entry>
</row>
<row>
- <entry>strtolower</entry>
- <entry>0.5.0</entry>
+ <entry><function>sprintf</function>, <function>vsprintf</function> (only the <literal>%s</literal> specifier carries the mark; <literal>sprintf("%d", $t)</literal> returns a clean string)</entry>
</row>
<row>
- <entry>strtoupper</entry>
- <entry>0.5.0</entry>
+ <entry><function>dirname</function>, <function>basename</function>, <function>pathinfo</function></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
+ <simpara>
+ Any function taint does not explicitly understand returns a fresh,
+ unmarked string — including escaping helpers such as
+ <function>htmlspecialchars</function>, <function>htmlentities</function> or
+ <function>mysqli_real_escape_string</function>. This is deliberate: taint
+ over-reports rather than trying to decide whether a value is
+ <quote>safe</quote> for a particular output context. Use
+ <function>untaint</function> to clear the mark on values you have validated
+ yourself.
+ </simpara>
</section>
- <section xml:id="taint.detail.taint">
- <title>Functions and statements which will check tainted string</title>
+ <section xml:id="taint.detail.sinks">
+ <title>Where taint raises warnings</title>
+ <simpara>
+ When a tainted string reaches one of the sinks below, taint raises a
+ warning (by default an <constant>E_USER_WARNING</constant>; the level is
+ configurable via <link linkend="ini.taint.error-level">taint.error_level</link>).
+ Only top-level string arguments are inspected; dumping an array that
+ merely contains tainted values does not warn.
+ </simpara>
<para>
<table>
- <title></title>
+ <title>Output sinks</title>
<tgroup cols="2">
- <colspec colname="name"/>
- <colspec colname="version"/>
<thead>
- <row>
- <entry>Function/Statement</entry>
- <entry>Since</entry>
- </row>
+ <row><entry>Sink</entry><entry>Checked</entry></row>
</thead>
<tbody>
<row>
- <entry namest="name" nameend="version">Basic statments</entry>
- </row>
- <row>
- <entry>eval</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>include/include_once</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>require/require_once</entry>
- <entry>0.1.0</entry>
- </row>
- <!--end basic -->
-
- <row>
- <entry namest="name" nameend="version">Outputing Functions</entry>
- </row>
- <row>
- <entry>echo</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>print</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>printf</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>file_put_contents</entry>
- <entry>0.1.0</entry>
- </row>
- <!-- end outputing -->
- <row>
- <entry namest="name" nameend="version">File System Functions</entry>
- </row>
- <row>
- <entry>fopen</entry>
- <entry>0.2.0</entry>
- </row>
- <row>
- <entry>opendir</entry>
- <entry>0.2.0</entry>
- </row>
- <row>
- <entry>basename</entry>
- <entry>0.2.0</entry>
- </row>
- <row>
- <entry>dirname</entry>
- <entry>0.2.0</entry>
- </row>
- <row>
- <entry>file</entry>
- <entry>0.2.0</entry>
- </row>
- <row>
- <entry>pathinfo</entry>
- <entry>0.2.0</entry>
- </row>
- <!-- end file system -->
- <row>
- <entry namest="name" nameend="version">Database relevant Functions</entry>
+ <entry><literal>echo</literal>, <literal>print</literal></entry>
+ <entry>the echoed/printed expression</entry>
</row>
<row>
- <entry>mysql_query</entry>
- <entry>0.2.0</entry>
+ <entry><function>printf</function>, <function>vprintf</function></entry>
+ <entry>the format string and the substituted values</entry>
</row>
<row>
- <entry>mysqli_query/MySQLi::query</entry>
- <entry>0.2.0</entry>
+ <entry><function>print_r</function>, <function>var_dump</function>, <function>var_export</function></entry>
+ <entry>the value being dumped, when it is a string</entry>
</row>
<row>
- <entry>sqlite_query/SqliteDataBase::query</entry>
- <entry>0.3.0</entry>
+ <entry><literal>exit</literal>/<literal>die</literal> with a message</entry>
+ <entry>the message</entry>
</row>
<row>
- <entry>sqlite_single_query/SqliteDataBase::singleQuery</entry>
- <entry>0.3.0</entry>
- </row>
- <row>
- <entry>oci_parse</entry>
- <entry>0.3.0</entry>
+ <entry><function>file_put_contents</function>, <function>fwrite</function>, <function>fputs</function> to <literal>php://output</literal></entry>
+ <entry>the data being written</entry>
</row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ <para>
+ <table>
+ <title>Filesystem sinks</title>
+ <tgroup cols="2">
+ <thead>
+ <row><entry>Sink</entry><entry>Checked</entry></row>
+ </thead>
+ <tbody>
<row>
- <entry>PDO::query</entry>
- <entry>0.3.0</entry>
+ <entry><function>fopen</function>, <function>opendir</function>, <function>unlink</function></entry>
+ <entry>the path</entry>
</row>
<row>
- <entry>PDO::prepare</entry>
- <entry>0.3.0</entry>
+ <entry><function>file</function>, <function>readfile</function>, <function>file_get_contents</function>, <function>highlight_file</function>/<function>show_source</function></entry>
+ <entry>the path</entry>
</row>
<row>
- <entry>SQLite3::query</entry>
- <entry>2.0.1</entry>
+ <entry><function>copy</function>, <function>rename</function>, <function>move_uploaded_file</function></entry>
+ <entry>both the source and destination paths</entry>
</row>
<row>
- <entry>SQLite3::prepare</entry>
- <entry>2.0.1</entry>
+ <entry><function>mkdir</function>, <function>rmdir</function>, <function>touch</function></entry>
+ <entry>the path</entry>
</row>
- <!-- end database -->
<row>
- <entry namest="name" nameend="version">Command Line relevant Functions</entry>
+ <entry><literal>include</literal>, <literal>include_once</literal>, <literal>require</literal>, <literal>require_once</literal></entry>
+ <entry>the file path</entry>
</row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ <para>
+ <table>
+ <title>SQL sinks</title>
+ <tgroup cols="2">
+ <thead>
+ <row><entry>Sink</entry><entry>Checked</entry></row>
+ </thead>
+ <tbody>
<row>
- <entry>system</entry>
- <entry>0.1.0</entry>
+ <entry><function>mysqli_query</function>, <function>mysqli_prepare</function>, <function>mysqli_real_query</function>, <function>mysqli_multi_query</function></entry>
+ <entry>the query string</entry>
</row>
<row>
- <entry>exec</entry>
- <entry>0.1.0</entry>
+ <entry><function>mysql_query</function>, <function>sqlite_query</function>, <function>sqlite_single_query</function>, <function>oci_parse</function>, <function>pg_query</function>, <function>pg_send_query</function></entry>
+ <entry>the query string</entry>
</row>
<row>
- <entry>proc_open</entry>
- <entry>0.1.0</entry>
+ <entry><methodname>mysqli::query</methodname>, <methodname>mysqli::prepare</methodname>, <methodname>mysqli::real_query</methodname>, <methodname>mysqli::multi_query</methodname></entry>
+ <entry>the query string</entry>
</row>
<row>
- <entry>passthru</entry>
- <entry>0.1.0</entry>
+ <entry><methodname>PDO::query</methodname>, <methodname>PDO::prepare</methodname>, <methodname>PDO::exec</methodname></entry>
+ <entry>the query string</entry>
</row>
<row>
- <entry>shell_exec</entry>
- <entry>0.3.0</entry>
+ <entry><methodname>SQLite3::query</methodname>, <methodname>SQLite3::prepare</methodname>, <methodname>SQLite3::exec</methodname>, <methodname>SQLiteDatabase::query</methodname>, <methodname>SQLiteDatabase::singleQuery</methodname></entry>
+ <entry>the query string</entry>
</row>
- <!-- end command line -->
-
</tbody>
</tgroup>
</table>
</para>
- </section>
-
- <section xml:id="taint.detail.untaint">
- <title>Functions which untaint the tainted string</title>
<para>
<table>
- <title></title>
+ <title>Command execution sinks</title>
<tgroup cols="2">
- <colspec colname="name"/>
- <colspec colname="version"/>
<thead>
- <row>
- <entry>Function</entry>
- <entry>Since</entry>
- </row>
+ <row><entry>Sink</entry><entry>Checked</entry></row>
</thead>
<tbody>
<row>
- <entry>addslashes</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>addcslashes</entry>
- <entry>0.1.0</entry>
+ <entry><function>exec</function>, <function>system</function>, <function>passthru</function>, <function>shell_exec</function> (including the backtick operator)</entry>
+ <entry>the command string</entry>
</row>
<row>
- <entry>htmlspecialchars</entry>
- <entry>0.1.0</entry>
+ <entry><function>proc_open</function>, <function>popen</function></entry>
+ <entry>the command string</entry>
</row>
<row>
- <entry>htmlentities</entry>
- <entry>0.1.0</entry>
+ <entry><literal>eval</literal></entry>
+ <entry>the evaluated code</entry>
</row>
<row>
- <entry>escapeshellcmd</entry>
- <entry>0.1.0</entry>
+ <entry>dynamic calls such as <literal>$func()</literal>, <literal>$obj->$method()</literal>, <function>call_user_func</function>, array callables</entry>
+ <entry>the function/method/class name being resolved</entry>
</row>
<row>
- <entry>mysql_escape_string</entry>
- <entry>0.1.0</entry>
- </row>
- <row>
- <entry>mysql_real_escape_string</entry>
- <entry>0.1.0</entry>
+ <entry><function>preg_match</function>, <function>preg_match_all</function>, <function>preg_replace</function>, <function>preg_split</function>, <function>preg_grep</function>, <function>preg_replace_callback</function></entry>
+ <entry>the pattern (and the callback name for <function>preg_replace_callback</function>)</entry>
</row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ <para>
+ <table>
+ <title>Header and cookie sinks</title>
+ <tgroup cols="2">
+ <thead>
+ <row><entry>Sink</entry><entry>Checked</entry></row>
+ </thead>
+ <tbody>
<row>
- <entry>mysqli_escape_string/MySQLi::escape_string</entry>
- <entry>0.1.0</entry>
+ <entry><function>header</function></entry>
+ <entry>the header string</entry>
</row>
<row>
- <entry>mysqli_real_escape_string/MySQLi::real_escape_string</entry>
- <entry>0.1.0</entry>
+ <entry><function>setcookie</function>, <function>setrawcookie</function></entry>
+ <entry>the cookie name and value</entry>
</row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ <para>
+ <table>
+ <title>Other sinks</title>
+ <tgroup cols="2">
+ <thead>
+ <row><entry>Sink</entry><entry>Checked</entry></row>
+ </thead>
+ <tbody>
<row>
- <entry>sqlite_escape_string/SqliteDataBase::escapeString</entry>
- <entry>0.3.0</entry>
+ <entry><function>unserialize</function></entry>
+ <entry>the serialized string</entry>
</row>
<row>
- <entry>PDO::quote</entry>
- <entry>0.3.0</entry>
+ <entry><function>mail</function></entry>
+ <entry>to, subject, additional parameters and additional headers (the message body is content and is not checked)</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
-
+ <simpara>
+ Warnings follow the format
+ <literal>function_name() [sink]: message</literal>, where
+ <literal>sink</literal> identifies the checked operation (for example
+ <literal>echo</literal>, <literal>include</literal> or the function name) and
+ the message describes what was found to be possibly tainted.
+ </simpara>
</section>
+
</chapter>
<!-- Keep this comment at the end of the file
diff --git a/reference/taint/functions/is-tainted.xml b/reference/taint/functions/is-tainted.xml
index dfc535fbd273..9f6ec527df31 100644
--- a/reference/taint/functions/is-tainted.xml
+++ b/reference/taint/functions/is-tainted.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- $Revision$ -->
+<!-- $Revision$ -->
<refentry xml:id="function.is-tainted" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<refnamediv>
<refname>is_tainted</refname>
- <refpurpose>Checks whether a string is tainted</refpurpose>
+ <refpurpose>Check whether a string is tainted</refpurpose>
</refnamediv>
<refsect1 role="description">
@@ -13,10 +13,10 @@
<type>bool</type><methodname>is_tainted</methodname>
<methodparam><type>string</type><parameter>string</parameter></methodparam>
</methodsynopsis>
- <para>
- Checks whether a string is tainted
- </para>
-
+ <simpara>
+ Checks whether the given value carries the taint mark. Only strings can
+ ever be tainted; any other type returns &false;.
+ </simpara>
</refsect1>
<refsect1 role="parameters">
@@ -25,9 +25,9 @@
<varlistentry>
<term><parameter>string</parameter></term>
<listitem>
- <para>
-
- </para>
+ <simpara>
+ The value to check.
+ </simpara>
</listitem>
</varlistentry>
</variablelist>
@@ -35,12 +35,44 @@
<refsect1 role="returnvalues">
&reftitle.returnvalues;
+ <simpara>
+ Returns &true; if the value is a tainted string, &false; otherwise.
+ Always returns &false; when
+ <link linkend="ini.taint.enable">taint.enable</link> is off.
+ </simpara>
+ </refsect1>
+
+ <refsect1 role="examples">
+ &reftitle.examples;
+ <example>
+ <title><function>is_tainted</function> example</title>
+ <programlisting role="php">
+<![CDATA[
+<?php
+$name = $_GET['name'] ?? 'world';
+var_dump(is_tainted($name));
+?>
+]]>
+ </programlisting>
+ &example.outputs.similar;
+ <screen>
+<![CDATA[
+bool(true)
+]]>
+ </screen>
+ </example>
+ </refsect1>
+
+ <refsect1 role="seealso">
+ &reftitle.seealso;
<para>
- Return TRUE if the string is tainted, FALSE otherwise.
+ <simplelist>
+ <member><function>taint</function></member>
+ <member><function>untaint</function></member>
+ </simplelist>
</para>
</refsect1>
-
</refentry>
<!-- Keep this comment at the end of the file
diff --git a/reference/taint/functions/taint.xml b/reference/taint/functions/taint.xml
index 8e47a39a2750..cce73f9728df 100644
--- a/reference/taint/functions/taint.xml
+++ b/reference/taint/functions/taint.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- $Revision$ -->
+<!-- $Revision$ -->
<refentry xml:id="function.taint" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<refnamediv>
<refname>taint</refname>
- <refpurpose>Taint a string</refpurpose>
+ <refpurpose>Mark strings as tainted</refpurpose>
</refnamediv>
<refsect1 role="description">
@@ -12,11 +12,21 @@
<methodsynopsis>
<type>bool</type><methodname>taint</methodname>
<methodparam><type>string</type><parameter role="reference">string</parameter></methodparam>
- <methodparam rep="repeat"><type>string</type><parameter>strings</parameter></methodparam>
+ <methodparam rep="repeat"><type>string</type><parameter role="reference">strings</parameter></methodparam>
</methodsynopsis>
- <para>
- Make a string tainted. This is used for testing purpose only.
- </para>
+ <simpara>
+ Manually marks the given strings as tainted, as if they had arrived
+ from user input. The variables are passed by reference, but the mark
+ itself is stored on the string rather than on the variable: every
+ variable sharing the same string becomes tainted at once.
+ </simpara>
+ <simpara>
+ This is mainly useful for testing, and for simulating user input in
+ CLI scripts where the <link linkend="reserved.variables.get">$_GET</link>,
+ <link linkend="reserved.variables.post">$_POST</link> and
+ <link linkend="reserved.variables.cookies">$_COOKIE</link>
+ superglobals are not populated.
+ </simpara>
</refsect1>
<refsect1 role="parameters">
@@ -25,16 +35,17 @@
<varlistentry>
<term><parameter>string</parameter></term>
<listitem>
- <para>
-
- </para>
+ <simpara>
+ A variable holding the string to mark.
+ </simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>strings</parameter></term>
<listitem>
- <para>
- </para>
+ <simpara>
+ Further variables to mark.
+ </simpara>
</listitem>
</varlistentry>
</variablelist>
@@ -42,13 +53,61 @@
<refsect1 role="returnvalues">
&reftitle.returnvalues;
+ <simpara>
+ Always returns &true;. When
+ <link linkend="ini.taint.enable">taint.enable</link> is off, the
+ function does nothing and still returns &true;.
+ </simpara>
+ </refsect1>
+
+ <refsect1 role="examples">
+ &reftitle.examples;
+ <example>
+ <title><function>taint</function> example</title>
+ <programlisting role="php">
+<![CDATA[
+<?php
+$name = "world";
+taint($name);
+var_dump(is_tainted($name));
+?>
+]]>
+ </programlisting>
+ &example.outputs.similar;
+ <screen>
+<![CDATA[
+bool(true)
+]]>
+ </screen>
+ </example>
+ </refsect1>
+
+ <refsect1 role="notes">
+ &reftitle.notes;
+ <note>
+ <simpara>
+ Only non-empty strings are marked; variables holding other types, or
+ empty strings, are silently ignored.
+ </simpara>
+ </note>
+ <note>
+ <simpara>
+ Interned, persistent and permanent strings (string literals, opcache
+ shared strings) can never carry the mark and are silently skipped.
+ </simpara>
+ </note>
+ </refsect1>
+
+ <refsect1 role="seealso">
+ &reftitle.seealso;
<para>
- Return TRUE if the transformation is done. Always return TRUE if the taint
- extension is not enabled.
+ <simplelist>
+ <member><function>untaint</function></member>
+ <member><function>is_tainted</function></member>
+ </simplelist>
</para>
</refsect1>
-
</refentry>
<!-- Keep this comment at the end of the file
diff --git a/reference/taint/functions/untaint.xml b/reference/taint/functions/untaint.xml
index 2a6848eef401..27b14560fe67 100644
--- a/reference/taint/functions/untaint.xml
+++ b/reference/taint/functions/untaint.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- $Revision$ -->
+<!-- $Revision$ -->
<refentry xml:id="function.untaint" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<refnamediv>
<refname>untaint</refname>
- <refpurpose>Untaint strings</refpurpose>
+ <refpurpose>Remove the taint mark from strings</refpurpose>
</refnamediv>
<refsect1 role="description">
@@ -12,11 +12,17 @@
<methodsynopsis>
<type>bool</type><methodname>untaint</methodname>
<methodparam><type>string</type><parameter role="reference">string</parameter></methodparam>
- <methodparam rep="repeat"><type>string</type><parameter>strings</parameter></methodparam>
+ <methodparam rep="repeat"><type>string</type><parameter role="reference">strings</parameter></methodparam>
</methodsynopsis>
- <para>
- Untaint strings
- </para>
+ <simpara>
+ Clears the taint mark on the given strings.
+ </simpara>
+ <simpara>
+ The mark is stored on the string itself, not on the variable, so this
+ clears it for every variable sharing the same string at once. Use it to
+ whitelist values you have validated yourself, for example after a strict
+ allow-list check.
+ </simpara>
</refsect1>
<refsect1 role="parameters">
@@ -25,17 +31,17 @@
<varlistentry>
<term><parameter>string</parameter></term>
<listitem>
- <para>
-
- </para>
+ <simpara>
+ A variable holding the string to clean.
+ </simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>strings</parameter></term>
<listitem>
- <para>
-
- </para>
+ <simpara>
+ Further variables to clean.
+ </simpara>
</listitem>
</varlistentry>
</variablelist>
@@ -43,11 +49,58 @@
<refsect1 role="returnvalues">
&reftitle.returnvalues;
- <para>
+ <simpara>
+ Always returns &true;. When
+ <link linkend="ini.taint.enable">taint.enable</link> is off, the
+ function does nothing and still returns &true;.
+ </simpara>
+ </refsect1>
- </para>
+ <refsect1 role="examples">
+ &reftitle.examples;
+ <example>
+ <title><function>untaint</function> example</title>
+ <programlisting role="php">
+<![CDATA[
+<?php
+$id = "42";
+taint($id);
+if (preg_match('/^\d+$/', $id)) {
+ // strictly validated as digits: safe to trust
+ untaint($id);
+}
+var_dump(is_tainted($id));
+?>
+]]>
+ </programlisting>
+ &example.outputs.similar;
+ <screen>
+<![CDATA[
+bool(false)
+]]>
+ </screen>
+ </example>
</refsect1>
+ <refsect1 role="notes">
+ &reftitle.notes;
+ <note>
+ <simpara>
+ Only string values can carry the mark; passing a non-string is a
+ no-op.
+ </simpara>
+ </note>
+ </refsect1>
+
+ <refsect1 role="seealso">
+ &reftitle.seealso;
+ <para>
+ <simplelist>
+ <member><function>taint</function></member>
+ <member><function>is_tainted</function></member>
+ </simplelist>
+ </para>
+ </refsect1>
</refentry>
diff --git a/reference/taint/ini.xml b/reference/taint/ini.xml
index 469e441d4a4f..8e2b3824e1f0 100644
--- a/reference/taint/ini.xml
+++ b/reference/taint/ini.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
-
+
<section xml:id="taint.configuration" xmlns="http://docbook.org/ns/docbook">
&reftitle.runtime;
&extension.runtime;
@@ -25,7 +25,7 @@
</row>
<row>
<entry><link linkend="ini.taint.error-level">taint.error_level</link></entry>
- <entry>E_WARNING</entry>
+ <entry>512 (E_USER_WARNING)</entry>
<entry><constant>INI_ALL</constant></entry>
<entry><!-- leave empty, this will be filled by an automatic script --></entry>
</row>
@@ -41,12 +41,25 @@
<varlistentry xml:id="ini.taint.enable">
<term>
<parameter>taint.enable</parameter>
- <type>int</type>
+ <type>bool</type>
</term>
<listitem>
- <para>
- Whether enable the taint.
- </para>
+ <simpara>
+ Master switch. When enabled, taint hooks the executor and marks
+ strings from <varname>$_GET</varname>, <varname>$_POST</varname> and
+ <varname>$_COOKIE</varname> as tainted at request startup.
+ </simpara>
+ <simpara>
+ This is an &php.ini;-only directive: enabling it requires a process
+ restart, so it cannot be toggled per request or per directory.
+ </simpara>
+ <note>
+ <simpara>
+ Do not enable this directive in production environments: the
+ instrumentation slows every request down and is incompatible with
+ the OPcache JIT.
+ </simpara>
+ </note>
</listitem>
</varlistentry>
<varlistentry xml:id="ini.taint.error-level">
@@ -55,13 +68,26 @@
<type>int</type>
</term>
<listitem>
+ <simpara>
+ The error level used when taint reports a possibly tainted string.
+ Defaults to <constant>E_USER_WARNING</constant> (512).
+ </simpara>
+ <simpara>
+ Because this directive is <constant>INI_ALL</constant>, it can be
+ changed at runtime. For example, to silence taint warnings for the
+ current script:
+ </simpara>
<para>
- the error type which taint will report as when taint find a tainted
- string.
+ <programlisting role="php">
+<![CDATA[
+<?php
+ini_set('taint.error_level', 0);
+?>
+]]>
+ </programlisting>
</para>
</listitem>
</varlistentry>
-
</variablelist>
</para>
</section>
diff --git a/reference/taint/reference.xml b/reference/taint/reference.xml
index 89911f5bcb71..1c818daa5122 100644
--- a/reference/taint/reference.xml
+++ b/reference/taint/reference.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- $Revision$ -->
+<!-- $Revision$ -->
<reference xml:id="ref.taint" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Taint &Functions;</title>
diff --git a/reference/taint/setup.xml b/reference/taint/setup.xml
index 94a1e3cd1bcf..e754cedd9ea4 100644
--- a/reference/taint/setup.xml
+++ b/reference/taint/setup.xml
@@ -4,21 +4,31 @@
<chapter xml:id="taint.setup" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
&reftitle.setup;
- <section xml:id="taint.installation">
- &reftitle.install;
- <para>
- &pecl.moved;
- </para>
- <para>
- &pecl.info;
- <link xlink:href="&url.pecl.package;taint">&url.pecl.package;taint</link>.
- </para>
+ <section xml:id="taint.requirements">
+ &reftitle.required;
+ <simpara>
+ Taint 3.x requires PHP 8.0 or newer. For PHP 7.x use the taint 2.1.x
+ releases, and for PHP 5.x the taint 1.x releases.
+ </simpara>
</section>
+ <!-- {{{ Installation -->
+ &reference.taint.configure;
+ <!-- }}} -->
+
<!-- {{{ Configuration -->
- &reference.taint.ini;
+ &reference.taint.ini;
<!-- }}} -->
+ <section xml:id="taint.resources">
+ &reftitle.resources;
+ <simpara>
+ Taint defines no resource types. The taint mark itself is stored in the
+ internal <literal>zend_string</literal> structure, not in a user-visible
+ resource.
+ </simpara>
+ </section>
+
</chapter>
<!-- Keep this comment at the end of the file