Foritfy安全漏洞: Server-Side Request Forgery修复记录

前言:

公司一项目上线前,甲方爸爸要求进行代码安全扫描,且还是使用静态代码的扫描模式,出现了很多让人无语的安全漏洞,Foritfy这个工具真的是一个LJ工具,甲方爸爸外包的安全扫描公司也是NB,那么多误报,瞎报,竟然理直气壮... ... ,程序猿的头发都是被这些人给薅的。

本次说明下Server-Side Request Forgery的修复过程,记录下。

一、漏洞说明

攻击者可以影响应用程序服务器建立的网络连接时,将会发生 Server-Side Request Forgery。网络连接源自于应用程序服务器内部 IP,因此攻击者将可以使用此连接来避开网络控制,并扫描或攻击没有以其他方式暴露的内部资源。

攻击者能否劫持网络连接取决于他可以控制的 U RI 的特定部分以及用于建立连接的库。例如,控制 U RI 方案将使攻击者可以使用不同于 http 或 https 的协议,类似于下面这样:
- up://
- ldap://
- jar://
- gopher://
- m ailto://
- ssh2://
- telnet://
- expect://
攻击者将可以利用劫持的此网络连接执行下列攻击:
- 对内联网资源进行端口扫描。
- 避开防火墙。
- 攻击运行于应用程序服务器或内联网上易受攻击的程序。
- 使用 Injection 攻击或 CSRF 攻击内部/外部 W eb 应用程序。
- 使用 file:// 方案访问本地文件。
- 在 W indows 系统上,file:// 方案和 U N C 路径可以允许攻击者扫描和访问内部共享。
- 执行 D N S 缓存中毒攻击。

二、官方修复建议

请勿基于用户控制的数据建立网络连接,并要确保请求发送给预期的目的地。如果需要提供用户数据来构建目的地URI,请采用间接方法:例如创建一个合法资源名的列表,用户只能选择其中的内容。通过这种方法,就不能直接使用用户提供的输入来指定资源名称。
但在某些情况下,这种方法并不可行,因为这样一份合法资源名的列表过于庞大,维护难度过大。因此,在这种情况下,程序员通常会采用执行拒绝列表的办法。在输入之前,拒绝列表会有选择地拒绝或避免潜在的危险字符。但是,任何这样一个列表都不可能是完整的,而且将随着时间的推移而过时。更好的方法是创建一个字符列表,允许其中的字符出现在资源名称中,且只接受完全由这些被认可的字符组成的输入。

此外,如果需要,还要确保用户输入仅用于在目标系统上指定资源,但 URI 方案、主机和端口由应用程序控制。这样就可以大大减小攻击者能够造成的损害。

PS:说的头头是道,怎么处理啥都没有说。

三、修复方案

项目中主要是使用SpringBoot作为核心框架进行开发,其中调用远程服务使用的是RestTemplate,很多时候调用远程服务需要传入参数,参数有些是需要前台页面传入,如果是使用RestTemplate的postForObject/PostForList模式的,其中url不会由前台传入参数进行拼接的就没有SSRF漏洞,只要是需要进行参数在URI上面拼接的都会产生本漏洞。

按照官方修复说明,可以通过url进行处理,主要是URI的协议判断,URI包含超过一个的http/https,URI的路径白名单判断。

private Boolean validateUriSsrf(String uri) {
       
        if (!uri.startsWith("http")) {
            return false;
        }
        
        // 禁用协议校验
        String regexProtocol = "file:///|gopher://|ftp://|ftps://|jar://|mailto://ssh2://|telent://|expect://";
        Pattern pPro = Pattern.compile(regexProtocol);
        Matcher mPro = pPro.matcher(uri);
        int notAllowCount = 0;
        while (mPro.find()) {
            notAllowCount ++;
        }
        if (notAllowCount > 0) {
            return false;
        }
        //禁止多个http<懒人写法>
        String regex = "http://|https://";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(url);
        int httpCount = 0;
        while (matcher.find()) {
            httpCount++;
        }
        //httpCount>1说明存在风险
        if (httpCount > 1) {
            return false;
        }
        // 白名单
        List<String> whiteList = new ArrayList<>();
        whiteList.add("https://api.baidu.com");
        whiteList.add("https://qyapi.weixin.qq.com");
        if (whiteList.contains(uri)) {
            return true;
        }
        //此处测试全部返回true,防止出现其他未在白名单域名
        return true;
    }

此处是url过滤,可以在原来RestTemplate基础上进行封装,然后在原来的getForObject/getForList/postForObject/postForList外,新增对应的uri检测的方法,如果验证uri不通过,直接抛出异常。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值