后端接口防止XSS漏洞攻击

有这样一个场景,首先构建一个docx文件并插入超链接(恶意的链接),上传到文件服务器后获取对应的文件filekey。现在我们提供一个预览接口,通过filekey便可以预览,在根据filekey转html文档返回给页面的时候由于插入的超链接成功触发XSS攻击;

初始预览接口代码示例如下:

java 复制代码
    @GetMapping(value = "/wordConvertHtml/{fileKey}")
    @ApiImplicitParam(name = "fileKey", value = "文件唯一key", required = true)
    public void wordConvertHtml(@PathVariable String fileKey, HttpServletResponse response) throws IOException {
        OutputStream bos = null;
        try {
            InputStreamVO inputStreamVO = commonFileService.getInputStreamWithAuthCheck(fileKey);
            Document doc = new Document(inputStreamVO.getInputStream());
            HtmlSaveOptions opts = new HtmlSaveOptions(SaveFormat.HTML);
            opts.setExportXhtmlTransitional(true);
            opts.setExportImagesAsBase64(true);
            opts.setExportPageSetup(true);
            opts.setPrettyFormat(true);
        }catch (FileCorruptedException e) {
            log.error("word转换HTML发生特定异常:{}", e);
            // 给前端提示特殊信息
            PrintWriter out = new PrintWriter(bos);
            out.println("文件含有特殊格式,系统不兼容。");
            out.flush();
            out.close();
        } catch (Exception e) {
            log.error("word转换HTML异常", e);
            // 给前端提示信息
            response.setContentType("text/html; charset=UTF-8");
            PrintWriter out = new PrintWriter(bos);
            out.println(e.getMessage());
            out.flush();
            out.close();
        } finally {
            if (bos != null) {
                bos.close();
            }
        }
    }

处理方式:

添加更全面的安全头设置:确保所有可能返回HTML内容的路径都设置了适当的安全头

1.Content-Security-Policy (CSP)

java 复制代码
response.setHeader("Content-Security-Policy", "default-src 'none'; script-src 'none'; style-src 'unsafe-inline'; img-src data:;");

这是一个重要的安全特性,用于防止跨站脚本攻击(XSS)、点击劫持等攻击:

  • default-src 'none': 默认情况下不允许从任何源加载资源
  • script-src 'none': 不允许执行任何JavaScript代码
  • style-src 'unsafe-inline': 允许内联样式(如<style>标签和style属性)
  • img-src data:: 只允许通过data URI加载图片

这种配置可以有效防止恶意脚本注入,同时允许内联样式和内嵌图片显示。

2.X-Content-Type-Options

java 复制代码
response.setHeader("X-Content-Type-Options", "nosniff");

防止浏览器尝试猜测或"嗅探"响应的内容类型:

  • 浏览器会严格按照服务器提供的Content-Type头来处理内容
  • 防止某些类型的攻击,比如将JavaScript文件当作图片显示

3.X-Frame-Options

java 复制代码
response.setHeader("X-Frame-Options", "DENY");

防止页面被嵌入到iframe中,用于防止点击劫持攻击:

  • DENY: 页面不能被嵌入到任何iframe中
  • 这可以防止恶意网站将你的页面嵌入到他们的网站中进行钓鱼攻击

4.X-XSS-Protection

java 复制代码
response.setHeader("X-XSS-Protection", "1; mode=block");

启用浏览器的内置XSS过滤器:

  • 1: 启用XSS过滤
  • mode=block: 当检测到XSS攻击时,浏览器会阻止整个页面加载,而不是仅仅过滤掉可疑内容

最后统一对错误进行处理,这样安全等级会更高。

java 复制代码
@GetMapping(value = "/wordConvertHtml/{fileKey}")
@ApiImplicitParam(name = "fileKey", value = "文件唯一key", required = true)
public void wordConvertHtml(@PathVariable String fileKey, HttpServletResponse response) throws IOException {
    OutputStream bos = null;
    try {
        bos = response.getOutputStream();
        response.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline';");
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.setHeader("X-Frame-Options", "DENY");
        response.setHeader("X-XSS-Protection", "1; mode=block");
        InputStreamVO inputStreamVO = commonFileService.getInputStreamWithAuthCheck(fileKey);
        Document doc = new Document(inputStreamVO.getInputStream());
        HtmlSaveOptions opts = new HtmlSaveOptions(SaveFormat.HTML);
        opts.setExportXhtmlTransitional(true);
        opts.setExportImagesAsBase64(true);
        opts.setExportPageSetup(true);
        opts.setPrettyFormat(true);
        doc.save(bos, opts);
    } catch (FileCorruptedException e) {
        log.error("word转换HTML发生特定异常:{}", e);
        // 给前端提示特殊信息
        // 设置安全响应头
        response.setContentType("text/html; charset=UTF-8");
        response.setHeader("Content-Security-Policy", "default-src 'none'; script-src 'none';");
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.setHeader("X-Frame-Options", "DENY");
        PrintWriter out = new PrintWriter(bos);
        out.println("文件含有特殊格式,系统不兼容。");
        out.flush();
        out.close();
    } catch (Exception e) {
        log.error("word转换HTML异常", e);
        // 给前端提示信息
        // 设置安全响应头
        response.setContentType("text/html; charset=UTF-8");
        response.setHeader("Content-Security-Policy", "default-src 'none'; script-src 'none';");
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.setHeader("X-Frame-Options", "DENY");
        PrintWriter out = new PrintWriter(bos);
        out.println(HtmlUtils.htmlEscape(e.getMessage()));
        out.flush();
        out.close();
    } finally {
        if (bos != null) {
            bos.close();
        }
    }
}
相关推荐
清铎1 天前
大模型训练_week3_day15_Llama概念_《穷途末路》
前端·javascript·人工智能·深度学习·自然语言处理·easyui
岛泪1 天前
把 el-cascader 的 options 平铺为一维数组(只要叶子节点)
前端·javascript·vue.js
Kiyra1 天前
阅读 Netty 源码关于 NioEventLoop 和 Channel 初始化部分的思考
运维·服务器·前端
冰暮流星1 天前
javascript的switch语句介绍
java·前端·javascript
做科研的周师兄1 天前
【MATLAB 实战】|多波段栅格数据提取部分波段均值——批量处理(NoData 修正 + 地理信息保真)_后附完整代码
前端·算法·机器学习·matlab·均值算法·分类·数据挖掘
da_vinci_x1 天前
图标量产:从“手绘地狱”到“风格克隆”?Style Reference 的工业化实战
前端·游戏·ui·prompt·aigc·设计师·游戏美术
利刃大大1 天前
【ES6】变量与常量 && 模板字符串 && 对象 && 解构赋值 && 箭头函数 && 数组 && 扩展运算符 && Promise/Await/Async
开发语言·前端·javascript·es6
天若有情6731 天前
ES6 模块与 CommonJS 的区别详解
前端·javascript·es6
大猫会长1 天前
postgreSQL中,RLS的using与with check
开发语言·前端·javascript
慧一居士1 天前
vite.config.ts 配置使用说明,完整配置示例
前端