SSRF在服务端代码中属于常见的漏洞,关于SSRF漏洞的介绍及攻击方式在网络上都有详细的资料,这两篇就写的非常详细了:

SSRF安全指北 - 博客 - 腾讯安全应急响应中心

SSRF Tips | xl7dev

目前互联网企业中的项目,主要以Java项目为主,我们知道在Java语言下,SSRF漏洞的利用场景一般比较有限,很少能通过该漏洞进行文件读取等操作,一般来说能进行漏洞利用的场景如下:

  1. 通过访问内网地址对内网服务进行探测、盲打
  2. CRLF注入
  3. 利用该接口作为DDOS攻击的一个发起点(可限制访问频率来解决,本文暂不讨论)

因此本文只针对内网探测类的SSRF攻击类型来进行防御修复讨论,由于在不同的业务场景下,制定一套相对通用的SSRF修复方案时是存在一定困难的,一般会产生两种不同的场景:

  1. 服务器需要访问的资源可以限制在一定范围之内,比如我们自己的oss服务器域名
  2. 服务器需要访问的资源无法设限,必须要访问任意的网络资源

我们根据不同的业务场景,设定了3种修复方案,其中前两种分别对应以上两个业务场景,第三种作为最终的解决方案,但实施起来比较复杂,目前无法实现,仅作为探索。

修复方案代码仅基于Java编写

1. 可对域名添加白名单的修复方案

/*
1. ssrf可增加白名单的修复方案,检查域名非法字符,白名单限制二级域名*/
public boolean ssrfFilterSecondaryDomain(String externalUrl) throws MalformedURLException {URL url = new URL(externalUrl);String domain = url.getHost();if (!domainFilter(domain)){return false;}String secondaryDomain = getSecondaryDomain(domain);List<String> domainWhiteList = new ArrayList<>();domainWhiteList.add("yuanfudao.com");domainWhiteList.add("fbcontent.cn");for (Iterator<String> it = domainWhiteList.iterator(); it.hasNext();) {String value = it.next();if(secondaryDomain.equals(value)){return true;}}return false;
}private static String getSecondaryDomain(String domain) {String[] domains = domain.split("\\.");return domains[domains.length - 2] + "." + domains[domains.length - 1];
}private static boolean domainFilter(String domain) {String regex = "[^a-zA-Z0-9\\.-]";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(domain);return !matcher.find();
}

这种场景修复起来较为简单,把服务器需要访问的资源限制在一定范围之内即可。

对url中host部分先进行域名有效字符的校验,防止使用反斜线等特殊字符进行绕过;再基于白名单对二级域名进行校验。

例如上述代码提供的ssrfFilter方法就可以对url进行一次过滤,符合规则返回true,可以使服务器该url进行访问,反之则不能访问。

其中的domainWhiteList部分可以由开发人员根据业务需求指定。

2. 无法添加白名单的修复方案

/*
2. 无法添加白名单的修复方案检测非法字符CRLF->通过域名解析ip->判断ip是否为内网地址->固定url中的host部分为该ip,同时设置headers中的host为域名,防止某些站点检测host不为域名时不能访问->请求不跟随302跳转可解决大部分ssrf问题*/
public URLConnection httpSecureAccess(String externalUrl) throws IOException {URL exurl = new URL(externalUrl);String host = exurl.getHost();String protocol = exurl.getProtocol();// 判断是否存在非法特殊字符String decode = URLDecoder.decode(externalUrl, "utf-8");if (decode.contains("\r\n")){//System.out.println("Illegal characters CRLF error");throw new IllegalArgumentException();}// 判断ip是否为内网,目前只判断ipv4InetAddress[] addresses = InetAddress.getAllByName(host);List<String> ips = new ArrayList<>();for (int i = 0; i < addresses.length; i++) {if(addresses[i] instanceof Inet4Address) {if (ipIsInner(addresses[i].getHostAddress())) {//System.out.println("Illegal address error");throw new IllegalArgumentException();} else {ips.add(addresses[i].getHostAddress());}}}// 若该域名对应多个ip则随机取值,同时重新拼接URL,设置host为域名,同时不跟随302跳转Collections.shuffle(ips);String ip = ips.get(0);String newurl = externalUrl.replaceFirst(host, ip);System.setProperty("sun.net.http.allowRestrictedHeaders", "true");URL url = new URL(newurl);// http和https请求分别处理,其他协议直接拒绝if (protocol.equals("https")) {HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();connection.setInstanceFollowRedirects(false);connection.setRequestMethod("GET");connection.setRequestProperty("Host", host);connection.setHostnameVerifier(new TrustAnyHostnameVerifier());return connection;}else if (protocol.equals("http")) {HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setInstanceFollowRedirects(false);connection.setRequestMethod("GET");connection.setRequestProperty("Host", host);return connection;}else {//System.out.println("Illegal protocol error");throw new IllegalArgumentException();}
}static class TrustAnyHostnameVerifier implements HostnameVerifier {public boolean verify(String hostname, SSLSession session){return true;}
}static List<Pattern> ipFilterRegexList = new ArrayList<>();
static {Set<String> ipFilter = new HashSet<String>();ipFilter.add("^10\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])"+ "\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])" + "\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");ipFilter.add("^172\\.(1[6789]|2[0-9]|3[01])\\" + ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])\\"+ ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");ipFilter.add("^192\\.168\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])\\"+ ".(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])$");ipFilter.add("127.0.0.1");ipFilter.add("0.0.0.0");ipFilter.add("localhost");for (String reg : ipFilter) {ipFilterRegexList.add(Pattern.compile(reg));}
}public static boolean ipIsInner(String ip) {for (Pattern reg : ipFilterRegexList) {Matcher matcher = reg.matcher(ip);if (matcher.find()){return true;}}return false;
}

