如何修复跨站漏洞?
2021-02-26

修复跨站脚本漏洞最终要求基于代码的修复。推荐措施包括实施安全编程技术确保正确过滤用户提供的数据,并编码所有用户提供的数据以防以可执行的格式向终端用户发送注入的脚本。

对于开发
========

可通过仔细验证所有输入和正确编码所有输出来防范跨站脚本攻击。可使用标准的ASP.NET验证控件或直接在代码中实施验证,要尽可能使用严格的模版。

输出编码要确保在将内容发送给客户端之前对任何可脚本化的内容都进行了正确的HTML编码。可通过HttpUtility.HtmlEncode函数实现,如以下Label控件示例所示:

Label2.Text = HttpUtility.HtmlEncode(input)

要考虑用户输入通过应用可能用到的所有路径。例如,如果数据是由用户输入的,存储在数据库中,然后再重新显示,就必须要确保在每次检索的时候都能正确编码。如果必须允许自由格式文本输入(如在消息板中),而又希望允许使用一些HTML格式,则可以通过仅明确允许很小的安全标签列表来安全的处理这种情况,如下所示:

C#示例:
StringBuilder sb = new StringBuilder(HttpUtility.HtmlEncode(htmlInputTxt.Text));
sb.Replace("&lt;b&gt;", "<b>");
sb.Replace("&lt;/b&gt;", "</b>");
sb.Replace("&lt;i&gt;", "<i>");
sb.Replace("&lt;/i&gt;", "</i>");
Response.Write(sb.ToString());

VB.NET示例:
Dim sb As StringBuilder = New StringBuilder( _
HttpUtility.HtmlEncode(input));
sb.Replace("&lt;b&gt;", "<b>");
sb.Replace("&lt;/b&gt;", "</b>");
sb.Replace("&lt;i&gt;", "<i>");
sb.Replace("&lt;/i&gt;", "</i>");
Response.Write(sb.ToString());

Java示例:
public static String HTMLEncode(String aText){
final StringBuilder result = new StringBuilder();
final StringCharacterIterator iterator = new StringCharacterIterator(aText);
char character = iterator.current();
while (character != CharacterIterator.DONE ){
if (character == '<') {
result.append("&lt;");
}
else if (character == '>') {
result.append("&gt;");
}
else if (character == '&') {
result.append("&amp;");
}
else if (character == '\"') {
result.append("&quot;");
}
else {
//the char is not a special one
//add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}

以下建议可帮助构建能够抵御跨站脚本攻击的web应用。

定义允许的内容。确保web应用对所有输入参数(cookies、头、查询字符串、表单、隐藏字段等)验证严格定义的预期结果。

检查POST和GET请求的响应,确保返回内容是预期的且有效。

通过编码用户提供的数据从用户输入中删除冲突字符、括号、单双引号。这可以防范以可执行的方式向终端用户发送注入的脚本。

在可能的时候将所有客户端提供的数据仅限于字母数字的数据。使用这种过滤方案时,如果用户输入了<script>alert( 'aaa') </script>,就会被减少为scriptalert( 'aaa')script。如果必须使用非字母数字字符,在HTTP响应中使用之前将其编码为HTML实体,这样就无法将其用于修改HTML文档的结构。

使用双重用户认证机制而不是单重认证。

在修改或使用脚本之前确认其来源。

在自己的代码中使用时不要明确的信任任何来自他人的脚本,无论是从web下载还是来自熟人。

大多数服务器端脚本语言都提供了内嵌方式将输入变量的值转换为正确的不可解释HTML。应使用这种方式在将输入显示给客户端之前过滤所有输入。

PHP: string htmlspecialchars (string string [, int quote_style])

ASP / ASP.NET: Server.HTMLEncode (strHTML String)

对于安全操作
============

服务器端编码指的是首先通过编码函数发送所有的动态内容,使用所选择字符集中的代码替换Scripting标签,这可以帮助防范跨站脚本攻击。服务器端编码的缺点是可能耗费资源,对一些web服务器的性能产生负面影响。

如果必须允许站点用户使用HTML标签,如允许用户使用的格式化标签的公告栏,则应限制可使用的标签。创建可接受标签的列表,如粗体字、斜体字或下划线,并仅允许使用这些,拒绝任何其他标签。以下是一些可帮助检测跨站脚本的正则表达式。

简单跨站脚本攻击的正则表达式:
/((\%3C)|<)((\%2F)|\/)*[a-z0-9\%]+((\%3E)|>)/ix

应如下将上述正则表达式添加到新的Snort规则:

alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"NIICross-Site Scripting attempt"; flow:to_server,established;pcre:"/((\%3C)|<)((\%2F)|\/)*[a-z0-9\%]+((\%3E)|>)/i";classtype:Web-application-attack; sid:9000; rev:5;)

跨站脚本攻击的偏执行正则表达式:
/((\%3C)|<)[^\n]+((\%3E)|>)/I

这条特征仅仅查找起始的HTML标签及其对等的16进制,之后的一个或多个字符为非换行符,再之后为结尾标签或其对等的16进制。这可能导致一些误报,具体取决于Web应用和Web服务器的架构。但这种方式可以确保捕获任何攻击,甚至远程类似的跨站脚本攻击。对于公众方面,可以加强教育程序,帮助用户防范可用于帐号劫持和其他形式身份窃取的在线欺诈,如网络钓鱼。