12.11 XML 函数

table12.15 XML 函数

NameDescription
ExtractValue()使用 XPathtable 示法从 XML 字符串中提取值
UpdateXML()返回替换的 XML 片段

本节讨论 MySQL 中的 XML 和相关功能。

Note

通过使用--xml选项调用mysqlmysqldumpClient 端,可以从 MySQL 获取 XML 格式的输出。参见第 4.5.1 节“ mysql-MySQL 命令行 Client 端”第 4.5.4 节“ mysqldump-数据库备份程序”

提供了两个提供基本 XPath 1.0(XML 路径语言,版本 1.0)功能的功能。本节稍后将提供有关 XPath 语法和用法的一些基本信息。但是,对这些主题的深入讨论超出了本手册的范围,您应该参考XML 路径语言(XPath)1.0 标准以获得最终信息。对于 XPath 新手或需要基础知识的人来说,有用的资源是Zvon.org XPath 教程,它可以使用多种语言。

Note

这些功能仍在开发中。我们将 continue 改进 MySQL 5.7 及更高版本中 XML 和 XPath 功能的这些方面和其他方面。您可以在MySQL XML 用户论坛中讨论这些问题,询问有关它们的问题,并从其他用户那里获得帮助。

这些函数使用的 XPathtable 达式支持用户变量和本地存储的程序变量。用户变量被弱检查;强烈检查存储程序本地的变量(另请参见 Bug#26518):

  • 用户变量(弱检查). 不检查使用语法$@variable_name的变量(即用户变量)。如果变量的类型错误或先前未分配值,则服务器不会发出警告或错误。这也意味着用户应对印刷错误承担全部责任,因为如果(例如)在使用$@myvariable的地方使用$@myvariable不会发出任何警告。

Example:

mysql> SET @xml = '<a><b>X</b><b>Y</b></a>';
Query OK, 0 rows affected (0.00 sec)

mysql> SET @i =1, @j = 2;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @i, ExtractValue(@xml, '//b[$@i]');
+------+--------------------------------+
| @i   | ExtractValue(@xml, '//b[$@i]') |
+------+--------------------------------+
|    1 | X                              |
+------+--------------------------------+
1 row in set (0.00 sec)

mysql> SELECT @j, ExtractValue(@xml, '//b[$@j]');
+------+--------------------------------+
| @j   | ExtractValue(@xml, '//b[$@j]') |
+------+--------------------------------+
|    2 | Y                              |
+------+--------------------------------+
1 row in set (0.00 sec)

mysql> SELECT @k, ExtractValue(@xml, '//b[$@k]');
+------+--------------------------------+
| @k   | ExtractValue(@xml, '//b[$@k]') |
+------+--------------------------------+
| NULL |                                |
+------+--------------------------------+
1 row in set (0.00 sec)
  • 存储程序中的变量(强检查). 在存储程序内部调用时,可以声明使用语法$variable_name的变量并将其与这些函数一起使用。此类变量在定义它们的存储程序中是局部的,并会严格检查其类型和值。

Example:

mysql> DELIMITER |

mysql> CREATE PROCEDURE myproc ()
    -> BEGIN
    ->   DECLARE i INT DEFAULT 1;
    ->   DECLARE xml VARCHAR(25) DEFAULT '<a>X</a><a>Y</a><a>Z</a>';
    ->
    ->   WHILE i < 4 DO
    ->     SELECT xml, i, ExtractValue(xml, '//a[$i]');
    ->     SET i = i+1;
    ->   END WHILE;
    -> END |
Query OK, 0 rows affected (0.01 sec)

mysql> DELIMITER ;

mysql> CALL myproc();
+--------------------------+---+------------------------------+
| xml                      | i | ExtractValue(xml, '//a[$i]') |
+--------------------------+---+------------------------------+
| <a>X</a><a>Y</a><a>Z</a> | 1 | X                            |
+--------------------------+---+------------------------------+
1 row in set (0.00 sec)

+--------------------------+---+------------------------------+
| xml                      | i | ExtractValue(xml, '//a[$i]') |
+--------------------------+---+------------------------------+
| <a>X</a><a>Y</a><a>Z</a> | 2 | Y                            |
+--------------------------+---+------------------------------+
1 row in set (0.01 sec)

+--------------------------+---+------------------------------+
| xml                      | i | ExtractValue(xml, '//a[$i]') |
+--------------------------+---+------------------------------+
| <a>X</a><a>Y</a><a>Z</a> | 3 | Z                            |
+--------------------------+---+------------------------------+
1 row in set (0.01 sec)

参数. 在作为参数传递的存储例程中的 XPathtable 达式中使用的变量也要经过严格检查。

包含用户变量或存储程序本地变量的 table 达式必须(除符号外)必须符合 XPath 1.0 规范中给出的包含变量的 XPathtable 达式的规则。

Note

用于存储 XPathtable 达式的用户变量被视为空字符串。因此,不可能将 XPathtable 达式存储为用户变量。 (错误#32911)

ExtractValue(xml_frag, xpath_expr)

ExtractValue()接受两个字符串参数,XML 标记* xml_frag 的片段和 XPathtable 达式 xpath_expr *(也称为定位符);它返回第一个文本节点的文本(CDATA),该文本节点是 XPathtable 达式匹配的一个或多个元素的子级。

使用此功能等效于在附加/text()之后使用* xpath_expr *执行匹配。换句话说,ExtractValue('<a><b>Sakila</b></a>', '/a/b')ExtractValue('<a><b>Sakila</b></a>', '/a/b/text()')产生相同的结果。

如果找到多个匹配项,则每个匹配元素的第一个子文本节点的内容(以匹配的 Sequences)作为单个以空格分隔的字符串返回。

如果找不到 table 达式的匹配文本节点(包括隐式/text()),则无论出于何种原因,只要* xpath_expr 有效且 xml_frag *由正确嵌套和封闭的元素组成,将返回空字符串。在空元素上的匹配与完全不匹配之间没有区别。这是设计使然。

如果需要确定是否在* xml_frag *中找不到匹配的元素,或者是否找到了这样的元素但不包含子文本节点,则应测试使用 XPath count()函数的 table 达式的结果。例如,这两个语句都返回一个空字符串,如下所示:

mysql> SELECT ExtractValue('<a><b/></a>', '/a/b');
+-------------------------------------+
| ExtractValue('<a><b/></a>', '/a/b') |
+-------------------------------------+
|                                     |
+-------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT ExtractValue('<a><c/></a>', '/a/b');
+-------------------------------------+
| ExtractValue('<a><c/></a>', '/a/b') |
+-------------------------------------+
|                                     |
+-------------------------------------+
1 row in set (0.00 sec)

但是,您可以使用以下方法确定实际上是否存在匹配的元素:

mysql> SELECT ExtractValue('<a><b/></a>', 'count(/a/b)');
+-------------------------------------+
| ExtractValue('<a><b/></a>', 'count(/a/b)') |
+-------------------------------------+
| 1                                   |
+-------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT ExtractValue('<a><c/></a>', 'count(/a/b)');
+-------------------------------------+
| ExtractValue('<a><c/></a>', 'count(/a/b)') |
+-------------------------------------+
| 0                                   |
+-------------------------------------+
1 row in set (0.01 sec)

Important

ExtractValue()仅返回CDATA,不返回匹配标签中可能包含的任何标签,也不返回其任何内容(请参见以下示例中返回为val1的结果)。

mysql> SELECT
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '/a') AS val1,
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '/a/b') AS val2,
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '//b') AS val3,
    ->   ExtractValue('<a>ccc<b>ddd</b></a>', '/b') AS val4,
    ->   ExtractValue('<a>ccc<b>ddd</b><b>eee</b></a>', '//b') AS val5;

