67

我配置了 PHP,以便打开魔术引号并关闭全局寄存器。

对于我输出的任何源自用户输入的内容,我都会尽我所能调用 htmlentities()。

我也偶尔会在我的数据库中搜索附加的 xss 中使用的常见内容,例如...

<script

我还应该做什么以及如何确保我想做的事情总是完成。

4

20 回答 20

59

转义输入并不是成功预防 XSS 的最佳方法。还必须转义输出。如果您使用 Smarty 模板引擎,您可以使用|escape:'htmlall'修饰符将所有敏感字符转换为 HTML 实体(我使用自己的|e修饰符,它是上面的别名)。

我的输入/输出安全方法是:

  • 存储未修改的用户输入(输入时没有 HTML 转义,只有通过 PDO 准备语句完成的 DB 感知转义)
  • 输出转义,取决于您使用的输出格式(例如 HTML 和 JSON 需要不同的转义规则)
于 2008-09-16T11:41:23.207 回答
18

我认为一个人不应该在输入过程中逃避任何东西,只在输出时。因为(大多数时候)你不能假设你知道数据的去向。例如,如果您的表单获取稍后出现在您发送的电子邮件中的数据,则需要不同的转义(否则恶意用户可能会重写您的电子邮件标题)。

换句话说,您只能在数据“离开”您的应用程序的最后一刻逃脱:

  • 项目清单
  • 写入 XML 文件,转义为 XML
  • 写入数据库,转义(针对特定的 DBMS)
  • 写电子邮件,逃避电子邮件
  • ETC

简而言之:

  1. 你不知道你的数据去哪儿了
  2. 数据实际上可能会出现在多个地方,需要不同的转义机制,但不是两者都需要
  3. 为错误的目标逃逸的数据真的不好。(例如,收到一封主题为“去汤米的酒吧”的电子邮件。)

如果您在输入层转义数据(或者您需要再次对其进行反转义等),则会发生 Esp #3。

PS:我会反对不使用magic_quotes的建议,那些是纯粹的邪恶!

于 2008-09-16T21:32:29.243 回答
12

