如何使字符串“XML 安全”?
Posted
技术标签:
【中文标题】如何使字符串“XML 安全”?【英文标题】:How do you make strings "XML safe"? 【发布时间】:2011-03-26 11:04:27 【问题描述】:我通过 php 回显发送一个 XML 文档来响应 AJAX 调用。为了形成这个 XML 文档,我遍历了数据库的记录。问题是数据库中包含有“
【问题讨论】:
您是否尝试创建一个函数,将所有有意义的字符替换为其 xml 等效项。或者可能在 "" 中包含所有具有潜在字符的值? 【参考方案1】:从 PHP 5.4 开始,您可以使用:
htmlspecialchars($string, ENT_XML1);
你应该指定编码,例如:
htmlspecialchars($string, ENT_XML1, 'UTF-8');
更新
注意以上只会转换:
&
到 &
<
到 <
>
到 >
如果您想转义文本以用于用双引号括起来的属性:
htmlspecialchars($string, ENT_XML1 | ENT_COMPAT, 'UTF-8');
除了&
、<
和>
之外,还将"
转换为"
。
如果你的属性用单引号括起来:
htmlspecialchars($string, ENT_XML1 | ENT_QUOTES, 'UTF-8');
除了&
、<
、>
和"
之外,还会将'
转换为'
。
(当然你甚至可以在属性之外使用它)。
见the manual entry for htmlspecialchars。
【讨论】:
htmlspecialchars($string, ENT_XML1, 'UTF-8') 对我有用,实际上我这样做只是为了安全 如果您为 SimpleXML 格式化一个需要 XML 验证的字符串,这似乎是最干净的工作解决方案。我正在处理大量使用的特殊字符,这解决了我的问题。htmlspecialchars
不会转义 \xB
(垂直选项卡),例如 invalid XML。【参考方案2】:
我更喜欢 Golang 为 XML 引用转义的方式(以及一些额外的东西,如换行符转义和转义一些其他字符),所以我将它的 XML 转义函数移植到下面的 PHP 中
function isInCharacterRange(int $r): bool
return $r == 0x09 ||
$r == 0x0A ||
$r == 0x0D ||
$r >= 0x20 && $r <= 0xDF77 ||
$r >= 0xE000 && $r <= 0xFFFD ||
$r >= 0x10000 && $r <= 0x10FFFF;
function xml(string $s, bool $escapeNewline = true): string
$w = '';
$Last = 0;
$l = strlen($s);
$i = 0;
while ($i < $l)
$r = mb_substr(substr($s, $i), 0, 1);
$Width = strlen($r);
$i += $Width;
switch ($r)
case '"':
$esc = '"';
break;
case "'":
$esc = ''';
break;
case '&':
$esc = '&';
break;
case '<':
$esc = '<';
break;
case '>':
$esc = '>';
break;
case "\t":
$esc = '	';
break;
case "\n":
if (!$escapeNewline)
continue 2;
$esc = '
';
break;
case "\r":
$esc = '
';
break;
default:
if (!isInCharacterRange(mb_ord($r)) || (mb_ord($r) === 0xFFFD && $Width === 1))
$esc = "\uFFFD";
break;
continue 2;
$w .= substr($s, $Last, $i - $Last - $Width) . $esc;
$Last = $i;
$w .= substr($s, $Last);
return $w;
请注意,由于mb_ord
的使用,您至少需要 PHP7.2,否则您必须将其换成另一个 polyfill,但这些功能对我们非常有用!
对于任何好奇的人,这里是相关的 Go 源代码https://golang.org/src/encoding/xml/xml.go?s=44219:44263#L1887
【讨论】:
【参考方案3】:添加这个以防它帮助某人。
当我使用日文字符时,编码也已适当设置。但是,我时常发现htmlentities
和htmlspecialchars
不够用。
某些用户输入包含上述函数未删除的特殊字符。在这些情况下,我必须这样做:
preg_replace('/[\x00-\x1f]/','',htmlspecialchars($string))
这还将删除某些xml-unsafe
控制字符,例如Null character
或EOT
。您可以使用此table 来确定要省略哪些字符。
【讨论】:
【参考方案4】:试试这个:
$str = htmlentities($str,ENT_QUOTES,'UTF-8');
因此,在使用htmlentities()
函数过滤数据后,您可以使用 XML 标记中的数据,例如:
<mytag>$str</mytag>
【讨论】:
请添加一些解释。 使用 htmlentities 函数过滤数据后,您可以使用 XML 标签中的数据,例如如果可能的话,使用 XML 类而不是字符串操作来创建 XML 总是一个好主意 - 好处之一是类会根据需要自动转义字符。
【讨论】:
【参考方案6】:1) 您可以像这样将文本包装为 CDATA:
<mytag>
<![CDATA[Your text goes here. Btw: 5<6 and 6>5]]>
</mytag>
见http://www.w3schools.com/xml/xml_cdata.asp
2)正如已经有人说过的那样:逃避那些字符。例如。像这样:
5<6 and 6>5
【讨论】:
oops我忽略了之前的回答中已经提到了CDATA 你说得很清楚我需要做什么,所以我很感激,不管它是否已经提到过。我最终使用您的解决方案进行快速修复,但最佳实践可能是使用提到 Artefacto 的 XMLWriter,所以我给他最好的答案。 +1 表示 CDATA(但要小心,可以设置 XML 解析器将 CDATA 块排除在解析树之外)【参考方案7】:通过使用htmlspecialchars
转义这些字符,或者更合适的是使用用于构建XML 文档的库,例如DOMDocument 或XMLWriter。
另一种选择是使用 CDATA 部分,但是您必须注意 ]]>
的出现。
还要考虑到您必须尊重您为 XML 文档定义的编码(默认为 UTF-8)。
【讨论】:
htmlspecialchars 并不是最好的方法,因为顾名思义,它用于 HTML 输出,而不是 XML。例如,当 XML 的正确编码为 < 时,它会将 @Gordon 哼?从什么时候&amp;lt;
不适合 XML? htmlspecialchars
实际上只用保证可用于 any XML 文档的实体进行实体替换,甚至留下一个(当它可以使用 &apos;
时将 '
替换为 &#039;
;当然,&#039;
也是正确的)。
@Gordon 顺便说一下,htmlspecialchars
对于 XML 可能不够用有一些的原因(即,它不会替换 XML 中的禁止字符,也不会t 在 $double_encode 为 TRUE 时对禁止的实体进行编码)——顺便说一句,我已经通过在主干版本的 htmlspecialchars/entities 中引入配置文件来解决这个问题——但你所说的根本不是真的。您所描述的是双重编码,您需要在 XML 中使用 &amp;lt;
,就像在 HTML 中需要它一样——当您需要表示 &amp;lt;
时。
不确定是否 <是最好的例子,但它是 htmlspecialchars 的一个非常现实的问题。它基本上是用于 HTML 转义,而不是 XML。 PHP 为这项工作提供了比 htmlspecialchars 更好的工具,应该使用这些工具。
我在尝试在数据 (£) 中插入带有井号的字符串时遇到问题,并且 htmlentities 不起作用,我认为这不是正确的答案,除非出于某种原因我正在这样做有事吗。使用 htmlentities,它返回的字符串不被 DOMDocument::loadXML 函数接受。还有其他建议吗?以上是关于如何使字符串“XML 安全”?的主要内容,如果未能解决你的问题,请参考以下文章