由于业务需求导致无法添加域名的白名单时,可从多个角度对url进行限制,尽量防止攻击的发生。

观察公司业务发现http请求的类大都使用的是HttpURLConnection,因此上述代码也以该类作为演示,其他类具体使用到的方法可能略有不同,但修复思路是相同的。

这种情况下的修复思路如下:

  1. 限制访问方法只能使用GET
  2. 限制访问的协议只能是http或https,其他协议直接拒绝访问
  3. 检测特殊字符CRLF,存在则直接拒绝访问
  4. 设置setInstanceFollowRedirects属性为false,禁止跟随302跳转;禁止跳转的目的是防止攻击者利用跳转访问内网,从而绕过之后的黑名单校验
  5. 解析url中域名对应的ipv4地址:
    1. 该地址先过黑名单校验,不允许是内网地址,利用正则表达式对ip进行过滤
    2. 使用ip对url中的域名进行替换,访问资源时都以ip进行访问;这样可以防御DNS Rebinding类的攻击

使用ip替换域名这一操作会导致访问资源出现一些问题,比如某些站点会对headers中的host进行校验,如果host不是域名则访问会被拒绝;以及https证书校验的过程也会受阻。

以上问题的解决方案:

  1. 使用setRequestProperty方法设置host头为域名,这样使用ip访问资源则不会受阻;但必须先设置System.setProperty("sun.net.http.allowRestrictedHeaders", "true"),才可以使host字段被修改,否则不能生效,上述代码中都有所体现
  2. https请求时使用setHostnameVerifier方法直接信任该域名的证书(这样直接信任证书不确定会不会带来其他安全问题,后续再进行研究讨论)

上述代码提供的httpSecureAccess方法,输入为url,如果该url符合规范则返回一个URLConnection类的connection连接,开发人员可以直接使用这个connection进行资源读取等操作;如果url不符合规范则直接抛出异常。

这部分修复方案可以保证绝大多数情况下对SSRF进行防御,但由于对http请求进行了较多的修改,可能会产生其他问题,因此还要经过实践验证。

3. 更安全的修复方案

/*
3. 更安全的修复方案,通过代理服务器访问外网资源,且该服务器对内网隔离。可更彻底的解决ssrf问题,但修复成本较高。*/
public HttpURLConnection httpProxyAccess(String externalUrl) throws IOException {String proxyHost = "10.x.x.x";int proxyPort = 8080;String proxyUser = "";String proxyPass = "";InetSocketAddress isa = new InetSocketAddress(proxyHost, proxyPort);Proxy proxy = new Proxy(Proxy.Type.HTTP, isa);Authenticator.setDefault(new CustomAuthenticator(proxyUser, proxyPass));URL url = new URL(externalUrl);HttpURLConnection connection = (HttpURLConnection)url.openConnection(proxy);connection.setInstanceFollowRedirects(false);connection.setRequestMethod("GET");return connection;
}static class CustomAuthenticator extends Authenticator {private String user = "";private String password = "";public CustomAuthenticator(String user, String password) {this.user = user;this.password = password;}protected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(user, password.toCharArray());}
}

最安全的修复方案是增加服务器集群,专门用来对需要访问资源的服务进行代理。

可分为内网集群和外网集群, 例如需要访问外网资源的服务走外网的代理集群,且该集群对办公内网隔离。

这样一来所有访问资源的请求都由专用的服务器集群发出,避免了内网探测的风险;一旦有利用SSRF进行任意文件读取的场景,也不会对生产环境的服务器本身造成危害。

但是该方案操作难度较大,我们现在的情况还不具备设置服务器集群作为代理的条件,因此仅作为一种思路进行讨论。

