专题1:URL编码、归一化、校验、常见漏洞

allenxx / 2023-07-28 / 原文

说明

1. URL编码&解码

 1 import java.io.UnsupportedEncodingException;
 2 import java.net.URLDecoder;
 3 import java.net.URLEncoder;
 4 
 5 public class URLEncode {
 6 
 7 
 8     public static void main(String[] args) {
 9         String urlString = "http://example.com/路径/文件.html?param=值&key=value#fragment";
10 
11         try {
12             // 对参数值进行编码
13             String encodedValue = URLEncoder.encode("值", "UTF-8");
14 
15             // 对整个URL进行编码
16             String encodedUrl = encodeFullUrl(urlString);
17             // Original URL: http://example.com/路径/文件.html?param=值&key=value#fragment
18             System.out.println("Original URL: " + urlString);
19             System.out.println("Encoded Value: " + encodedValue); // Encoded Value: %E5%80%BC
20             // Encoded URL: http%3A%2F%2Fexample.com%2F%E8%B7%AF%E5%BE%84%2F%E6%96%87%E4%BB%B6.html%3Fparam%3D%E5%80%BC%26key%3Dvalue#fragment
21             System.out.println("Encoded URL: " + encodedUrl);
22             // 解码 
23             System.out.println(URLDecoder.decode(encodedUrl)); // encodedUrl
24         } catch (UnsupportedEncodingException e) {
25             e.printStackTrace();
26         }
27     }
28 
29     private static String encodeFullUrl(String urlString) throws UnsupportedEncodingException {
30         String[] parts = urlString.split("#", 2); // 分割URL和片段标识符
31 
32         String encodedUrl = URLEncoder.encode(parts[0], "UTF-8"); // 编码URL部分
33 
34         if (parts.length > 1) {
35             encodedUrl += "#" + URLEncoder.encode(parts[1], "UTF-8"); // 编码片段标识符
36         }
37 
38         return encodedUrl;
39     }
40 
41 }

场景:

1. URL参数传递:当通过URL传递参数时,需要对参数值进行编码,以确保特殊字符不会破坏URL的结构。例如:

1 String paramValue = "value with special characters";
2 String encodedParamValue = URLEncoder.encode(paramValue, "UTF-8");
3 String url = "http://example.com/?param=" + encodedParamValue;

2. URL构建:当动态构建URL时,尤其是在拼接路径、查询参数或片段标识符时,需要对每个部分进行编码。例如:

1 String path = "/path/with/special characters";
2 String encodedPath = URLEncoder.encode(path, "UTF-8");
3 String queryParam = "param=value with special characters";
4 String encodedQueryParam = URLEncoder.encode(queryParam, "UTF-8");
5 String fragment = "fragment with special characters";
6 String encodedFragment = URLEncoder.encode(fragment, "UTF-8");
7 
8 String url = "http://example.com" + encodedPath + "?q=" + encodedQueryParam + "#" + encodedFragment;

构建url就是为发送http/https请求。

3. URL解析和处理:在解析和处理URL时,有时需要对URL进行解码,以获取原始的参数值或处理特殊字符。例如:

1 String urlString = "http://example.com/path?param=value%20with%20special%20characters";
2 String decodedUrl = URLDecoder.decode(urlString, "UTF-8");
3 
4 URL url = new URL(decodedUrl);
5 String path = url.getPath();
6 String decodedParamValue = url.getQuery().split("=")[1];

4. 预防URL漏洞:对于用户输入的URL或从其他来源获取的URL,需要进行编码和解码来预防URL漏洞,如跨站脚本攻击(XSS)或路径遍历攻击。例如:

1 String userInputUrl = "http://example.com/path?param=<script>alert('XSS')</script>";
2 String encodedUrl = URLEncoder.encode(userInputUrl, "UTF-8");
3 // ...
4 String safeUrl = URLDecoder.decode(encodedUrl, "UTF-8");

 

 

2. URL归一化

  在Java中,URL归一化是指将输入的URL进行规范化处理,使其符合统一的格式和标准

最佳实践:

  1. 使用java.net.URL类:Java提供了java.net.URL类来处理URL相关操作。可以使用该类的方法对URL进行归一化处理。

  2. 使用java.net.URI类:除了URL类,还可以使用java.net.URI类来进行URL归一化。URI类提供了更灵活的处理方式,并且能够处理不合法的URL。

  3. 使用java.nio.file.Path类:如果您需要进行本地文件路径归一化,可以使用java.nio.file.Path类。它提供了许多方便的方法来操作文件路径。

