Java代码审计中的SSRF漏洞检测与防范

图片[1]-Java代码审计中的SSRF漏洞检测与防范-山海云端论坛

1.1 漏洞挖掘

原生方法:

<code>String url = request.getParameter("url"); URL u = new URL(url); // 直接打开URL,可以跨协议 InputStream inputStream = u.openStream(); // 使用URLConnection,可以跨协议 URLConnection urlConnection = u.openConnection(); // 使用HttpURLConnection,限制为http/https HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection; InputStream inputStream = urlConnection.getInputStream(); // 处理请求结果 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String inputLine; StringBuilder html = new StringBuilder(); while ((inputLine = bufferedReader.readLine()) != null) { html.append(inputLine); } response.getWriter().println("html:" + html.toString()); bufferedReader.close();</code>
图片[2]-Java代码审计中的SSRF漏洞检测与防范-山海云端论坛

第三方类库方法:

<code>// 使用Request漏洞示例 String url = request.getParameter("url"); return Request.Get(url).execute().returnContent().toString(); // 发起请求 // 使用OkHttpClient漏洞示例 String url = request.getParameter("url"); OkHttpClient client = new OkHttpClient(); com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build(); client.newCall(ok_http).execute(); // 发起请求 // 使用HttpClients漏洞示例 String url = request.getParameter("url"); CloseableHttpClient client = HttpClients.createDefault(); HttpGet httpGet = new HttpGet(url); HttpResponse httpResponse = client.execute(httpGet); // 发起请求</code>

1.2 漏洞防御

防御措施:

  • 正确处理302跳转,对跳转的地址重新进行检查。
  • 限制协议只能为http/https,防止跨协议。
  • 设置内网IP黑名单,正确判定内网IP和获取Host。
  • 设置常见Web端口白名单,防止端口扫描。

代码示例:

<code>private static int connectTime = 5 * 1000; public static boolean checkSsrf(String url) { HttpURLConnection httpURLConnection; String finalUrl = url; try { do { if (!Pattern.matches("^https?://.*/.*$", finalUrl)) { // 只允许http/https协议 return false; } if (isInnerIp(url)) { // 判断是否为内网IP return false; } httpURLConnection = (HttpURLConnection) new URL(finalUrl).openConnection(); httpURLConnection.setInstanceFollowRedirects(false); // 不跟随跳转 httpURLConnection.setUseCaches(false); // 不使用缓存 httpURLConnection.setConnectTimeout(connectTime); // 设置超时时间 httpURLConnection.connect(); // 发送DNS请求 int statusCode = httpURLConnection.getResponseCode(); if (statusCode >= 300 && statusCode <= 307 && statusCode != 304 && statusCode != 306) { String redirectedUrl = httpURLConnection.getHeaderField("Location"); if (redirectedUrl == null) break; finalUrl = redirectedUrl; // 获取到跳转后的URL,再次进行判断 } else { break; } } while (httpURLConnection.getResponseCode() != HttpURLConnection.HTTP_OK); // 如果没有返回200,继续对跳转后的链接进行检查 httpURLConnection.disconnect(); } catch (Exception e) { return true; } return true; } private static boolean isInnerIp(String url) throws URISyntaxException, UnknownHostException { URI uri = new URI(url); String host = uri.getHost(); // URL转Host InetAddress inetAddress = InetAddress.getByName(host); String ip = inetAddress.getHostAddress(); String blackSubnetlist[] = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"}; // 内网IP段 for (String subnet : blackSubnetlist) { SubnetUtils subnetUtils = new SubnetUtils(subnet); // Commons-net 3.6 if (subnetUtils.getInfo().isInRange(ip)) { return true; // 如果IP在内网段中,直接返回 } } return false; }</code>

1.3 参考资料

  • JAVA代码审计之XXE与SSRF
  • 谈一谈如何在Python开发中拒绝SSRF漏洞
© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容