+------+------+------+------+---------+
| val1 | val2 | val3 | val4 | val5    |
+------+------+------+------+---------+
| ccc  | ddd  | ddd  |      | ddd eee |
+------+------+------+------+---------+

该函数使用当前的 SQL 排序规则与contains()进行比较,并考虑到其参数的排序规则可强制性,与其他字符串函数(例如CONCAT())执行相同的排序规则聚合;有关 Management 此行为的规则的说明,请参见第 10.8.4 节“table 达式中的排序规则强制性”

(以前,始终使用二进制(即区分大小写)比较。)

如果* xml_frag *包含未正确嵌套或关闭的元素并返回警告,则返回NULL,如以下示例所示:

mysql> SELECT ExtractValue('<a>c</a><b', '//a');
+-----------------------------------+
| ExtractValue('<a>c</a><b', '//a') |
+-----------------------------------+
| NULL                              |
+-----------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1525
Message: Incorrect XML value: 'parse error at line 1 pos 11:
         END-OF-INPUT unexpected ('>' wanted)'
1 row in set (0.00 sec)

mysql> SELECT ExtractValue('<a>c</a><b/>', '//a');
+-------------------------------------+
| ExtractValue('<a>c</a><b/>', '//a') |
+-------------------------------------+
| c                                   |
+-------------------------------------+
1 row in set (0.00 sec)

