浅谈Spring与安全约束SecurityConstraint
字数 1841 2025-08-06 12:20:59

Spring与安全约束SecurityConstraint深度解析

0x00 安全约束概述

安全约束(Security Constraint)是Java Web应用程序中实现访问控制的重要机制,用于保护Web资源(Servlet、JSP、HTML文件等)。它定义了访问资源所需的身份验证要求和安全限制。

安全约束三要素

  1. 安全约束名称:可选元素,用于管理和调试
  2. Web资源集合:要保护的资源集合(URL模式+HTTP方法)
  3. 角色名:允许访问资源的角色名称

0x01 SecurityConstraint实现原理

1.1 配置方式

web.xml配置示例

<security-constraint>
  <web-resource-collection>
    <web-resource-name>Private</web-resource-name>
    <url-pattern>/private/*</url-pattern>
    <http-method>GET</http-method>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin</role-name>
  </auth-constraint>
</security-constraint>

Spring Boot配置方式

通过@Configuration类配置,不同容器有不同的实现方式。

1.2 各容器实现细节

1.2.1 Tomcat实现

配置示例

@Bean
public TomcatServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    tomcat.addContextCustomizers(context -> {
        SecurityConstraint constraint = new SecurityConstraint();
        SecurityCollection collection = new SecurityCollection();
        collection.addPattern("/admin/*");
        constraint.addCollection(collection);
        constraint.addAuthRole("admin");
        context.addConstraint(constraint);
    });
    return tomcat;
}

处理流程

  1. AuthenticatorBase#invoke中处理安全约束
  2. 通过realm.findSecurityConstraints()查找匹配的安全约束
  3. 路径匹配逻辑:
    • 精确匹配请求URI
    • 检查/*模式(匹配任意路径)
    • 检查*.开头的后缀模式
    • 检查根路径/匹配

1.2.2 Jetty实现

依赖调整

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

配置示例

@Bean
public JettyServletWebServerFactory servletWebServerFactory() {
    JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
    factory.addServerCustomizers(server -> {
        ConstraintSecurityHandler securityHandler = ...;
        ConstraintMapping mapping = new ConstraintMapping();
        mapping.setPathSpec("/admin/*");
        Constraint constraint = new Constraint();
        constraint.setName("auth");
        constraint.setRoles(new String[]{"admin"});
        constraint.setAuthenticate(true);
        mapping.setConstraint(constraint);
        securityHandler.addConstraintMapping(mapping);
    });
    return factory;
}

处理流程

  1. 通过SecurityHandler#handle处理安全约束
  2. 使用prepareConstraintInfo获取RoleInfo
  3. 路径匹配逻辑:
    • 精确路径匹配(_exactMap)
    • 前缀匹配(_prefixMap)
    • 后缀匹配(_suffixMap)

1.2.3 Undertow实现

依赖调整

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

配置示例

@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addDeploymentInfoCustomizers(deploymentInfo -> {
        deploymentInfo.addSecurityConstraint(
            new SecurityConstraint()
                .addWebResourceCollection(
                    new WebResourceCollection()
                        .addUrlPattern("/admin/*"))
                .setEmptyRoleSemantic(SecurityInfo.EmptyRoleSemantic.DENY)
                .addRoleAllowed("admin"));
    });
    return factory;
}

处理流程

  1. 通过ServletSecurityConstraintHandler#handleRequest处理
  2. 使用securityPathMatches.getSecurityInfo()获取安全约束
  3. 路径匹配逻辑:
    • 精确路径匹配
    • 前缀匹配
    • 后缀匹配

0x02 安全约束绕过风险

2.1 各容器路径处理差异

Tomcat路径处理

  1. 移除;xxx/部分(分号参数)
  2. 标准化处理(移除多余/,处理/../)

Jetty路径处理

  1. 解码URI
  2. 规范化处理(处理/.//../)

Undertow路径处理

  1. 处理分号参数(移除;后到下一个/前的内容)
  2. 不处理目录穿越符(..)

2.2 常见绕过场景

尾部斜杠绕过

场景:配置了/admin/detail的安全约束
绕过方式:访问/admin/detail/

原理

  • Spring的AntPathMatcherPathPattern默认支持尾部/匹配
  • 安全约束配置可能未考虑尾部斜杠情况

目录穿越绕过

场景:配置了/admin/*的安全约束
绕过方式:访问/admin/../other

原理

  • Tomcat/Jetty会规范化路径,可能导致安全约束不匹配
  • Spring的PathPattern不处理..,可能导致路由匹配但安全约束不匹配

分号参数绕过

场景:配置了特定路径的安全约束
绕过方式:在路径中插入分号参数(如/admin/;param/)

原理

  • 各容器对分号参数处理方式不同
  • 可能导致路径解析不一致

0x03 安全建议

  1. 全面测试:对所有配置的安全约束进行边界测试
  2. 规范化处理:在安全约束配置中考虑各种路径变体
  3. 使用成熟框架:考虑使用Spring Security等专业安全框架
  4. 容器选择:了解所选容器的安全约束实现细节
  5. 防御性配置:配置拒绝所有未明确允许的访问

附录:各容器关键处理类

容器 关键处理类
Tomcat org.apache.catalina.authenticator.AuthenticatorBase
Jetty org.eclipse.jetty.security.SecurityHandler
Undertow io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler
Spring与安全约束SecurityConstraint深度解析 0x00 安全约束概述 安全约束(Security Constraint)是Java Web应用程序中实现访问控制的重要机制,用于保护Web资源(Servlet、JSP、HTML文件等)。它定义了访问资源所需的身份验证要求和安全限制。 安全约束三要素 安全约束名称 :可选元素,用于管理和调试 Web资源集合 :要保护的资源集合(URL模式+HTTP方法) 角色名 :允许访问资源的角色名称 0x01 SecurityConstraint实现原理 1.1 配置方式 web.xml配置示例 Spring Boot配置方式 通过 @Configuration 类配置,不同容器有不同的实现方式。 1.2 各容器实现细节 1.2.1 Tomcat实现 配置示例 : 处理流程 : 在 AuthenticatorBase#invoke 中处理安全约束 通过 realm.findSecurityConstraints() 查找匹配的安全约束 路径匹配逻辑: 精确匹配请求URI 检查 /* 模式(匹配任意路径) 检查 *. 开头的后缀模式 检查根路径 / 匹配 1.2.2 Jetty实现 依赖调整 : 配置示例 : 处理流程 : 通过 SecurityHandler#handle 处理安全约束 使用 prepareConstraintInfo 获取 RoleInfo 路径匹配逻辑: 精确路径匹配( _exactMap ) 前缀匹配( _prefixMap ) 后缀匹配( _suffixMap ) 1.2.3 Undertow实现 依赖调整 : 配置示例 : 处理流程 : 通过 ServletSecurityConstraintHandler#handleRequest 处理 使用 securityPathMatches.getSecurityInfo() 获取安全约束 路径匹配逻辑: 精确路径匹配 前缀匹配 后缀匹配 0x02 安全约束绕过风险 2.1 各容器路径处理差异 Tomcat路径处理 移除 ;xxx/ 部分(分号参数) 标准化处理(移除多余 / ,处理 /../ ) Jetty路径处理 解码URI 规范化处理(处理 /./ 和 /../ ) Undertow路径处理 处理分号参数(移除 ; 后到下一个 / 前的内容) 不处理目录穿越符( .. ) 2.2 常见绕过场景 尾部斜杠绕过 场景 :配置了 /admin/detail 的安全约束 绕过方式 :访问 /admin/detail/ 原理 : Spring的 AntPathMatcher 和 PathPattern 默认支持尾部 / 匹配 安全约束配置可能未考虑尾部斜杠情况 目录穿越绕过 场景 :配置了 /admin/* 的安全约束 绕过方式 :访问 /admin/../other 原理 : Tomcat/Jetty会规范化路径,可能导致安全约束不匹配 Spring的 PathPattern 不处理 .. ,可能导致路由匹配但安全约束不匹配 分号参数绕过 场景 :配置了特定路径的安全约束 绕过方式 :在路径中插入分号参数(如 /admin/;param/ ) 原理 : 各容器对分号参数处理方式不同 可能导致路径解析不一致 0x03 安全建议 全面测试 :对所有配置的安全约束进行边界测试 规范化处理 :在安全约束配置中考虑各种路径变体 使用成熟框架 :考虑使用Spring Security等专业安全框架 容器选择 :了解所选容器的安全约束实现细节 防御性配置 :配置拒绝所有未明确允许的访问 附录:各容器关键处理类 | 容器 | 关键处理类 | |--------|-----------------------------------| | Tomcat | org.apache.catalina.authenticator.AuthenticatorBase | | Jetty | org.eclipse.jetty.security.SecurityHandler | | Undertow | io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler |