前言
本月迭代需求没有几个,领导给我派了一个漏洞修复的活。这个项目是一个前后端不分离的ToB
老项目,前端使用Velocity
模板+JQuery
+miniui
,后端是用的 Spring
。嗯,前后端不分离,技术老旧,模块众多且耦合,基础设施不完善,让我从一开始校招(2022)刚进来接触到这个项目时候,就感到“畏惧、难受”。虽然后端 Spring
我还了解一些,但是一个十几年的老项目,光 Java
代码就 130 多万行,代码中还充斥着各种二开的代码,再加上业务的复杂,自己本身的知识薄弱,所以一直就是抗拒的!
抗拒能咋办,毕竟工作时常已经快两年了,还能推了不成。那就检验一下自己分析、解决问题的能力进步了没有。
XSS 漏洞
解决问题时,要先了解问题本身(名词含义),产生机制(产生背景),从而在去找一些前人的一些实践经验(文章),对比汲取,再着手去解决问题。
XSS 简介
XSS
全称:跨站脚本(Cross Site Scripting
) ,为了不和层叠样式表(Cascading Style Sheets
)的缩写CSS
混合,所以改名为XSS
;攻击者会向web
页面(input
表单、URL
、留言版等位置)插入恶意JavaScript
代码,导致管理员/用户访问时触发,从而达到攻击者的目的。
XSS 分类
反射型(非持久型)
反射型XSS
,又称非持久型XSS
,攻击相对于受害者而言是一次性的,具体表现在受害者点击了含有的恶意JavaScript
脚本的url
,恶意代码并没有保存在目标网站,而Web
应用程序只是不加处理的把该恶意脚本“反射”回受害者的浏览器而使受害者的浏览器执行相应的脚本。
举个例子:比如我在一个站点,恶意的传递特殊Url
路径参数,如:Src
一张图片,站点服务器接收到请求,并没有对路径传参做XSS
过滤,从而接收请求并将请求返回,这张照片就渲染到当前站点的页面内了,恶意的脚本被执行了!
你也可能会想,服务器端又没存储这段恶意脚本,只会影响我自己啊,不会影响其他人啊,反射型 XSS
的危害在哪呢?其实不然,如果有人诱导你去点击一个链接,然后这个网站刚好你是登录过的,你的Cookie
等凭证敏感信息都在当前的Domain
下,这个链接刚好是带有恶意脚本的,那么你的Cookie
等凭证敏感信息很可能被泄露,从而攻击者盗用你的身份,做一些恶意操作
存储型(持久型)
存储型XSS
是指应用程序通过Web请求获取不可信赖的数据,在未检验数据是否存在XSS
代码的情况下,便将其存入数据库。当下一次从数据库中获取该数据时程序也未对其进行过滤,页面再次执行XSS
代码持续攻击用户。存储型XSS
漏洞大多出现在留言板、评论区,用户提交了包含XSS
代码的留言到数据库,当目标用户查询留言时,那些留言的内容会从服务器解析之后加载出来。
DOM型(非持久型)
DOM
,全称Document Object Model
,是一个平台和语言都中立的接口,可以使程序和脚本能够动态访问和更新文档的内容、结构以及样式 ,DOM-XSS
简单理解就是不与后台服务器产生数据交互,是一种通过DOM
操作前端代码输出的时候产生的问题。
XSS 漏洞危害
- 反射型 XSS:
危害:
用户凭据泄露:攻击者可以窃取受害者的 Cookie、会话标识符等敏感信息。
执行恶意操作:恶意脚本可以执行用户未授权的操作。
钓鱼攻击:诱导用户输入敏感信息。
扩散攻击面:通过传播恶意链接影响更多用户。
- 存储型 XSS:
危害:
持久性攻击:恶意脚本存储在服务器上,任何访问受影响页面的用户都会执行这些脚本。
广泛影响:攻击可影响所有访问受感染页面的用户,导致大规模信息泄露、账户劫持等。
恶意传播:恶意脚本可能进一步传播攻击,如在论坛、留言板等公共平台上。
- DOM 型 XSS:
危害:
前端攻击:脚本直接在用户浏览器的 DOM 环境中执行,绕过了传统的服务器端防御措施。
持续性:攻击持久存在于受影响的前端代码中,通常难以通过服务器端过滤或清理。
执行恶意操作:攻击者可以操纵页面内容、修改页面结构,甚至窃取敏感信息。
XSS 漏洞处理实践
本次扫描出来的 XSS
漏洞一共有2个,反射型一个,存储型一个,下面给出实例
存储型
这是一个存储型的 XSS
,业务场景是在上传附件时,恶意构造了文件名,从而触发恶意脚本的执行。
这里,公司安全部门给出对于 XSS
漏洞的解决方法是对 html
进行实体化编码。那么什么是 html
实体化编码呢,能解决什么问题呢?带着疑问我进行了搜索。实体化编码可以简单的理解为对一些特殊字符的转义。比如说,用户上传附件的名字是 <script>alert('XSS');</script>
如果后端没有对附件名字做实体化编码或者说转义的话,那么当附件成功上传保存到数据库,然后渲染到前端html
页面的时候,浏览器会将附件的名字解释为正常的 js
脚本并执行,那么此时就发生了存储型 XSS
。
印象中项目的代码有一个 XssFilter
,所以我就点开了这个过滤器类
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse))
throw new ServletException("此过滤器只保护HTTP资源");
HttpServletRequest req = (HttpServletRequest) request;
if(StringUtil.ignoreSuffix(req.getRequestURI())){ //在可以忽略的文件后缀中,此类资源文件放行
chain.doFilter(request, response);
return;
}
if (exclude(req)) { //指定路径通过
chain.doFilter(request, response);
return;
}
XssHttpRequestWrapper xssRequest = new XssHttpRequestWrapper(req,excludeMap);
req = this.wrapRequest(xssRequest);
//过滤客户端提交表单中特殊字符
Map<String,String> rules = null;
if(req.getMethod().equalsIgnoreCase("post")){
rules = addCustomParams(req, postMap);
}else{
rules = addCustomParams(req, getMap);
}
this.filterParams(req, rules);
chain.doFilter(req, response);
}
眨眼一看上去,这不是有做 XSS
过滤的吗,为啥没有转义呢?仔细的检查了一下 XssHttpRequestWrapper
,这个请求包装类中并没有对 POST Multi form-data
请求类型做处理,而项目中附件上传请求正式此种请求类型。那好,我就加上针对此种请求的过滤,本地调试一下,我又发现居然在 HttpServletRequest
中拿不到请求参数。
针对于 POST Multi form-data
请求,没法直接在HttpServletRequest
使用 getParameterMap()
等获取请求参数,而是要使用 MultipartResolver
将请求进行处理才能拿到请求参数。
使用MultipartResolver
处理 HttpServletRequest
如下所示
此时就可以针对请求参数做 XSS
过滤了。但是最终还是没有在 Filter
中去做过滤,我去问了一下我们组的技术大佬军哥,他建议我还是不要动这个过滤器了,因为整个项目(二三十个模块微服务)全部依赖这个Filter
,既然只有附件上传时的名称有 XSS
注入风险,那就只针对附件上传方法中的附件名称做下实体化编码即可。还是大佬考虑的全面!这要是改出毛病,整个系统都”爆炸“!
反射型 XSS
公司安全部门提的反射型 XSS
,其实就是在url
路径上构造恶意脚本字符,然后服务端没有进行参数过滤,在请求成功返回后,在浏览器端执行恶意代码。
在上面的XssFilter
中对 /tab
路径进行了放行,可能是历史原因,tab
链接页面路径没有强制做XSS
防御,而是做成了管控点,还是问了军哥,军哥说你把这个管控点去掉吧。好好好!这种活我最爱了!
参考
- https://www.cnblogs.com/blbl-blog/p/17188558.html
- https://blog.csdn.net/llwutong/article/details/118162794
- https://blog.csdn.net/A_Runner/article/details/84285207