Author: jstephens
Date: 2008-01-18 22:36:15 +0100 (Fri, 18 Jan 2008)
New Revision: 9606
Log:
Added info about security concerns when using XPath and LOAD XML
Modified:
trunk/refman-5.1/functions-core.xml
trunk/refman-6.0/functions-core.xml
trunk/refman-6.0/sql-syntax.xml
Modified: trunk/refman-5.1/functions-core.xml
===================================================================
--- trunk/refman-5.1/functions-core.xml 2008-01-18 17:45:03 UTC (rev 9605)
+++ trunk/refman-5.1/functions-core.xml 2008-01-18 21:36:15 UTC (rev 9606)
Changed blocks: 1, Lines Added: 208, Lines Deleted: 0; 7260 bytes
@@ -14179,6 +14179,214 @@
</formalpara>
+ <formalpara>
+
+ <title>XPath Injection</title>
+
+ <para>
+ <firstterm>code injection</firstterm> occurs when malicious code
+ is introduced into the system to gain unauthorized access to
+ privileges and data. It is based on exploiting assumptions made
+ by developers about the type and content of data input from
+ users. XPath is no exception in this regard.
+ </para>
+
+ </formalpara>
+
+ <para>
+ A common scenario in which this can happen is the case of
+ application which handles authorization by matching the
+ combination of a login name and password with those found in an
+ XML file, using an XPath expression like this one:
+
+<programlisting>
+//user[login/text()='neapolitan' and password/text()='1c3cr34m']/attribute::id
+</programlisting>
+
+ This is the XPath equivalent of an SQL statement like this one:
+
+<programlisting>
+SELECT id FROM users WHERE login='neapolitan' AND password='1c3cr34m';
+</programlisting>
+ </para>
+
+ <para>
+ A PHP application employing XPath might handle the login process
+ like this:
+
+<programlisting>
+<?php
+
+ $file = "users.xml";
+
+ $login = $POST["login"];
+ $password = $POST["password"];
+
+ $xpath = "//user[login/text()=$login and password/text()=$password]/attribute::id";
+
+ if( file_exists($file) )
+ {
+ $xml = simplexml_load_file($file);
+
+ if($result = $xml->xpath($xpath))
+ echo "You are now logged in as user $result[0].";
+ else
+ echo "Invalid login name or password.";
+ }
+ else
+ exit("Failed to open $file.");
+
+?>
+</programlisting>
+
+ No checks are performed on the input. This means that a malevolent
+ user can <quote>short-circuit</quote> the test by entering
+ <literal>' or 1=1</literal> for both the login name and password,
+ resulting in <varname>$xpath</varname> being evaluated as shown
+ here:
+
+<programlisting>
+//user[login/text()='' or 1=1 and password/text()='' or 1=1]/attribute::id
+</programlisting>
+
+ Since the expression inside the square brackets always evaluates
+ as <literal>true</literal>, it is effectively the same as this
+ one, which matches the <literal>id</literal> attribute of every
+ <literal>user</literal> element in the XML document:
+
+<programlisting>
+//user/attribute::id
+</programlisting>
+
+ One way in which this particular attack can be circumvented is
+ simply by quoting the variable names to be interpolated in the
+ definition of <literal>$xpath</literal>, forcing the values passed
+ from a Web form to be converted to strings:
+
+<programlisting>
+$xpath = "//user[login/text()='$login' and password/text()='$password']/attribute::id";
+</programlisting>
+ </para>
+
+ <para>
+ This is the same strategy that is often recommended for preventing
+ SQL injection attacks. In general, the practices you should follow
+ for preventing XPath injection attacks are the same as for
+ preventing SQL injection:
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+ Never accepted untested data from users in your application.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Check all user-submited data for type; reject or convert
+ data that is of the wrong type
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Test numerical data for out of range values; truncate,
+ round, or reject values that are out of range. Test strings
+ for illegal characters and either strip them out or reject
+ input containing them.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Do not output explicit error messages that might provide an
+ unauthorized user with clues that could be used to
+ compromise the system; log these to a file or database table
+ instead.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ Just as SQL injection attacks can be used to obtain information
+ about database schemas, so can XPath injection be used to traverse
+ XML files to uncover their structure, as discussed in Amit
+ Klein's paper
+ <ulink
url="http://www.packetstormsecurity.org/papers/bypass/Blind_XPath_Injection_20040518.pdf">Blind
+ XPath Injection</ulink> (PDF file, 46KB).
+ </para>
+
+ <para>
+ It is also important to check the output being sent back to the
+ client. Consider what can happen when we use the MySQL
+ <function>ExtractValue()</function> function:
+
+<programlisting>
+mysql> <userinput>SELECT ExtractValue(</userinput>
+ -> <userinput>LOAD_FILE('users.xml'),</userinput>
+ -> <userinput>'//user[login/text()="" or 1=1 and password/text()=""
or 1=1]/attribute::id'</userinput>
+ -> <userinput>) AS id;</userinput>
++-------------------------------+
+| id |
++-------------------------------+
+| 00327 13579 02403 42354 28570 |
++-------------------------------+
+1 row in set (0.01 sec)
+</programlisting>
+
+ Because <function>ExtractValue()</function> returns multiple
+ matches as a single space-delimited string, this injection attack
+ provides every valid ID contained within
+ <filename>users.xml</filename> to the user as a single row of
+ output. As an extra safeguard, you should also test output before
+ returning it to the user. Here is a simple example:
+
+<programlisting>
+mysql> <userinput>SELECT @id = ExtractValue(</userinput>
+ -> <userinput>LOAD_FILE('users.xml'),</userinput>
+ -> <userinput>'//user[login/text()="" or 1=1 and password/text()=""
or 1=1]/attribute::id'</userinput>
+ -> <userinput>);</userinput>
+Query OK, 0 rows affected (0.00 sec)
+
+mysql> <userinput>SELECT IF(</userinput>
+ -> <userinput>INSTR(@id, ' ') = 0,</userinput>
+ -> <userinput>@id,</userinput>
+ -> <userinput>'Unable to retrieve user ID')</userinput>
+ -> <userinput>AS singleID;</userinput>
++----------------------------+
+| singleID |
++----------------------------+
+| Unable to retrieve user ID |
++----------------------------+
+1 row in set (0.00 sec)
+</programlisting>
+ </para>
+
+ <para>
+ In general, the guidelines for returning data to users securely
+ are the same as for accepting user input. These can be summed up
+ as:
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+ Always test outgoing data for type and allowable values.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Never allow unauthorized users to view error messages that
+ might provide information about the application that could
+ be used to exploit it.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+ </para>
+
</section>
<section id="other-functions">
Modified: trunk/refman-6.0/functions-core.xml
===================================================================
--- trunk/refman-6.0/functions-core.xml 2008-01-18 17:45:03 UTC (rev 9605)
+++ trunk/refman-6.0/functions-core.xml 2008-01-18 21:36:15 UTC (rev 9606)
Changed blocks: 1, Lines Added: 208, Lines Deleted: 0; 7260 bytes
@@ -14450,6 +14450,214 @@
</formalpara>
+ <formalpara>
+
+ <title>XPath Injection</title>
+
+ <para>
+ <firstterm>code injection</firstterm> occurs when malicious code
+ is introduced into the system to gain unauthorized access to
+ privileges and data. It is based on exploiting assumptions made
+ by developers about the type and content of data input from
+ users. XPath is no exception in this regard.
+ </para>
+
+ </formalpara>
+
+ <para>
+ A common scenario in which this can happen is the case of
+ application which handles authorization by matching the
+ combination of a login name and password with those found in an
+ XML file, using an XPath expression like this one:
+
+<programlisting>
+//user[login/text()='neapolitan' and password/text()='1c3cr34m']/attribute::id
+</programlisting>
+
+ This is the XPath equivalent of an SQL statement like this one:
+
+<programlisting>
+SELECT id FROM users WHERE login='neapolitan' AND password='1c3cr34m';
+</programlisting>
+ </para>
+
+ <para>
+ A PHP application employing XPath might handle the login process
+ like this:
+
+<programlisting>
+<?php
+
+ $file = "users.xml";
+
+ $login = $POST["login"];
+ $password = $POST["password"];
+
+ $xpath = "//user[login/text()=$login and password/text()=$password]/attribute::id";
+
+ if( file_exists($file) )
+ {
+ $xml = simplexml_load_file($file);
+
+ if($result = $xml->xpath($xpath))
+ echo "You are now logged in as user $result[0].";
+ else
+ echo "Invalid login name or password.";
+ }
+ else
+ exit("Failed to open $file.");
+
+?>
+</programlisting>
+
+ No checks are performed on the input. This means that a malevolent
+ user can <quote>short-circuit</quote> the test by entering
+ <literal>' or 1=1</literal> for both the login name and password,
+ resulting in <varname>$xpath</varname> being evaluated as shown
+ here:
+
+<programlisting>
+//user[login/text()='' or 1=1 and password/text()='' or 1=1]/attribute::id
+</programlisting>
+
+ Since the expression inside the square brackets always evaluates
+ as <literal>true</literal>, it is effectively the same as this
+ one, which matches the <literal>id</literal> attribute of every
+ <literal>user</literal> element in the XML document:
+
+<programlisting>
+//user/attribute::id
+</programlisting>
+
+ One way in which this particular attack can be circumvented is
+ simply by quoting the variable names to be interpolated in the
+ definition of <literal>$xpath</literal>, forcing the values passed
+ from a Web form to be converted to strings:
+
+<programlisting>
+$xpath = "//user[login/text()='$login' and password/text()='$password']/attribute::id";
+</programlisting>
+ </para>
+
+ <para>
+ This is the same strategy that is often recommended for preventing
+ SQL injection attacks. In general, the practices you should follow
+ for preventing XPath injection attacks are the same as for
+ preventing SQL injection:
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+ Never accepted untested data from users in your application.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Check all user-submited data for type; reject or convert
+ data that is of the wrong type
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Test numerical data for out of range values; truncate,
+ round, or reject values that are out of range. Test strings
+ for illegal characters and either strip them out or reject
+ input containing them.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Do not output explicit error messages that might provide an
+ unauthorized user with clues that could be used to
+ compromise the system; log these to a file or database table
+ instead.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+
+ Just as SQL injection attacks can be used to obtain information
+ about database schemas, so can XPath injection be used to traverse
+ XML files to uncover their structure, as discussed in Amit
+ Klein's paper
+ <ulink
url="http://www.packetstormsecurity.org/papers/bypass/Blind_XPath_Injection_20040518.pdf">Blind
+ XPath Injection</ulink> (PDF file, 46KB).
+ </para>
+
+ <para>
+ It is also important to check the output being sent back to the
+ client. Consider what can happen when we use the MySQL
+ <function>ExtractValue()</function> function:
+
+<programlisting>
+mysql> <userinput>SELECT ExtractValue(</userinput>
+ -> <userinput>LOAD_FILE('users.xml'),</userinput>
+ -> <userinput>'//user[login/text()="" or 1=1 and password/text()=""
or 1=1]/attribute::id'</userinput>
+ -> <userinput>) AS id;</userinput>
++-------------------------------+
+| id |
++-------------------------------+
+| 00327 13579 02403 42354 28570 |
++-------------------------------+
+1 row in set (0.01 sec)
+</programlisting>
+
+ Because <function>ExtractValue()</function> returns multiple
+ matches as a single space-delimited string, this injection attack
+ provides every valid ID contained within
+ <filename>users.xml</filename> to the user as a single row of
+ output. As an extra safeguard, you should also test output before
+ returning it to the user. Here is a simple example:
+
+<programlisting>
+mysql> <userinput>SELECT @id = ExtractValue(</userinput>
+ -> <userinput>LOAD_FILE('users.xml'),</userinput>
+ -> <userinput>'//user[login/text()="" or 1=1 and password/text()=""
or 1=1]/attribute::id'</userinput>
+ -> <userinput>);</userinput>
+Query OK, 0 rows affected (0.00 sec)
+
+mysql> <userinput>SELECT IF(</userinput>
+ -> <userinput>INSTR(@id, ' ') = 0,</userinput>
+ -> <userinput>@id,</userinput>
+ -> <userinput>'Unable to retrieve user ID')</userinput>
+ -> <userinput>AS singleID;</userinput>
++----------------------------+
+| singleID |
++----------------------------+
+| Unable to retrieve user ID |
++----------------------------+
+1 row in set (0.00 sec)
+</programlisting>
+ </para>
+
+ <para>
+ In general, the guidelines for returning data to users securely
+ are the same as for accepting user input. These can be summed up
+ as:
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+ Always test outgoing data for type and allowable values.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Never allow unauthorized users to view error messages that
+ might provide information about the application that could
+ be used to exploit it.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+ </para>
+
</section>
<section id="other-functions">
Modified: trunk/refman-6.0/sql-syntax.xml
===================================================================
--- trunk/refman-6.0/sql-syntax.xml 2008-01-18 17:45:03 UTC (rev 9605)
+++ trunk/refman-6.0/sql-syntax.xml 2008-01-18 21:36:15 UTC (rev 9606)
Changed blocks: 2, Lines Added: 64, Lines Deleted: 1; 3340 bytes
@@ -8867,7 +8867,7 @@
<remark role="help-keywords">
LOW_PRIORITY CONCURRENT LOCAL INFILE REPLACE IGNORE CHARACTER
- SET ROWS IDENITIFIED BY LINES
+ SET ROWS IDENTIFIED BY LINES
</remark>
<remark role="help-syntax-begin"/>
@@ -9371,6 +9371,69 @@
imported into the <literal>address</literal> table.
</para>
+ <formalpara>
+
+ <title>Security Considerations</title>
+
+ <para>
+ As with the <literal>LOAD DATA</literal> statement, the
+ transfer of the XML file from the client host to the server
+ host is initiated by the MySQL server. In theory, a patched
+ server could be built that would tell the client program to
+ transfer a file of the server's choosing rather than the
+ file named by the client in the <literal>LOAD XML</literal>
+ statement. Such a server could access any file on the client
+ host to which the client user has read access.
+ </para>
+
+ </formalpara>
+
+ <para>
+ In a Web environment, clients usually connect to MySQL from a
+ Web server. A user that can run any command against the MySQL
+ server can use <literal>LOAD XML LOCAL</literal> to read any
+ files to which the Web server process has read access. In this
+ environment, the client with respect to the MySQL server is
+ actually the Web server, not the remote program being run by the
+ user who connects to the Web server.
+ </para>
+
+ <para>
+ You can disable loading of XML files from clients by starting
+ the server with <option>--local-infile=0</option> or
+ <option>--local-infile=OFF</option>. This option can also be
+ used when starting the <command>mysql</command> client to
+ disable <literal>LOAD XML</literal> for the duration of the
+ client session.
+ </para>
+
+ <para>
+ To prevent a client from loading XML files from the server, do
+ not grant the <literal>FILE</literal> privilege to the
+ corresponding MySQL user account, or revoke this privilege if
+ the client user account already has it.
+ </para>
+
+ <important>
+ <para>
+ Revoking the <literal>FILE</literal> privilege (or not
+ granting it in the first place) keeps the user only from
+ executing the <literal>LOAD XML INFILE</literal> statement (as
+ well as the <function>LOAD_FILE()</function> function; it does
+ <emphasis>not</emphasis> prevent the user from executing
+ <literal>LOAD XML LOCAL INFILE</literal>. To disallow this
+ statement, you must start the server or the client with
+ <option>--local-infile=OFF</option>.
+ </para>
+
+ <para>
+ In other words, the <literal>FILE</literal> privilege affects
+ only whether the client can read files on the server; it has
+ no bearing on whether the client can read files on the local
+ filesystem.
+ </para>
+ </important>
+
</section>
<section id="replace">
| Thread |
|---|
| • svn commit - mysqldoc@docsrva: r9606 - in trunk: refman-5.1 refman-6.0 | jon | 18 Jan |