XSS攻击防御

背景和价值

好的,咱们用白话再把“输出编码防XSS”这块讲透。核心思路就一句话:把用户输入的“危险字符”变成“无害字符”再显示到页面上

为什么需要输出编码?

XSS攻击的本质是:用户输入的内容里藏了恶意代码(比如<script>盗号</script>),如果直接显示到页面上,浏览器会把它当成正常代码执行,就中招了。

输出编码的作用就是:告诉浏览器“这只是普通文本,别当代码执行”。比如把<变成&lt;,浏览器看到&lt;就知道这只是个小于号,不是标签的开始,自然就不会执行里面的脚本了。

不同场景下的编码方式(白话版)

用户输入的内容可能会显示在不同地方(比如HTML标签里、JS代码里、URL里),每个地方的“危险字符”不一样,所以编码方式也不同。

1. 最常见场景:内容显示在HTML标签里(比如<div><span>中)

例子:用户在评论框输入<script>alert('偷你cookie')</script>,你要把这段内容显示在<div class="comment"></div>里。

危险:直接显示的话,浏览器会执行<script>里的代码,弹出弹窗+偷cookie。

编码方法:HTML编码(转义特殊字符)
把这些“危险字符”换成浏览器能看懂的“安全替身”:

  • <&lt;
  • >&gt;
  • &&amp;
  • "&quot;
  • '&#39;

编码后效果
原来的<script>alert('偷你cookie')</script>会变成:
&lt;script&gt;alert(&#39;偷你cookie&#39;)&lt;/script&gt;

浏览器看到这个,就会把它当成普通文本显示成<script>alert('偷你cookie')</script>,但不会执行里面的代码。

Java里怎么实现
用OWASP的编码工具(最靠谱),maven引入依赖:

<dependency>
    <groupId>org.owasp.encoder</groupId>
    <artifactId>encoder</artifactId>
    <version>1.2.3</version>
</dependency>

然后在代码里调用:

import org.owasp.encoder.Encode;

// 用户输入的危险内容
String userInput = "<script>alert('偷你cookie')</script>";
// 进行HTML编码
String safeContent = Encode.forHtml(userInput);
// 把safeContent输出到HTML标签里,比如用Thymeleaf、JSP的<c:out>等

2. 特殊场景:内容显示在HTML标签的属性里(比如valuehref中)

例子:用户输入" onclick="alert('攻击'),你要把它放到<input type="text" value="用户输入">value里。

危险:如果直接放进去,会变成<input value="" onclick="alert('攻击')">,用户点输入框就会触发弹窗。

编码方法:HTML属性编码(比普通HTML编码更严格)
除了转义<>等,还要转义更多可能闭合属性的字符(比如空格、引号)。

Java里怎么实现
用OWASP的forHtmlAttribute方法:

String userInput = "\" onclick=\"alert('攻击')";
// 专门针对HTML属性的编码
String safeAttr = Encode.forHtmlAttribute(userInput);
// 然后放到<input value="safeAttr">里

3. 特殊场景:内容要在JavaScript代码里使用

例子:用户输入'; alert('攻击'); //,你要把它放到JS代码里:var username = '用户输入';

危险:直接放进去会变成var username = ''; alert('攻击'); //';,JS会执行alert

编码方法:JavaScript编码(把字符转成Unicode编码)
比如把单引号'转成\u0027,双引号"转成\u0022,这样JS就不会把它当成代码分隔符了。

Java里怎么实现
用OWASP的forJavaScript方法:

String userInput = "'; alert('攻击'); //";
// 专门针对JS的编码
String safeJs = Encode.forJavaScript(userInput);
// 然后放到JS代码里:var username = '"+safeJs+"';

4. 特殊场景:内容作为URL的参数(比如跳转链接里)

例子:用户输入?page=1&user=admin<script>,你要把它拼到URL里:http://xxx.com?param=用户输入

危险:直接拼接可能导致URL格式错乱,甚至注入恶意参数。

编码方法:URL编码(把特殊字符转成%xx格式)
比如空格转成%20<转成%3C,确保参数是安全的。

Java里怎么实现
URLEncoder或者OWASP的forUriComponent

import java.net.URLEncoder;

String userInput = "?page=1&user=admin<script>";
// URL编码(注意指定UTF-8编码)
String safeUrlParam = URLEncoder.encode(userInput, "UTF-8");
// 或者用OWASP的方法
String safeUrlParam2 = Encode.forUriComponent(userInput);
// 拼到URL里:http://xxx.com?param="+safeUrlParam

关键原则:“在哪里用,就用哪里的编码”

  • 显示在HTML标签里 → 用HTML编码
  • 放在HTML属性里 → 用HTML属性编码
  • 放到JS代码里 → 用JS编码
  • 作为URL参数 → 用URL编码

千万别混用!比如在JS里用HTML编码是没用的,照样可能被攻击。

总结

输出编码的核心就是:根据内容最终要出现的位置,用对应的编码方式把“危险字符”转成“无害字符”,让浏览器或脚本引擎把它们当成普通文本,而不是可执行的代码。这样不管用户输入什么花里胡哨的东西,最终都会变成安全的文本显示出来。

实际开发中,优先用成熟的编码库(比如OWASP Encoder),别自己写编码逻辑(容易漏考虑特殊情况)。

上面的代码示例展示了不同场景下的编码效果,实际开发中只需根据内容的使用位置,调用对应的编码方法即可有效防止XSS攻击。记住:编码后的数据只能在对应的场景中使用,不要跨场景复用。

参考资料

posted @ 2025-09-19 11:41  向着朝阳  阅读(36)  评论(0)    收藏  举报