安全漏洞整改系列(二)

发布于 2022年 01月 24日 00:21

本篇是之前安全漏洞整改系列(一)的延续,也是终结篇,希望通过这两篇内容带给大家一些关于安全问题的体验和重视,不至于漏洞真正来临的时候手忙脚乱,就像前几天的log4j2一夜间让多少程序员又白了头,宽了衣带。

 图片拍摄于西安太奥海洋馆

问题4:反射性xss存储

漏洞等级:高危

漏洞详情:存在xss漏洞,可以将恶意脚本执行到合法网站或者Web应用程序中,漏洞验证截图如下

 

 

 

漏洞危害:攻击者可以伪造网站链接,诱导他人点击,获取关键信息,如session窃取等。

 

说下这里的tabs2是做什么用的,这是一个Controller方法,对应的视图是一个velocity页面,Controller解析url参数进行一些处理返回给velocity页面。

1
2
3
4
5
6
7
Controller
setAttribute("tabsParams",tabsParams);
 
velocity
<script>
tabsParams= "$!tabsParams";
</script>

这个访问链接为什么会造成xss呢?

1
tabs2?;9805";;alert(document.cookie);"9688

velocity是一种模板引擎,最终返回给浏览器的还是html,我们看下最终生成的产物是什么样的。

1
2
3
<script>
tabsParams = "&;9805";;alert(document.cookie);"9688=null";
</script>

看到这儿其实已经很明显了,;9805";;alert(document.cookie);"9688这一串内容前后两个双引号很巧妙的破坏了原来js的语义,本来只是一个普通的赋值语句,现在却变成了三条js语句,其中就包含那条邪恶的alert(document.cookie);  

解决办法和之前很相似,对返回给前端的内容做充分转义,这里我使用的是org.owasp.encoder这个转义库,这个转义库针对多种上下文提供了很好的支持,使用的时候要区别对待,不能一概而论,否则会误杀。

1
Encode.forJavaScript(tabsParams);

 

 

 

对比看下,转义前后返回到前端的内容有何区别

转义前:

1
2
3
<script>
tabs = "&;9805";;alert(document.cookie);"9688=null";
</script>

转义后:

1
tabs = "\x26;9805\x22;;alert(document.cookie);\x229688=null";

  

可以看到字符串被转义成了\x22,保护了原有的执行语义。

 

问题5:sql注入

漏洞等级:高危

漏洞详情:***/findPage接口存在SQL注入漏洞

漏洞危害:攻击者能够直接获取数据库操作权限,查看用户信息下载用户数据

 

findPage接口是一个分页接口,会接收一个sortField字段,这个sortField最终会反映到SQL语句中,起到动态调整排序列的效果。

比如以下SQL语句:

1
select * from user order by ${sortField}

${sortField}会被替换为传入的值,比如age、name这些正常的值,还有一些非正常的值,比如1 and (SELECT 1271 from (select (sleep(10)))Ssvo),看下最后这个替换完会呈现什么效果  

1
2
3
4
5
6
7
8
9
SELECT
    *
FROM
    test.user
ORDER BY 1
    AND (SELECT
        1271
    FROM
        (SELECT (SLEEP(10))) Ssvo)

最终执行的效果就是睡眠10s,想象下如果有成百上千个sleep在数据库执行,我们的业务系统会怎样。  

 

 

 

聪明的你也许会说为什么不用预编译解决这种SQL注入呢,在这种场景之下其实是不能使用预编译的,order by后面需要一个确切的列名,如果是问号,那SQL解析阶段就会报语法错误,那有什么办法可以解决呢?

 1.控制sortField为一个有限的集合,不要直接替换到SQL中,增加一层判断

1
2
3
4
5
6
7
if(sortField.equals("age")){
  sql.replace(${sortField},"age");
}else if(sortField.equals("name")){
  sql.replace(${sortField},"name");
}else {
  throw Exception("不支持的排序列");
}

2.对sortField执行过滤,限制只能是数字、字母、下划线

1
sortField.replace("[^a-zA-Z0-9_\s+]", "");

 

问题6:任意用户密码重置

漏洞等级:高危

漏洞详情:忘记密码时,先输入一个已经存在的手机号,点击发送短信验证码,攻击者随意输入6位字符,点击下一步,拦截响应包,篡改为true,进入到设置新密码页面

漏洞危害:重置任意用户密码

 

下面是目前重置密码的流程

 

 

1.验证手机号码是否存在;

2.后台发送验证码;

3.校验验证码是否正确;

4.修改新密码;

 

这个漏洞也是一些多步操作容易遗漏的,后面的环节没有在后台校验前面步骤的合法性,就拿这个场景来说,真正修改密码时应该后台再校验前一步验证码的状态,防止攻击者绕过前端校验。

 

改造后的流程如下:

1.验证手机号码是否存在;

2.后台发送验证码;

3.校验验证码是否正确,并记录验证码状态

4.后台校验前一步验证码状态,如果合法,修改新密码

 

 

推荐阅读

1.阿里技术-如何避免出现SQL注入漏洞

https://mp.weixin.qq.com/s/mP49OCkwqSnamtop0cChdQ

 

安全问题无小事,以上两篇是我近期负责整改的一些安全漏洞的记录,希望能给大家带来些许帮助,后续如果还有这方面的内容会继续输出,我对安全领域也是小白一枚,以上内容如有纰漏,欢迎指正。

 

 

 

 

推荐文章