常见坑:

  1. 编码问题:在处理URL时,要注意编码问题。

  2. URL格式验证:在对URL进行归一化之前,最好先进行URL的格式验证,以避免处理不合法的URL导致异常或错误结果

 1 import java.net.MalformedURLException;
 2 import java.net.URISyntaxException;
 3 import java.net.URL;
 4 
 5 /**
 6  * URL 编码、归一化、校验、使用
 7  */
 8 
 9 public class URLNormalize {
10     public static void main(String[] args) {
11         String urlString = "http://example.com/a//b/../path/file.html";
12 
13         try {
14             URL url = new URL(urlString);
15             String normalizedUrl = url.toURI().normalize().toURL().toString();
16 
17             System.out.println("Original URL: " + urlString);  // Original URL: http://example.com/a//b/../path/file.html
18             // 处理掉了// 和../
19             System.out.println("Normalized URL: " + normalizedUrl); // Normalized URL: http://example.com/a/path/file.html
20         } catch (MalformedURLException e) {
21             e.printStackTrace();
22         } catch (URISyntaxException e) {
23             throw new RuntimeException(e);
24         }
25     }
26 }

在上述示例中,我们首先定义了一个字符串urlString表示原始的URL。然后,使用new URL(urlString)创建一个URL对象。接下来,我们将URL转换为URI,并调用normalize()方法对URL进行归一化处理。最后,我们将归一化后的URL转换回字符串并打印出来。

这样可以确保URL中的相对路径和冗余部分被正确处理,并获得一个符合标准的URL。

 1             // 1. 创建URL对象
 2             URL url = new URL(urlString);
 3             
 4             // 2. 将URL转换为URI对象
 5             URI uri = url.toURI();
 6             
 7             // 3. 调用normalize()方法对URI进行归一化处理
 8             URI normalizedUri = uri.normalize();
 9             
10             // 4. 将URI转换回URL对象
11             URL normalizedUrl = normalizedUri.toURL();
12 
13             System.out.println("Original URL: " + urlString);   
14             System.out.println("Normalized URL: " + normalizedUrl.toString());  // 5.
normalizedUrl.toString()获取归一化的url

场景:

  1. URL比较和匹配:当需要比较两个URL是否相同或匹配时,对URL进行归一化可以消除不必要的差异,使得比较更准确。这在去重、数据匹配和URL路由等场景下很常见。

  2. 缓存管理:在缓存系统中,URL通常用作键(key)来存储和检索缓存数据。归一化URL可以避免因为URL的差异而导致缓存失效或无法命中的情况。

  3. 防止URL重复爬取:在网络爬虫和搜索引擎中,为了避免重复爬取相同的页面,可以对URL进行归一化,并将归一化后的URL用作判断页面是否已被爬取的依据。

  4. 规范化URL展示:在用户界面中显示URL时,对URL进行归一化可以提供更统一、易读的URL展示,增强用户体验。

  5. URL路由和请求处理:在Web应用程序中,URL被用于路由请求到相应的处理程序或控制器。归一化URL可以简化路由规则和请求处理逻辑,提高代码的可维护性和可扩展性

归一化前后都需要验证:前验证的是不是url?,不然归一化会报错。后验证验证的是归一化的url,与白名单、redis中的key是否匹配。

URL常见漏洞

在Java中,URL相关的场景漏洞主要包括以下几种:

  1. 跨站脚本攻击(XSS):当从用户输入或其他不可信的来源获取URL时,如果直接将URL用于HTML页面展示而没有进行适当的转义,恶意脚本可能会被执行,导致XSS攻击。

  2. 路径遍历攻击:在处理URL路径时,如果没有对输入进行正确的验证和过滤,攻击者可能通过构造恶意的URL来访问系统中的敏感文件或目录。

  3. URL重定向攻击:当使用用户提供的URL进行重定向时,如果没有对URL进行严格的验证和限制,攻击者可以构造恶意URL来引导用户到恶意网站或欺骗用户。

为了预防这些URL场景漏洞,最佳实践:

  1. 输入验证和输出转义:

    • 对于用户输入的URL或从其他来源获取的URL,进行输入验证,确保URL符合预期的格式和内容。
    • 在将URL用于HTML页面展示时,使用适当的HTML转义方式(如StringEscapeUtils.escapeHtml())对URL进行转义,以防止XSS攻击。
  2. URL编码和解码:

    • 对于用户输入的URL或从其他来源获取的URL,进行URL编码,使用URLEncoder.encode()方法将URL参数值进行编码。
    • 在使用或显示URL之前,对URL进行解码,使用URLDecoder.decode()方法将URL参数值解码为原始形式。
  3. 限制URL访问范围:

    • 如果用户输入的URL用于访问敏感资源,应该在服务器端对URL进行权限验证和授权,确保只有经过身份验证和授权的用户可以访问。
  4. URL重定向验证:

    • 在进行URL重定向时,确保重定向目标URL是可信的,可以限制重定向到同一域名下或预定义的URL列表。