SV.HTTP_SPLIT

Http 响应拆分

当在写入 HTTP 头的方法中,存在来自 HTTP 客户端或数据库且未经验证的值时,将报告此错误。SV.XSS.* 检查器会涵盖类似攻击。

此检查器可帮助确保一致地使用输入验证。如果使用特定的验证实用工具设计代码,并且将适当的注释添加到知识库文件,则此检查器将针对忘记验证的代码路径报告问题。 请参阅调整 Java 分析。

从 2023.2 版本开始,此检查器将支持 Jakarta EE。

漏洞与风险

“HTTP 响应拆分”是一种攻击应用程序的技术。通过它可以实现各种新的攻击,例如 Web 缓存中毒、跨用户破坏、劫持含有敏感用户信息的页面,以及跨站脚本 (XSS)。大多数 Web 环境中都可能出现这种攻击技术,以及从其衍生出的其他攻击技术,从而导致应用程序不会拒绝非法的用户输入,例如恶意字符或非预期的字符。

HTTP 响应拆分的本质是攻击者可以发送能够强制 Web 服务器形成输出流的单个 HTTP 请求,该请求随后会被目标对象解释为两个 HTTP 响应,而非正常的单个响应。第一个响应可能在部分程度上被攻击者控制,但是这并不是最重要的。更重要的是攻击者可以完全控制第二个响应(标头)的形式。一旦这成为可能,攻击者就可以通过目标对象发送两个请求,从而完成攻击。第一个请求从 Web 服务器调用两个响应,而第二个请求通常会利用 Web 服务器上的某些不设防的资源。但是,目标对象会将第二个请求与第二个 HTTP 响应相匹配,而后者是完全由攻击者控制的。因此攻击者可以欺骗目标对象,使其相信 Web 服务器上的特定资源(由第二个请求指定)是服务器的 HTTP 响应(服务器内容),但实际上这是攻击者通过 Web 服务器伪造的数据,即第二个响应。

Klocwork 安全漏洞 (SV) 检查器可识别可能创建危险数据的调用;这些调用被视为不安全的来源。用户所提供的任何数据都可能是不安全的来源,因为用户可能是攻击者,或者可能引入人为错误。

缓解与预防

验证所有输入数据,即使这些数据来自数据库等不完全受信任的来源。尤其要检查回车后接换行 (CRLF) 的组合。

漏洞代码示例 1

复制
    import javax.servlet.http.*;
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String url = req.getParameter("HIDDEN_URL");
         if (url.length() == 0) {
             generatePage(req, resp);
         } else {
             resp.sendRedirect(url);
         }
     }
 
     private void generatePage(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         PrintWriter wr = resp.getWriter();
         wr.print("Hello, World!");
     }

Klocwork 在第 7 行报告 SV.HTTP_SPLIT 缺陷,指出:url 包含来自 HTTP 请求参数的数据,因此可能已被污染(第 3 行)。该值用于在第 20 行中发送 HTTP 重定向。

漏洞代码示例 2

复制
    import jakarta.servlet.http.*;
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String url = req.getParameter("HIDDEN_URL");
         if (url.length() == 0) {
             generatePage(req, resp);
         } else {
             resp.sendRedirect(url);
         }
     }
 
     private void generatePage(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         PrintWriter wr = resp.getWriter();
         wr.print("Hello, World!");
     }

Klocwork 在第 7 行报告 SV.HTTP_SPLIT 缺陷,指出:url 包含来自 HTTP 请求参数的数据,因此可能已被污染(第 3 行)。该值用于在第 20 行中发送 HTTP 重定向。

漏洞代码示例 3

复制
@GetMapping("/vuln")
    public String vuln(HttpServletResponse response, HttpServletRequest request) throws IOException {
        response.sendRedirect(request.getParameter("HIDDEN_URL"));
        return "Response with header using HttpServletResponse";
    }

Klocwork 报告了 SV.HTTP_SPLIT 缺陷,因为数据来自 HTTP 请求参数(第 3 行)。

修正代码示例

您可以通过实现许多帮助程序方法来验证输入,以便在标头字段内安全使用。

Restriction: 方法 1 和 2 仅检查是否存在换行符。这应该足以防止 SV.HTTP_SPLIT,但根据此值之后的使用方式,此方法可能并不完善。

方法 1

验证输入字符串并确保它不会导致标头拆分风险。

复制
    private static String validateNoSvHttpSplitOrThrow(String s) throws IllegalArgumentException{
        if (s.contains("\r") || s.contains("\n")){
            throw new IllegalArgumentException("unexpected SV.HTTP_SPLIT hazard");
        }
        return s;
    }

如果参数不安全,将会引发异常。

方法 2

针对标头拆分攻击,验证参数的安全性。

复制
    private static boolean validateNoSvHttpSplit(String s) {
        if (s.contains("\r") || s.contains("\n")){
            return false;
        }
        return true;
    }

如果参数可以安全使用,消息将返回 true。

方法 3

应用自定义 Java KB 来注释帮助程序函数。

复制
@Bind("SV.HTTP_SPLIT")
public class WelcomeController {
    private static String validateNoSvHttpSplitOrThrow(@Check String s);
    private static boolean validateNoSvHttpSplit(@CheckTrue String s);
}

使用带注释的帮助程序来确保参数的安全性。

复制
    @GetMapping("/checked_except")
    public String checkedExcept(HttpServletResponse response, HttpServletRequest request) throws IOException {
        response.sendRedirect(validateNoSvHttpSplitOrThrow(request.getParameter("HIDDEN_URL")));
        return "Response with header using HttpServletResponse";
    }
    
    @GetMapping("/checked_except")
    public String checkedExcept2(HttpServletResponse response, HttpServletRequest request) throws IOException {
        String hidden_url = request.getParameter("HIDDEN_URL");
        validateNoSvHttpSplitOrThrow(hidden_url);
        response.sendRedirect(hidden_url);
        return "Response with header using HttpServletResponse";
    }

    @GetMapping("/checked_bool")
    public String checkedBool(HttpServletResponse response, HttpServletRequest request) throws IOException {
        String hidden_url = request.getParameter("HIDDEN_URL");
        if (validateNoSvHttpSplit(hidden_url)) {
            response.sendRedirect(validateNoSvHttpSplitOrThrow(hidden_url));
        }else{
            return "do something else";
        }
        return "Response with header using HttpServletResponse";
    }

扩展

此检查器可通过 Klocwork 知识库进行扩展。有关详情,请参阅调整 Java 分析。