反射型XSS
简介
XSS(跨站脚本攻击)利用浏览器对服务器内容的信任,攻击者通过在网页中注入恶意脚本,使这些脚本在用户的浏览器上执行,从而实现攻击。常见的XSS攻击危害包括窃取用户会话信息、篡改网页内容、将用户重定向到恶意网站,以及执行恶意操作(如点击劫持和钓鱼攻击)
反射型XSS:攻击者通过在URL参数中注入恶意脚本,使服务器将该脚本直接反射回用户浏览器并执行。该攻击一般不涉及数据库,而是通过服务器处理用户请求时立即返回恶意内容
1.GET型
1.探测漏洞标签(用来测试是否存在漏洞)
123<u>A</u>123
1.恶意流量劫持
重定向恶意网站-SEO引流<a href=javascript:window.location.href='http://bilibili.com'>Click Me</a>
其中里面的http://bilibili.com可以替换成实际要攻击的网站
我们点击,发现跳转到b站了
2.窃取用户信息
<a href='/other/cookie.txt' target='_blank'>点击查看</a><script src='/static/js/hackcookie.js'></script>
点击进行查看
发现存在用户ip和cookie等敏感信息
3.篡改网页内容
修改背景颜色为红色<img src οnerrοr=javascript:document.getElementsByClassName('layuimini-container')[0].style.backgroundColor='red'>
POST型同上
2.String型
发现直接弹出页面
缺陷代码
// 原生漏洞场景,未加任何过滤,Controller接口返回Json类型结果
public R vul1(String content) {
return R.ok(content);
}
// R 是对返回结果的封装工具util
// 返回结果:
// {
// "msg": "<script>alert(document.cookie)</script>",
// "code": 0
// }
// payload在json中是不会触发xss的 需要解析到页面中
// 原生漏洞场景,未加任何过滤,Controller接口返回String类型结果
public String vul2(String content) {
return content;
}
3.Content-Type问题
1.textplain: 浏览器在获取到这种文件时并不会对其进行处理,将文件设置为纯文本的形式
2.texthtml:浏览器在获取到这种文件时会自动调用html的解析器对文件进行相应的处理
缺陷代码
// Tomcat内置HttpServletResponse,Content-Type导致反射XSS
public void vul3(String type,String content, HttpServletResponse response) {
switch (type) {
case "html":
response.getWriter().print(content);
response.setContentType("text/html;charset=utf-8");
response.getWriter().flush();
break;
case "plain":
response.getWriter().print(content);
response.setContentType("text/plain;charset=utf-8");
response.getWriter().flush();
...
}
}
4.安全场景
1.用户输入验证和过滤
前端白名单:在前端代码中执行,用于过滤或验证用户输入的数据,UI友好,不安全,容易被绕过
后端白名单:后端业务逻辑处理或数据支持化层面执行,更安全,不易绕过
安全代码
// 对用户输入的数据进行验证和过滤,确保不包含恶意代码。使用白名单过滤,只允许特定类型的输入,如纯文本或指定格式的数据
// 前端校验代码
var whitelistRegex = /^[a-zA-Z0-9_\s]+$/;
// 检查输入值是否符合白名单要求
if (!whitelistRegex.test(value)) {
layer.msg('输入内容包含非法字符,请检查输入', {icon: 2, offset: '10px'});
return false; // 取消表单提交
} else {
// 正常发送请求
}
// 后端校验代码
private static final String WHITELIST_REGEX = "^[a-zA-Z0-9_\\s]+$";
private static final Pattern pattern = Pattern.compile(WHITELIST_REGEX);
Matcher matcher = pattern.matcher(content);
if (matcher.matches()){
return R.ok(content);
}else return R.error("输入内容包含非法字符,请检查输入");
2.内容安全策略-CSP防护
内容安全策略(CSP:Content Security Policy)是一种由浏览器实施的安全机制(可理解为额外的安全层),旨在减少和防范跨站脚本攻击等安全威胁
核心原理:网站通过发送一个CSP header头部(也可以在html直接设置),告诉浏览器具体的策略(什么是授权的与什么是被禁止的),从而防止恶意内容的加载和执行
CSP 指令说明:
default-src: 指定默认的加载内容的来源,如果未指定其他指令,则默认应用此指令
script-src: 指定允许加载 JavaScript 的来源
style-src: 指定允许加载样式表的来源
img-src: 指定允许加载图片的来源
connect-src: 指定允许向其发送请求的来源(例如 AJAX、WebSocket 连接等)
安全代码
// 内容安全策略(Content Security Policy)是一种由浏览器实施的安全机制,旨在减少和防范跨站脚本攻击(XSS)等安全威胁。它通过允许网站管理员定义哪些内容来源是可信任的,从而防止恶意内容的加载和执行
// 前端Meta配置
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://apis.example.com; style-src 'self' https://fonts.googleapis.com; img-src 'self' data: https://*.example.com;">
// 后端Header配置
public String safe2(String content,HttpServletResponse response) {
response.setHeader("Content-Security-Policy","default-src self");
return content;
}
3.特殊字符实体转义
实体编码(Entity Encoding)是一种将特殊字符转换为HTML实体的过程,以确保这些字符能够在HTML文档中正确显示而不会被解释为HTML标记。
常见的实体编码包括将 < 转换为 < 将 > 转换为 > 等。
PS:这里前端实体编码Demo放到了存储型XSS模块中
安全代码
// 特殊字符实体转义是一种将HTML中的特殊字符转换为预定义实体表示的过程
// 这种转义是为了确保在HTML页面中正确显示特定字符,同时避免它们被浏览器误解为HTML标签或JavaScript代码的一部分,从而导致页面结构混乱或安全漏洞
public R safe3(@ApiParam(String type, String content) {
String filterContented = "";
switch (type){
case "manual":
content = StringUtils.replace(content, "&", "&");
content = StringUtils.replace(content, "<", "<");
content = StringUtils.replace(content, ">", ">");
content = StringUtils.replace(content, "\"", """);
content = StringUtils.replace(content, "'", "'");
content = StringUtils.replace(content, "/", "/");
filterContented = content;
break;
case "spring":
filterContented = HtmlUtils.htmlEscape(content);
break;
...
}
}
4.HttpOnly配置
单个接口配置:并非所有的cookie都必须设置为HttpOnly,可能有一些cookie是需要客户端JavaScript访问的,例如用于前端操作或分析目的的cookie
全局配置场景:在整个应用程序中所有的cookie都具有HttpOnly属性,可以考虑在全局配置中进行设置
使用HttpOnly并不是绝对安全的,以下三个场景还是会存在安全问题:
1、当攻击者使用CSRF+XSS进行攻击时,可绕过绕过浏览器的安全限制
2、中间人攻击
3、恶意浏览器插件
安全代码
// HttpOnly是HTTP响应头属性,用于增强Web应用程序安全性。它防止客户端脚本访问(只能通过http/https协议访问)带有HttpOnly标记的 cookie,从而减少跨站点脚本攻击(XSS)的风险
// 单个接口配置
public R safe4(String content, HttpServletRequest request,HttpServletResponse response) {
Cookie cookie = request.getCookies()[ueditor];
cookie.setHttpOnly(true); // 设置为 HttpOnly
cookie.setMaxAge(600); // 这里设置生效时间为十分钟
cookie.setPath("/");
response.addCookie(cookie);
return R.ok(content);
}
// 全局配置
// ueditor、application.yml配置
server:
servlet:
session:
cookie:
http-only: true
// 2、Springboot配置类
@Configuration
public class ServerConfig {
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
return factory -> {
Session session = new Session();
session.getCookie().setHttpOnly(true);
factory.setSession(session);
...
}
存储型XSS
简介
存储型XSS:攻击者将恶意脚本上传到目标网站的数据库,用户访问网站时执行这些恶意脚本,达到攻击目的。该攻击经过服务器和数据库
1.漏洞场景:原生无过滤
漏洞场景:
用户交互的地方:get、post、headers、反馈与浏览、富文本编辑器、标签插入和自定义
数据输出的地方:用户资料、关键词、评论、留言、关键词、标签、说明、文件上传
缺陷代码
/ 原生漏洞场景,未加任何过滤,将用户输入存储到数据库中
// Controller层
public R vul(String content,HttpServletRequest request) {
String ua = request.getHeader("User-Agent");
final int code = xssService.insertOne(content,ua);
...
}
// Service层
public int insertOne(String content, String ua) {
final int code = xssMapper.insertAll(content,ua,DateUtil.now());
return code;
}
// Mapper层
int insertAll(String content,String ua,String date);
<insert id="insertAll">
insert into xss
(content,ua, date)
values (#{content,jdbcType=VARCHAR},#{ua,jdbcType=VARCHAR}, #{date,jdbcType=VARCHAR})
</insert>
2.安全场景:前端实体转义
前端实体转义就是将可能是包含HTML标签的内容,先中转存储为纯文本,当浏览器进行渲染时,会将纯文本内容中特殊字符进行实体转义(浏览器策略),确保可以正确展示
// 表格数据渲染
table.render({
...
cols: [
{field: 'id', title: 'ID', sort: true, width: '60', fixed: 'left'},
{field: 'content', title: 'Content', width: '200', templet: function(d){
return escapeHtml(d.content);
}},
{field: 'ua', title: 'User-Agent', width: '200', templet: function(d){
return escapeHtml(d.ua);
}},
...
// 方法一、HTML 实体转义函数
function escapeHtml(html) {
var text = document.createElement("textarea");
text.textContent = html;
return text.innerHTML;
}
// 方法二、JavaScript的文本节点
var textNode = document.createTextNode(htmlContent);
element.appendChild(textNode);
// 方法三、jQuery的text()方法
$('#element').text(htmlContent);
DOM型XSS
1.简介
DOM(Document Object Model)即文档对象模型,是HTML和XML文档的编程接口 DOM型XSS:攻击者利用客户端的DOM环境,通过操纵页面的DOM元素来注入和执行恶意脚本。该攻击不经过服务器和数据库
一些可能导致DOM XSS的SINK点:
document.write()
document.writeln()
document.domain
element.innerHTML
element.outerHTML
element.insertAdjacentHTML
element.onevent
PS:除此之外,还有URL参数注入、DOM属性注入、document.write、eval等场景,后续会进行补充
其他类型
简介
包含模版引擎解析问题、文件上传特殊文件类型、第三方依赖问题(供应链安全)...
1.漏洞场景:模版引擎解析问题
th:text用于展示纯文本,会对特殊字符进行转义
th:utext则不进行转义,直接展示原始HTML内容
当获取后端传来的参数中带有HTML标签时,th:text不会解析这些标签,而th:utext 会解析并渲染它们。这类似于Vue中的v-text和v-html
缺陷代码
public String handleTemplateInjection(String content,String type, Model model) {
if ("html".equals(type)) {
model.addAttribute("html", content);
} else if ("text".equals(type)) {
model.addAttribute("text", content);
}
return "vul/xss/other";
}
<div class="layui-card-body layui-text layadmin-text" style="color: red;font-size: 15px;">
<p th:utext="${html}"></p>
<p th:text="${text}"></p>
</div>
2.漏洞场景:文件上传导致存储XSS
除了文件上传导致存储XSS,xml场景下还需要后端进行xml解析
这里PDF型XSS实际是没有危害的,考虑到合规监管问题,还是放上去了
PS:除此之外,还有flash等漏洞场景,后续会补充
缺陷代码
public String uploadFile(MultipartFile file, String suffix,String path) throws IOException {
String uploadFolderPath = sysConstant.getUploadFolder();
try {
String fileName = +DateUtil.current() + "."+suffix;
String newFilePath = uploadFolderPath + "/" + fileName;
file.transferTo(new File(newFilePath)); // 将文件保存到指定路径
log.info("上传文件成功,文件路径:" + newFilePath);
return "上传文件成功,文件路径:" + path + fileName;
} catch (IOException e) {
e.printStackTrace(); // 打印异常堆栈信息
log.info("文件上传失败" + e.getMessage());
return "文件上传失败" + e.getMessage();
}
}
3.漏洞场景:第三方组件导致XSS-供应链安全
为什么要单独抽出来呢?
这里的XSS场景可以看出是由第三方组件导致的,也就引出了所谓的供应链安全(当然不止这些)
风险识别与修复方案?
一般企业在DevOps流水线中会嵌入相关安全扫描环境(SCA、SAST、IAST……)进行应用安全扫描
针对供应链安全,可使用专门的SCA工具进行软件成分扫描,升级到修复版本或采用非升级修复方案
1.jQuery
在版本[1.2,3.5.0)范围内存在多个XSS漏洞 eg:
CVE-2020-11022
CVE-2020-11023
2.Swagger-ui
在版本内[3.14.1,3.38.0)范围内存在XSS漏洞
3.Ueditor编辑器
poc
拼接路径/ueditor/php/controller.php?action=uploadfile
Content-Disposition: form-data; name="upfile"; filename="1.xml"
Content-Type: image/png
<html>
<head></head>
<body>
<something:script xmlns:something="http://www.w3.org/1999/xhtml" src="https://xcjh.ahredcross.org.cn/ueditor/upload/image/20240723/6385736220575785005385782.js">
</something:script>
</body>
</html>
或者
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary209pDxz5IJ3h3EeJ
> Content-Length: 348
>
> ------WebKitFormBoundary209pDxz5IJ3h3EeJ
> Content-Disposition: form-data; name="upfile"; filename="1.xml"
> Content-Type: image/png
>
> <html>
> <head></head>
> <body>
> <something:script xmlns:something="http://www.w3.org/1999/xhtml"> alert(1);
> </something:script>
> </body>
> </html>
> ------WebKitFormBoundary209pDxz5IJ3h3EeJ--
> -----------------------
缺陷代码
// jQuery依赖
<head>
<meta charset="utf-8">
<title>jQuery XSS Examples (CVE-2020-11022/CVE-2020-11023)</title>
<!-- 测试JQuery -->
<script src="/lib/jquery-1.6.1.js"></script>
<!-- <script src="./jquery.min.js"></script> -->
</head>
<!--swagger依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version> // 该版本存在xss
</dependency>
// Ueditor编辑器未做任何限制 抓上传数据包后,可以上传任意类型文件