你如何使字符串“XML安全”?

时间:2010-08-06 17:14:31

标签: php xml cakephp

我通过PHP回声发送XML文档来响应AJAX调用。为了形成这个XML文档,我遍历数据库的记录。问题是数据库包含具有'<'的记录其中的符号。很自然地,浏览器会在该特定位置抛出错误。如何解决这个问题?

7 个答案:

答案 0 :(得分:62)

使用htmlspecialchars转义这些字符,或者更合适地使用库来构建XML文档,例如DOMDocumentXMLWriter

另一种选择是使用CDATA部分,但是你必须注意]]>的出现次数。

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

答案 1 :(得分:59)

自PHP 5.4起,您可以使用:

htmlspecialchars($string, ENT_XML1);

您应该指定编码,例如:

htmlspecialchars($string, ENT_XML1, 'UTF-8');

更新

请注意,上面只会转换:

  • &&
  • <&lt;
  • >&gt;

如果要转义文本以用于用双引号括起来的属性:

htmlspecialchars($string, ENT_XML1 | ENT_COMPAT, 'UTF-8');
除了"&quot;&之外,

会将<转换为>

如果您的属性用单引号括起来:

htmlspecialchars($string, ENT_XML1 | ENT_QUOTES, 'UTF-8');
除了'&apos;&<之外,

会将>转换为"

(当然你甚至可以在属性之外使用它。)

请参阅the manual entry for htmlspecialchars

答案 2 :(得分:9)

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

答案 3 :(得分:5)

如果可能的话,使用XML类而不是字符串操作来创建XML总是一个好主意 - 其中一个好处是类会根据需要自动转义字符。

答案 4 :(得分:5)

试试这个:

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

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

<mytag>$str</mytag>

答案 5 :(得分:4)

添加此项以防万一。

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

某些用户输入包含未被上述功能剥离的特殊字符。在这些情况下,我必须这样做:

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

这也会删除某些xml-unsafe控制字符,例如Null characterEOT。您可以使用此table来确定要忽略的字符。

答案 6 :(得分:0)

我更喜欢Golang引用XML转义的方式(以及一些额外的内容,比如newline转义,并转义其他一些字符),所以我已将其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 = "\u{FFFD}";
                    break;
                }

                continue 2;
        }
        $w .= substr($s, $Last, $i - $Last - $Width) . $esc;
        $Last = $i;
    }
    $w .= substr($s, $Last);
    return $w;
}

请注意,由于使用mb_ord,您至少需要PHP7.2,否则您必须将其换成另一个polyfill,但这些功能对我们来说非常有用! / p>

对于任何好奇的人,这里是相关的Go源https://golang.org/src/encoding/xml/xml.go?s=44219:44263#L1887