如何使字符串“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 = '&#34;';
                break;
            case "'":
                $esc = '&#39;';
                break;
            case '&':
                $esc = '&amp;';
                break;
            case '<':
                $esc = '&lt;';
                break;
            case '>':
                $esc = '&gt;';
                break;
            case "\t":
                $esc = '&#x9;';
                break;
            case "\n":
                if (!$escapeNewline) 
                    continue 2;
                
                $esc = '&#xA;';
                break;
            case "\r":
                $esc = '&#xD;';
                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】:

添加这个以防它帮助某人。

当我使用日文字符时,编码也已适当设置。但是,我时常发现htmlentitieshtmlspecialchars不够用。

某些用户输入包含上述函数未删除的特殊字符。在这些情况下,我必须这样做:

preg_replace('/[\x00-\x1f]/','',htmlspecialchars($string))

这还将删除某些xml-unsafe 控制字符,例如Null characterEOT。您可以使用此table 来确定要省略哪些字符。

【讨论】:

【参考方案4】:

试试这个:

$str = htmlentities($str,ENT_QUOTES,'UTF-8');

因此,在使用htmlentities() 函数过滤数据后,您可以使用 XML 标记中的数据,例如:

<mytag>$str</mytag>

【讨论】:

请添加一些解释。 使用 htmlentities 函数过滤数据后,您可以使用 XML 标签中的数据,例如 $str【参考方案5】:

如果可能的话,使用 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&lt;6 and 6&gt;5

【讨论】:

oops我忽略了之前的回答中已经提到了CDATA 你说得很清楚我需要做什么,所以我很感激,不管它是否已经提到过。我最终使用您的解决方案进行快速修复,但最佳实践可能是使用提到 Artefacto 的 XMLWriter,所以我给他最好的答案。 +1 表示 CDATA(但要小心,可以设置 XML 解析器将 CDATA 块排除在解析树之外)【参考方案7】:

通过使用htmlspecialchars 转义这些字符,或者更合适的是使用用于构建XML 文档的库,例如DOMDocument 或XMLWriter。

另一种选择是使用 CDATA 部分,但是您必须注意 ]]&gt; 的出现。

还要考虑到您必须尊重您为 XML 文档定义的编码(默认为 UTF-8)。

【讨论】:

htmlspecialchars 并不是最好的方法,因为顾名思义,它用于 HTML 输出,而不是 XML。例如,当 XML 的正确编码为 &lt; 时,它会将 @Gordon 哼?从什么时候&amp;amp;lt; 不适合 XML? htmlspecialchars 实际上只用保证可用于 any XML 文档的实体进行实体替换,甚至留下一个(当它可以使用 &amp;apos; 时将 ' 替换为 &amp;#039;;当然,&amp;#039; 也是正确的)。 @Gordon 顺便说一下,htmlspecialchars 对于 XML 可能不够用有一些的原因(即,它不会替换 XML 中的禁止字符,也不会t 在 $double_encode 为 TRUE 时对禁止的实体进行编码)——顺便说一句,我已经通过在主干版本的 htmlspecialchars/entities 中引入配置文件来解决这个问题——但你所说的根本不是真的。您所描述的是双重编码,您需要在 XML 中使用 &amp;amp;lt;,就像在 HTML 中需要它一样——当您需要表示 &amp;amp;lt; 时。 不确定是否 <是最好的例子,但它是 htmlspecialchars 的一个非常现实的问题。它基本上是用于 HTML 转义,而不是 XML。 PHP 为这项工作提供了比 htmlspecialchars 更好的工具,应该使用这些工具。 我在尝试在数据 (£) 中插入带有井号的字符串时遇到问题,并且 htmlentities 不起作用,我认为这不是正确的答案,除非出于某种原因我正在这样做有事吗。使用 htmlentities,它返回的字符串不被 DOMDocument::loadXML 函数接受。还有其他建议吗?

以上是关于如何使字符串“XML 安全”?的主要内容,如果未能解决你的问题,请参考以下文章

PHP 用XML安全代码替换字符

基数排序时如何使字符串粘在一起?

如何使我的字符串属性可以为空?

如何使字符串的第一个字母大写? [复制]

如何使 SignalR 承载身份验证安全或如何不记录查询字符串

如何使javascript字符串成为函数调用[重复]