我配置了 PHP,以便打开魔术引号并关闭全局寄存器。
对于我输出的任何源自用户输入的内容,我都会尽我所能调用 htmlentities()。
我也偶尔会在我的数据库中搜索附加的 xss 中使用的常见内容,例如...
<script
我还应该做什么以及如何确保我想做的事情总是完成。
转义输入并不是成功预防 XSS 的最佳方法。还必须转义输出。如果您使用 Smarty 模板引擎,您可以使用|escape:'htmlall'
修饰符将所有敏感字符转换为 HTML 实体(我使用自己的|e
修饰符,它是上面的别名)。
我的输入/输出安全方法是:
我认为一个人不应该在输入过程中逃避任何东西,只在输出时。因为(大多数时候)你不能假设你知道数据的去向。例如,如果您的表单获取稍后出现在您发送的电子邮件中的数据,则需要不同的转义(否则恶意用户可能会重写您的电子邮件标题)。
换句话说,您只能在数据“离开”您的应用程序的最后一刻逃脱:
简而言之:
如果您在输入层转义数据(或者您需要再次对其进行反转义等),则会发生 Esp #3。
PS:我会反对不使用magic_quotes的建议,那些是纯粹的邪恶!
有很多方法可以实现 XSS(参见http://ha.ckers.org/xss.html)而且很难捕捉。
我个人将此委托给我正在使用的当前框架(例如 Code Igniter)。虽然并不完美,但它可能比我手工制作的程序更能捕捉到。
这是一个很好的问题。
首先,不要在输入时转义文本,除非是为了存储安全(例如放入数据库中)。这样做的原因是您希望保留输入的内容,以便您可以以不同的方式和位置在上下文中呈现它。在此处进行更改可能会影响您以后的演示文稿。
当你去展示你的数据时,过滤掉不应该存在的东西。例如,如果没有理由让 javascript 存在,请搜索并删除它。一个简单的方法是使用strip_tags函数并且只显示您允许的 html 标签。
接下来,获取您拥有的内容并将其传递给 htmlentities 或 htmlspecialchars 以将那里的内容更改为 ascii 字符。根据上下文和您想要摆脱的内容来执行此操作。
我还建议关闭 Magic Quotes。它已从 PHP 6 中删除,使用它被认为是不好的做法。http://us3.php.net/magic_quotes的详细信息
有关更多详细信息,请查看http://ha.ckers.org/xss.html
这不是一个完整的答案,但希望足以帮助您入门。
里克写道:
对于我输出的任何源自用户输入的内容,我都会尽我所能调用 htmlentities()。
请参阅 Joel 关于使代码看起来错误的文章以获得帮助
模板库。或者至少,这是模板库应该做的。为了防止 XSS ,所有输出都应该被编码。这不是主应用程序/控制逻辑的任务,它应该仅由输出方法处理。
如果将 htmlentities() 撒在代码中,则整体设计是错误的。正如您所建议的那样,您可能会错过一两个景点。这就是为什么唯一的解决方案是严格的 html 编码-> 当输出变量被写入 html/xml 流时。
不幸的是,大多数 php 模板库只添加自己的模板语法,而不关心输出编码、本地化、html 验证或任何重要的事情。也许其他人知道适当的 php 模板库?
为此,我依靠PHPTAL。
与 Smarty 和普通 PHP 不同,它默认转义所有输出。htmlspecialchars()
这是安全性的一大胜利,因为如果您忘记或在某个地方,您的网站不会变得易受攻击|escape
。
XSS 是针对 HTML 的攻击,因此 HTML 输出是防止它的正确位置。您不应该尝试对数据库中的数据进行预过滤,因为您可能需要将数据输出到另一种不接受 HTML 的媒体,但有其自身的风险。
对于大多数网站来说,转义所有用户输入就足够了。还要确保会话 ID 不会出现在 URL 中,这样它们就不会从Referer
指向另一个站点的链接中被盗。此外,如果您允许您的用户提交链接,请确保javascript:
不允许任何协议链接;这些将在用户单击链接后立即执行脚本。
如果您担心 XSS 攻击,将输出字符串编码为 HTML 是解决方案。如果您记得将每个输出字符编码为 HTML 格式,则无法执行成功的 XSS 攻击。
阅读更多: 清理用户数据:如何以及在何处进行
“魔术引号”是对一些最严重的 XSS 缺陷的姑息疗法,它通过逃避输入的所有内容来工作,这是设计上的错误。唯一想要使用它的情况是,当您绝对必须使用已知的针对 XSS 粗心编写的现有 PHP 应用程序时。(在这种情况下,即使使用“魔术引号”,您也会遇到严重的麻烦。)在开发自己的应用程序时,您应该禁用“魔术引号”并遵循 XSS 安全实践。
XSS 是一种跨站点脚本漏洞,当应用程序在其 [X]HTML、CSS、ECMAscript 或其他浏览器解析的输出中包含来自外部源的字符串(用户输入、从其他网站获取等)时发生,没有适当的转义,希望像小于(在 [X]HTML 中)、单引号或双引号(ECMAscript)这样的特殊字符永远不会出现。正确的解决方案是始终根据输出语言的规则转义字符串:在 [X]HTML 中使用实体,在 ECMAscript 中使用反斜杠等。
因为很难跟踪不可信和必须转义的内容,所以最好始终转义所有“文本字符串”,而不是像 HTML 这样的语言中的“带有标记的文本”。一些编程环境通过引入几种不兼容的字符串类型使其变得更容易:“字符串”(普通文本)、“HTML 字符串”(HTML 标记)等等。这样,从“字符串”到“HTML 字符串”的直接隐式转换是不可能的,字符串成为 HTML 标记的唯一方法是通过转义函数将其传递。
“注册全局变量”虽然禁用它绝对是个好主意,但它处理的问题与 XSS 完全不同。
就个人而言,我会禁用magic_quotes。在 PHP5+ 中,默认情况下它是禁用的,最好像它根本不存在一样编码,因为它不会转义所有内容,它将从 PHP6 中删除。
接下来,根据您过滤的用户数据的类型,将指示下一步要做什么,例如,如果它只是文本(例如名称),那么strip_tags(trim(stripslashes()));
它或检查范围使用正则表达式。
如果您期望某个范围的值,请创建一个包含有效值的数组,并且只允许这些值通过 ( in_array($userData, array(...))
)。
如果您正在检查数字,请使用 is_numeric 强制整数或强制转换为特定类型,这应该可以防止人们尝试发送字符串。
如果您有 PHP5.2+,请考虑查看filter()并使用该扩展程序,该扩展程序可以过滤各种数据类型,包括电子邮件地址。文档不是特别好,但正在改进。
如果您必须处理 HTML,那么您应该考虑使用PHP Input Filter或HTML Purifier 之类的东西。HTML Purifier 还将验证 HTML 的一致性。我不确定输入过滤器是否仍在开发中。两者都允许您定义一组可以使用的标签以及允许的属性。
无论您做出什么决定,请永远记住,永远不要相信来自用户(包括您自己!)的任何东西进入您的 PHP 脚本。
所有这些答案都很好,但从根本上说,XSS 的解决方案将是停止通过字符串操作生成 HTML 文档。
对于任何应用程序来说,过滤输入总是一个好主意。
只要使用得当,使用 htmlentities() 和朋友转义你的输出应该可以工作,但这是通过将字符串与 mysql_real_escape_string($var) 连接来创建 SQL 查询的 HTML 等价物 - 它应该可以工作,但更少的事情可以验证你的工作,可以这么说,与使用参数化查询之类的方法相比。
长期的解决方案应该是应用程序在内部构建页面,可能使用像 DOM 这样的标准接口,然后使用库(如 libxml)来处理对 XHTML/HTML/等的序列化。当然,我们离普及和足够快还有很长的路要走,但与此同时,我们必须通过字符串操作来构建我们的 HTML 文档,而这本质上风险更大。
我发现使用此功能有助于消除许多可能的 xss 攻击:
<?php
function h($string, $esc_type = 'htmlall')
{
switch ($esc_type) {
case 'css':
$string = str_replace(array('<', '>', '\\'), array('<', '>', '/'), $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('<' , '>'), $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;
}
}
?>
让您使用 HttpOnly 的任何会话 cookie(或所有 cookie)。在这种情况下,大多数浏览器都会对 JavaScript 隐藏 cookie 值。用户仍然可以手动复制 cookie,但这有助于防止直接脚本访问。StackOverflow 在测试期间遇到了这个问题。
这不是解决方案,只是墙上的另一块砖
您至少应该验证进入数据库的所有数据。并尝试验证所有离开数据库的数据。
mysql_real_escape_string 可以很好的防止 SQL 注入,但是 XSS 比较棘手。您应该尽可能地进行 preg_match、stip_tags 或 htmlentities!
当前在 PHP 应用程序中防止 XSS 的最佳方法是 HTML Purifier (http://htmlpurifier.org/)。它的一个小缺点是它是一个相当大的库,最好与 APC 之类的操作码缓存一起使用。您可以在将不受信任的内容输出到屏幕的任何地方使用它。htmlentities、htmlspecialchars、filter_input、filter_var、strip_tags 等要彻底得多。
使用现有的用户输入清理库来清理所有用户输入。除非你付出很多努力,否则自己实施它永远不会奏效。
我发现最好的方法是使用一个允许您绑定代码的类,这样您就不必担心手动转义数据。
在不引起误报的站点上,很难实施彻底的 sql 注入/xss 注入预防。在 CMS 中,最终用户可能想要使用<script>
或<object>
链接到来自另一个站点的项目。
我建议让所有用户使用 NoScript 安装 FireFox ;-)