UpdateXML(xml_target,xpath_expr,new_xml)

此函数将 XML 标记* xml_target 的给定片段的单个部分替换为新的 XML 片段 new_xml *,然后返回更改后的 XML。 * xml_target 的替换部分与用户提供的 XPathtable 达式 xpath_expr *匹配。

如果没有找到与* xpath_expr 匹配的 table 达式,或者如果找到多个匹配项,则该函数将返回原始 xml_target * XML 片段。所有三个参数都应为字符串。

mysql> SELECT
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '/a', '<e>fff</e>') AS val1,
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '/b', '<e>fff</e>') AS val2,
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '//b', '<e>fff</e>') AS val3,
    ->   UpdateXML('<a><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val4,
    ->   UpdateXML('<a><d></d><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val5
    -> \G

*************************** 1. row ***************************
val1: <e>fff</e>
val2: <a><b>ccc</b><d></d></a>
val3: <a><e>fff</e><d></d></a>
val4: <a><b>ccc</b><e>fff</e></a>
val5: <a><d></d><b>ccc</b><d></d></a>

Note

有关 XPath 语法和用法的深入讨论超出了本手册的范围。有关详细信息,请参见XML 路径语言(XPath)1.0 规范。对于 XPath 新手或希望复习基础的人来说,有用的资源是Zvon.org XPath 教程,它可以以多种语言提供。

一些基本 XPathtable 达式的描述和示例如下:

  • /tag

当且仅当<tag/>是根元素时,才匹配<tag/>

示例:/a<a><b/></a>中具有匹配项,因为它与最外面的(根)标记匹配。它与<b><a/></b>中的内部* a *元素不匹配,因为在这种情况下,它是另一个元素的子元素。

  • /tag1/tag2

当且仅当它是<tag1/>的子代且<tag1/>是根元素时,才匹配<tag2/>

示例:/a/b匹配 XML 片段<a><b/></a>中的* b 元素,因为它是根元素 a 的子元素。它在<b><a/></b>中没有匹配项,因为在这种情况下, b 是根元素(因此没有其他元素的子元素)。 XPathtable 达式在<a><c><b/></c></a>中也没有匹配项;在这里, b a 的后代,但实际上不是 a *的子代。

该构造可扩展到三个或更多元素。例如,XPathtable 达式/a/b/c与片段<a><b><c/></b></a>中的* c *元素匹配。

  • //tag

匹配<tag>的任何实例。

示例://a与以下任何一项中的* a *元素匹配:<a><b><c/></b></a>; <c><a><b/></a></b>; <c><b><a/></b></c>

//可以与/结合使用。例如,//a/b匹配片段<a><b/></a><c><a><b/></a></c>中的* b *元素。

Note

//tag等于/descendant-or-self::*/tag。一个常见的错误是将它与/descendant-or-self::tag混淆,尽管后一个 table 达式实际上可以导致非常不同的结果,如下所示:

mysql> SET @xml = '<a><b><c>w</c><b>x</b><d>y</d>z</b></a>';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @xml;
+-----------------------------------------+
| @xml                                    |
+-----------------------------------------+
| <a><b><c>w</c><b>x</b><d>y</d>z</b></a> |
+-----------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT ExtractValue(@xml, '//b[1]');
+------------------------------+
| ExtractValue(@xml, '//b[1]') |
+------------------------------+
| x z                          |
+------------------------------+
1 row in set (0.00 sec)

mysql> SELECT ExtractValue(@xml, '//b[2]');
+------------------------------+
| ExtractValue(@xml, '//b[2]') |
+------------------------------+
|                              |
+------------------------------+
1 row in set (0.01 sec)

mysql> SELECT ExtractValue(@xml, '/descendant-or-self::*/b[1]');
+---------------------------------------------------+
| ExtractValue(@xml, '/descendant-or-self::*/b[1]') |
+---------------------------------------------------+
| x z                                               |
+---------------------------------------------------+
1 row in set (0.06 sec)

mysql> SELECT ExtractValue(@xml, '/descendant-or-self::*/b[2]');
+---------------------------------------------------+
| ExtractValue(@xml, '/descendant-or-self::*/b[2]') |
+---------------------------------------------------+
|                                                   |
+---------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT ExtractValue(@xml, '/descendant-or-self::b[1]');
+-------------------------------------------------+
| ExtractValue(@xml, '/descendant-or-self::b[1]') |
+-------------------------------------------------+
| z                                               |
+-------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT ExtractValue(@xml, '/descendant-or-self::b[2]');
+-------------------------------------------------+
| ExtractValue(@xml, '/descendant-or-self::b[2]') |
+-------------------------------------------------+
| x                                               |
+-------------------------------------------------+
1 row in set (0.00 sec)
  • *运算符充当与任何元素匹配的“通配符”。例如,table 达式/*/b与 XML 片段<a><b/></a><c><b/></c>中的* b 元素匹配。但是,该 table 达式不会在片段<b><a/></b>中产生匹配项,因为 b 必须是某些其他元素的子代。通配符可以在任何位置使用:table 达式/*/b/*将匹配 b *元素的任何子元素,而该元素本身不是根元素。

  • 您可以使用|(UNION)运算符来匹配任何定位符。例如,table 达式//b|//c匹配 XML 目标中的所有* b c *元素。

  • 也可以根据一个或多个元素的属性值来匹配该元素。这是使用语法tag[@attribute="value"]完成的。例如,table 达式//b[@id="idB"]匹配片段<a><b id="idA"/><c/><b id="idB"/></a>中的第二个* b 元素。要与具有attribute="value" any *元素匹配,请使用 XPathtable 达式//*[attribute="value"]

要过滤多个属性值,只需连续使用多个属性比较子句。例如,table 达式//b[@c="x"][@d="y"]与给定 XML 片段中任何位置出现的元素<b c="x" d="y"/>匹配。

要查找具有相同属性匹配多个值中任意一个的元素,可以使用由|运算符连接的多个定位符。例如,要匹配其* c 属性具有值 23 或 17 的所有 b *元素,请使用 table 达式//b[@c="23"]|//b[@c="17"]。您也可以为此使用逻辑or运算符://b[@c="23" or @c="17"]

Note

or|的区别在于or联接条件,而|联接结果集。

XPath 限制. 这些功能支持的 XPath 语法目前受到以下限制:

  • 不支持节点集到节点集的比较(例如'/a/b[@c=@d]')。

  • 支持所有标准的 XPath 比较运算符。错误 22228)

  • 相对定位器 table 达式在根节点的上下文中解析。例如,考虑以下查询和结果:

mysql> SELECT ExtractValue(
    ->   '<a><b c="1">X</b><b c="2">Y</b></a>',
    ->    'a/b'
    -> ) AS result;
+--------+
| result |
+--------+
| X Y    |
+--------+
1 row in set (0.03 sec)

在这种情况下,定位器a/b解析为/a/b

谓词中也支持相对定位符。在以下示例中,d[../@c="1"]被解析为/a/b[@c="1"]/d

mysql> SELECT ExtractValue(
    ->      '<a>
    ->        <b c="1"><d>X</d></b>
    ->        <b c="2"><d>X</d></b>
    ->      </a>',
    ->      'a/b/d[../@c="1"]')
    -> AS result;
+--------+
| result |
+--------+
| X      |
+--------+
1 row in set (0.00 sec)
  • 不允许以带有标量值(包括变量引用,Literals,数字和标量函数调用)为 table 达式的 table 达式作为前缀的定位符,它们的使用会导致错误。

  • 结合以下类型的节点类型,不支持::运算符:

  • axis::comment()

    • axis::text()

    • axis::processing-instructions()

    • axis::node()

但是,支持名称测试(例如axis::nameaxis::*),如以下示例所示:

mysql> SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b');
+-------------------------------------------------------+
| ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b') |
+-------------------------------------------------------+
| x                                                     |
+-------------------------------------------------------+
1 row in set (0.02 sec)

mysql> SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*');
+-------------------------------------------------------+
| ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*') |
+-------------------------------------------------------+
| x y                                                   |
+-------------------------------------------------------+
1 row in set (0.01 sec)
  • 如果路径将导致根元素“上方”,则不支持“上下”导航。也就是说,您不能使用与给定元素的祖先的后代匹配的 table 达式,其中当前元素的一个或多个祖先也是根元素的祖先(请参见 Bug#16321)。

  • 以下 XPath 函数不受支持,或存在指示的已知问题:

  • id()

    • lang()

    • local-name()

    • name()

    • namespace-uri()

    • normalize-space()

    • starts-with()

    • string()

    • substring-after()

    • substring-before()

    • translate()

  • 不支持以下轴:

  • following-sibling

    • following

    • preceding-sibling

    • preceding

作为参数传递给ExtractValue()UpdateXML()的 XPathtable 达式可以在元素 selectors 中包含冒号(:),这使得它们可以与使用 XML 名称空间 table 示法的标记一起使用。例如:

mysql> SET @xml = '<a>111<b:c>222<d>333</d><e:f>444</e:f></b:c></a>';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT ExtractValue(@xml, '//e:f');
+-----------------------------+
| ExtractValue(@xml, '//e:f') |
+-----------------------------+
| 444                         |
+-----------------------------+
1 row in set (0.00 sec)

mysql> SELECT UpdateXML(@xml, '//b:c', '<g:h>555</g:h>');
+--------------------------------------------+
| UpdateXML(@xml, '//b:c', '<g:h>555</g:h>') |
+--------------------------------------------+
| <a>111<g:h>555</g:h></a>                   |
+--------------------------------------------+
1 row in set (0.00 sec)

在某些方面,这类似于Apache Xalan和某些其他解析器所允许的内容,并且比要求名称空间声明或使用namespace-uri()local-name()函数要简单得多。

错误处理. 对于ExtractValue()UpdateXML(),使用的 XPath 定位器必须有效,并且要搜索的 XML 必须包含正确嵌套和关闭的元素。如果定位器无效,则会生成错误:

mysql> SELECT ExtractValue('<a>c</a><b/>', '/&a');
ERROR 1105 (HY000): XPATH syntax error: '&a'

如果* xml_frag *不包含正确嵌套和关闭的元素,则返回NULL并生成警告,如以下示例所示:

mysql> SELECT ExtractValue('<a>c</a><b', '//a');
+-----------------------------------+
| ExtractValue('<a>c</a><b', '//a') |
+-----------------------------------+
| NULL                              |
+-----------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1525
Message: Incorrect XML value: 'parse error at line 1 pos 11:
         END-OF-INPUT unexpected ('>' wanted)'
1 row in set (0.00 sec)

mysql> SELECT ExtractValue('<a>c</a><b/>', '//a');
+-------------------------------------+
| ExtractValue('<a>c</a><b/>', '//a') |
+-------------------------------------+
| c                                   |
+-------------------------------------+
1 row in set (0.00 sec)

Important

不会检查用作UpdateXML()的第三个参数的替换 XML,以确定它是否仅由正确嵌套和关闭的元素组成。

XPath 注入. 注入代码是指将恶意代码引入系统以获取对特权和数据的未授权访问。它基于开发人员对用户 Importing 数据的类型和内容的假设。 XPath 在这方面也不 exception。

发生这种情况的一种常见情况是,应用程序使用如下所示的 XPathtable 达式,通过将登录名和密码的组合与 XML 文件中找到的登录名和密码的组合进行匹配来处理授权:

//user[login/text()='neapolitan' and password/text()='1c3cr34m']/attribute::id

这是与 SQL 语句类似的 XPath:

SELECT id FROM users WHERE login='neapolitan' AND password='1c3cr34m';

使用 XPath 的 PHP 应用程序可能会这样处理登录过程:

<?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.");

?>

对 Importing 不执行任何检查。这意味着恶意用户可以通过为登录名和密码 Importing' or 1=1来“短路”测试,从而对$xpath进行评估,如下所示:

//user[login/text()='' or 1=1 and password/text()='' or 1=1]/attribute::id

由于方括号内的 table 达式的求值始终为true,因此它实际上与此 table 达式相同,它与 XML 文档中每个user元素的id属性匹配:

//user/attribute::id

可以避免这种特殊攻击的一种方法是,只需引用要在$xpath的定义中内插的变量名,然后将从 Webtable 单传递的值转换为字符串:

$xpath = "//user[login/text()='$login' and password/text()='$password']/attribute::id";

经常建议采用与防止 SQL 注入攻击相同的策略。通常,防止 XPath 注入攻击应遵循的做法与防止 SQL 注入相同:

  • 从不接受应用程序中用户的未经测试的数据。

  • 检查所有用户提交的数据的类型;拒绝或转换错误类型的数据

  • 测试数值数据是否超出范围;截断,舍入或拒绝超出范围的值。测试字符串中的非法字符,然后将其删除或拒绝包含它们的 Importing。

  • 不要输出明显的错误消息,这些错误消息可能会为未经授权的用户提供可能用于破坏系统的线索;而是将它们记录到文件或数据库 table 中。

正如 SQL 注入攻击可用于获取有关数据库模式的信息一样,XPath 注入也可用于遍历 XML 文件以揭示其结构,如 Amit Klein 的论文盲 XPath 注入(PDF 文件,46KB)所述。

检查将输出发送回 Client 端的输出也很重要。考虑当我们使用 MySQL ExtractValue()函数时会发生什么:

mysql> SELECT ExtractValue(
    ->     LOAD_FILE('users.xml'),
    ->     '//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id'
    -> ) AS id;
+-------------------------------+
| id                            |
+-------------------------------+
| 00327 13579 02403 42354 28570 |
+-------------------------------+
1 row in set (0.01 sec)

因为ExtractValue()以单个空格分隔的字符串返回多个匹配项,所以这种注入攻击将users.xml中包含的每个有效 ID 作为单行输出提供给用户。作为额外的保护措施,您还应该在将输出返回给用户之前测试输出。这是一个简单的示例:

mysql> SELECT @id = ExtractValue(
    ->     LOAD_FILE('users.xml'),
    ->     '//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id'
    -> );
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT IF(
    ->     INSTR(@id, ' ') = 0,
    ->     @id,
    ->     'Unable to retrieve user ID')
    -> AS singleID;
+----------------------------+
| singleID                   |
+----------------------------+
| Unable to retrieve user ID |
+----------------------------+
1 row in set (0.00 sec)

通常,安全地将数据返回给用户的准则与接受用户 Importing 的准则相同。这些可以总结为:

  • 始终测试传出数据的类型和允许值。

  • 绝不允许未经授权的用户查看错误消息,这些错误消息可能会提供有关可用于利用它的应用程序的信息。