SSRF漏洞修复方案相关推荐

  1. java+poodle漏洞修复_SSL3.0 POODLE漏洞修复方案

    SSL3.0 POODLE漏洞修复方案 发布时间:2014-10-15 15:02:27 关于SSLPOODLE漏洞 POODLE = Padding Oracle On Downgraded Leg ...

  2. Struts2漏洞修复方案

    Struts2漏洞修复方案 近期Struts2被曝重要漏洞,此漏洞影响struts2.0-struts2.3所有版本,可直接导致服务器被远程控制从而引起数据泄漏,影响巨大,受影响站点以电商.银行.门户 ...

  3. weblogic中ssrf漏洞修复_WebLogic SSRF 及漏洞修复

    SSRF漏洞,也称为XSPA(跨站端口攻击),问题存在于应用程序在加载用户提供的URL时,没能正确验证服务器的响应,然后就反馈回了客户端.攻击者可以利用该漏洞绕过访问限制(如防火墙),进而将受感染的服 ...

  4. 远程执行漏洞修复方案_请马上修复!SaltStack远程命令执行漏洞

    [漏洞预警]SaltStack远程命令执行漏洞(CVE-2020-11651.CVE-2020-11652) 2020年5月3日,阿里云应急响应中心监测到近日国外某安全团队披露了SaltStack存在 ...

  5. weblogic中ssrf漏洞修复_Weblogic-SSRF漏洞复现

    0x00简介 WebLogic是美国Oracle公司出品的一个application server确切的说是一个基于JAVAEE架构的中间件,BEA WebLogic是用于开发.集成.部署和管理大型分 ...

  6. Oracle 漏洞修复方案

    背景: 安全公司进行漏洞扫描,发现Oracle有安全漏洞,提供两种修复方案 方案: 1.下载安全漏洞补丁(因没有购买Oracle服务提供不了授权码不能下载,为何windows 操作系统盗版下载补丁). ...

  7. URL存在http host头攻击漏洞-修复方案

    [使用Nginx的修复方案] if ( $host !~* " 10.4.15.1| 10.9.4.9 " ) {     return 403; }  [基于tocmat的修复方 ...

  8. 网站漏洞修复方案防止SQL注入攻击漏洞

    SQL注入漏洞在网站漏洞里面属于高危漏洞,排列在前三,受影响范围较广,像asp..net.PHP.java.等程序语言编写的代码,都存在着sql注入漏洞,那么如何检测网站存在sql注入漏洞? SQL注 ...

  9. 怎么修复网站漏洞 骑士cms的漏洞修复方案

    骑士CMS是国内公司开发的一套开源人才网站系统,使用PHP语言开发以及mysql数据库的架构,2019年1月份被某安全组织检测出漏洞,目前最新版本4.2存在高危网站漏洞,通杀SQL注入漏洞,利用该网站 ...

  10. Spring Cloud Gateway高危漏洞修复方案

    小菜鸟的个人博客已经正式上线且对外开放啦- 博客访问地址:小菜鸟的大梦想 欢迎各位同学扫码关注本人公众号 ↓↓↓ 更多优质内容将 首发 公众号 2022年3月1日,Spring官方发布了关于Sprin ...

最新文章

  1. 15个应该掌握的jupyter notebook技巧
  2. cocos2d-x游戏实例(5)-A星算法(1)
  3. jsonp的使用方法
  4. Javascript:ES6语法简述
  5. 同比增长19.1%,软银第一季度净利2542亿日元
  6. 期末总结(第五章—)
  7. 几大技术体系极其应用
  8. 软件测试培训班出来好找工作么
  9. win 2008无法远程桌面服务器,Windows 2008远程桌面无法连接的几个情况分析
  10. ble_mesh学习笔记(8) 关于IV index 更新详细理解
  11. 盟军敢死队I:深入敌后--秘籍
  12. sticky-粘性布局
  13. SeedLab6: TCP/IP Attack Lab
  14. linkkitapp log for debug
  15. uniapp对接微信公众号H5微信支付、分享、小程序隐藏右上角分享胶囊
  16. 内存控制器(以位宽为16的NOR FLASH举例)
  17. 关于redis缓存穿透浅析
  18. 基于51单片机的贪吃蛇小程序(8*8LED点阵实现)by_jy
  19. CentOS7中:安装ifconfig命令
  20. android x86兼容arm so,Android SO文件的兼容和适配

热门文章

  1. java 值传递 引用传递的理解 言简意赅 一字千金
  2. 生日提醒功能-提前三天
  3. Win11dns解析状态异常怎么处理?Win11dns解析失败解决方法
  4. curl DNS解析失败crash问题
  5. html飞机大战论文,7.HTML5--飞机大战
  6. 【web前端面试题整理07】我不理解表现与数据分离。。。
  7. CTF之misc-图片隐写
  8. 一个劣质无线鼠标接收器导致笔记本无线网络连接出现故障
  9. X509数字证书格式
  10. 在计算机中NIC是什么意思?