有很多方法可以实现 XSS(参见http://ha.ckers.org/xss.html)而且很难捕捉。

我个人将此委托给我正在使用的当前框架(例如 Code Igniter)。虽然并不完美,但它可能比我手工制作的程序更能捕捉到。

于 2008-09-16T11:38:17.690 回答
10

这是一个很好的问题。

首先,不要在输入时转义文本,除非是为了存储安全(例如放入数据库中)。这样做的原因是您希望保留输入的内容,以便您可以以不同的方式和位置在上下文中呈现它。在此处进行更改可能会影响您以后的演示文稿。

当你去展示你的数据时,过滤掉不应该存在的东西。例如,如果没有理由让 javascript 存在,请搜索并删除它。一个简单的方法是使用strip_tags函数并且只显示您允许的 html 标签。

接下来,获取您拥有的内容并将其传递给 htmlentities 或 htmlspecialchars 以将那里的内容更改为 ascii 字符。根据上下文和您想要摆脱的内容来执行此操作。

我还建议关闭 Magic Quotes。它已从 PHP 6 中删除,使用它被认为是不好的做法。http://us3.php.net/magic_quotes的详细信息

有关更多详细信息,请查看http://ha.ckers.org/xss.html

这不是一个完整的答案,但希望足以帮助您入门。

于 2008-09-16T12:13:45.000 回答
7

里克写道:

对于我输出的任何源自用户输入的内容,我都会尽我所能调用 htmlentities()。

请参阅 Joel 关于使代码看起来错误的文章以获得帮助

于 2008-09-16T19:13:19.080 回答
5

模板库。或者至少,这是模板库应该做的。为了防止 XSS ,所有输出都应该被编码。这不是主应用程序/控制逻辑的任务,它应该仅由输出方法处理。

如果将 htmlentities() 撒在代码中,则整体设计是错误的。正如您所建议的那样,您可能会错过一两个景点。这就是为什么唯一的解决方案是严格的 html 编码-> 当输出变量被写入 html/xml 流时。

不幸的是,大多数 php 模板库只添加自己的模板语法,而不关心输出编码、本地化、html 验证或任何重要的事情。也许其他人知道适当的 php 模板库?

于 2010-04-18T02:01:08.457 回答
4

为此,我依靠PHPTAL

与 Smarty 和普通 PHP 不同,它默认转义所有输出。htmlspecialchars()这是安全性的一大胜利,因为如果您忘记或在某个地方,您的网站不会变得易受攻击|escape

XSS 是针对 HTML 的攻击,因此 HTML 输出是防止它的正确位置。您不应该尝试对数据库中的数据进行预过滤,因为您可能需要将数据输出到另一种不接受 HTML 的媒体,但有其自身的风险。

于 2008-10-16T18:39:29.937 回答
2

对于大多数网站来说,转义所有用户输入就足够了。还要确保会话 ID 不会出现在 URL 中,这样它们就不会从Referer指向另一个站点的链接中被盗。此外,如果您允许您的用户提交链接,请确保javascript:不允许任何协议链接;这些将在用户单击链接后立即执行脚本。

于 2008-09-16T11:24:39.440 回答
2

如果您担心 XSS 攻击,将输出字符串编码为 HTML 是解决方案。如果您记得将每个输出字符编码为 HTML 格式,则无法执行成功的 XSS 攻击。

阅读更多: 清理用户数据:如何以及在何处进行

于 2008-09-16T11:40:58.277 回答
2

“魔术引号”是对一些最严重的 XSS 缺陷的姑息疗法,它通过逃避输入的所有内容来工作,这是设计上的错误。唯一想要使用它的情况是,当您绝对必须使用已知的针对 XSS 粗心编写的现有 PHP 应用程序时。(在这种情况下,即使使用“魔术引号”,您也会遇到严重的麻烦。)在开发自己的应用程序时,您应该禁用“魔术引号”并遵循 XSS 安全实践。

XSS 是一种跨站点脚本漏洞,当应用程序在其 [X]HTML、CSS、ECMAscript 或其他浏览器解析的输出中包含来自外部源的字符串(用户输入、从其他网站获取等)时发生,没有适当的转义,希望像小于(在 [X]HTML 中)、单引号或双引号(ECMAscript)这样的特殊字符永远不会出现。正确的解决方案是始终根据输出语言的规则转义字符串:在 [X]HTML 中使用实体,在 ECMAscript 中使用反斜杠等。

因为很难跟踪不可信和必须转义的内容,所以最好始终转义所有“文本字符串”,而不是像 HTML 这样的语言中的“带有标记的文本”。一些编程环境通过引入几种不兼容的字符串类型使其变得更容易:“字符串”(普通文本)、“HTML 字符串”(HTML 标记)等等。这样,从“字符串”到“HTML 字符串”的直接隐式转换是不可能的,字符串成为 HTML 标记的唯一方法是通过转义函数将其传递。

“注册全局变量”虽然禁用它绝对是个好主意,但它处理的问题与 XSS 完全不同。

于 2008-09-16T11:59:02.803 回答
2

就个人而言,我会禁用magic_quotes。在 PHP5+ 中,默认情况下它是禁用的,最好像它根本不存在一样编码,因为它不会转义所有内容,它将从 PHP6 中删除。

接下来,根据您过滤的用户数据的类型,将指示下一步要做什么,例如,如果它只是文本(例如名称),那么strip_tags(trim(stripslashes()));它或检查范围使用正则表达式。

如果您期望某个范围的值,请创建一个包含有效值的数组,并且只允许这些值通过 ( in_array($userData, array(...)))。

如果您正在检查数字,请使用 is_numeric 强制整数或强制转换为特定类型,这应该可以防止人们尝试发送字符串。

如果您有 PHP5.2+,请考虑查看filter()并使用该扩展程序,该扩展程序可以过滤各种数据类型,包括电子邮件地址。文档不是特别好,但正在改进。

如果您必须处理 HTML,那么您应该考虑使用PHP Input FilterHTML Purifier 之类的东西。HTML Purifier 还将验证 HTML 的一致性。我不确定输入过滤器是否仍在开发中。两者都允许您定义一组可以使用的标签以及允许的属性。

无论您做出什么决定,请永远记住,永远不要相信来自用户(包括您自己!)的任何东西进入您的 PHP 脚本。

于 2008-09-16T19:09:23.410 回答
2

所有这些答案都很好,但从根本上说,XSS 的解决方案将是停止通过字符串操作生成 HTML 文档。

对于任何应用程序来说,过滤输入总是一个好主意。

只要使用得当,使用 htmlentities() 和朋友转义你的输出应该可以工作,但这是通过将字符串与 mysql_real_escape_string($var) 连接来创建 SQL 查询的 HTML 等价物 - 它应该可以工作,但更少的事情可以验证你的工作,可以这么说,与使用参数化查询之类的方法相比。

长期的解决方案应该是应用程序在内部构建页面,可能使用像 DOM 这样的标准接口,然后使用库(如 libxml)来处理对 XHTML/HTML/等的序列化。当然,我们离普及和足够快还有很长的路要走,但与此同时,我们必须通过字符串操作来构建我们的 HTML 文档,而这本质上风险更大。

于 2008-09-16T21:24:32.803 回答
2

我发现使用此功能有助于消除许多可能的 xss 攻击:

<?php

function h($string, $esc_type = 'htmlall')
{
    switch ($esc_type) {
        case 'css':
            $string = str_replace(array('<', '>', '\\'), array('&lt;', '&gt;', '&#47;'), $string);
            // get rid of various versions of javascript
            $string = preg_replace(
                    '/j\s*[\\\]*\s*a\s*[\\\]*\s*v\s*[\\\]*\s*a\s*[\\\]*\s*s\s*[\\\]*\s*c\s*[\\\]*\s*r\s*[\\\]*\s*i\s*[\\\]*\s*p\s*[\\\]*\s*t\s*[\\\]*\s*:/i',
                    'blocked', $string);
            $string = preg_replace(
                    '/@\s*[\\\]*\s*i\s*[\\\]*\s*m\s*[\\\]*\s*p\s*[\\\]*\s*o\s*[\\\]*\s*r\s*[\\\]*\s*t/i',
                    'blocked', $string);
            $string = preg_replace(
                    '/e\s*[\\\]*\s*x\s*[\\\]*\s*p\s*[\\\]*\s*r\s*[\\\]*\s*e\s*[\\\]*\s*s\s*[\\\]*\s*s\s*[\\\]*\s*i\s*[\\\]*\s*o\s*[\\\]*\s*n\s*[\\\]*\s*/i',
                    'blocked', $string);
            $string = preg_replace('/b\s*[\\\]*\s*i\s*[\\\]*\s*n\s*[\\\]*\s*d\s*[\\\]*\s*i\s*[\\\]*\s*n\s*[\\\]*\s*g:/i', 'blocked', $string);
                return $string;

        case 'html':
            //return htmlspecialchars($string, ENT_NOQUOTES);
            return str_replace(array('<', '>'), array('&lt;' , '&gt;'), $string);

        case 'htmlall':
            return htmlentities($string, ENT_QUOTES);
        case 'url':
            return rawurlencode($string);
        case 'query':
            return urlencode($string);

        case 'quotes':
            // escape unescaped single quotes
            return preg_replace("%(?<!\\\\)'%", "\\'", $string);

        case 'hex':
            // escape every character into hex
            $s_return = '';
            for ($x=0; $x < strlen($string); $x++) {
                $s_return .= '%' . bin2hex($string[$x]);
            }
            return $s_return;

        case 'hexentity':
            $s_return = '';
            for ($x=0; $x < strlen($string); $x++) {
                $s_return .= '&#x' . bin2hex($string[$x]) . ';';
            }
            return $s_return;

        case 'decentity':
            $s_return = '';
            for ($x=0; $x < strlen($string); $x++) {
                $s_return .= '&#' . ord($string[$x]) . ';';
            }
            return $s_return;

        case 'javascript':
            // escape quotes and backslashes, newlines, etc.
            return strtr($string, array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n','</'=>'<\/'));

        case 'mail':
            // safe way to display e-mail address on a web page
            return str_replace(array('@', '.'),array(' [AT] ', ' [DOT] '), $string);

        case 'nonstd':
            // escape non-standard chars, such as ms document quotes
            $_res = '';
            for($_i = 0, $_len = strlen($string); $_i < $_len; $_i++) {
                $_ord = ord($string{$_i});
                // non-standard char, escape it
                if($_ord >= 126){ 
                    $_res .= '&#' . $_ord . ';'; 
                } else {
                    $_res .= $string{$_i};
                }
            }
               return $_res;

        default:
            return $string;
    }
}
    
?>

来源

于 2008-09-16T21:35:01.687 回答
1

让您使用 HttpOnly 的任何会话 cookie(或所有 cookie)。在这种情况下,大多数浏览器都会对 JavaScript 隐藏 cookie 值。用户仍然可以手动复制 cookie,但这有助于防止直接脚本访问。StackOverflow 在测试期间遇到了这个问题。

这不是解决方案,只是墙上的另一块砖

于 2008-09-16T12:17:07.083 回答
1
  • 不要相信用户输入
  • 转义所有自由文本输出
  • 不要使用magic_quotes;查看是否有特定于 DBMS 的变体,或使用 PDO
  • 考虑尽可能使用仅 HTTP cookie 以避免任何恶意脚本能够劫持会话
于 2008-09-16T21:28:06.650 回答
1

您至少应该验证进入数据库的所有数据。并尝试验证所有离开数据库的数据。

mysql_real_escape_string 可以很好的防止 SQL 注入,但是 XSS 比较棘手。您应该尽可能地进行 preg_match、stip_tags 或 htmlentities!

于 2010-04-19T23:54:53.360 回答
1

当前在 PHP 应用程序中防止 XSS 的最佳方法是 HTML Purifier (http://htmlpurifier.org/)。它的一个小缺点是它是一个相当大的库,最好与 APC 之类的操作码缓存一起使用。您可以在将不受信任的内容输出到屏幕的任何地方使用它。htmlentities、htmlspecialchars、filter_input、filter_var、strip_tags 等要彻底得多。

于 2011-04-24T18:36:55.867 回答
0

使用现有的用户输入清理库来清理所有用户输入。除非你付出很多努力,否则自己实施它永远不会奏效。

于 2008-09-16T12:04:44.927 回答
0

我发现最好的方法是使用一个允许您绑定代码的类,这样您就不必担心手动转义数据。

于 2008-09-16T21:37:34.037 回答
-1

在不引起误报的站点上,很难实施彻底的 sql 注入/xss 注入预防。在 CMS 中,最终用户可能想要使用<script><object>链接到来自另一个站点的项目。

我建议让所有用户使用 NoScript 安装 FireFox ;-)

于 2008-09-16T22:02:37.800 回答