JAVA:Web安全防御

目录

一、Web安全基础与常见威胁

  1. OWASP Top 10核心漏洞解析 • SQL注入(SQLi)、跨站脚本(XSS)、跨站请求伪造(CSRF) • 不安全的反序列化、敏感数据泄露

  2. Java后端常见攻击场景 • 通过HttpServletRequest的请求参数篡改 • JWT令牌伪造与越权访问

  3. 安全防御目标 • 代码层防御(输入校验、输出编码) • 架构层防护(HTTPS、权限控制)


二、认证与授权安全实战

  1. 安全的用户认证方案 • 密码存储:BCrypt算法与盐值加密(Spring Security实现) • 多因素认证(MFA):整合短信/邮箱验证码

  2. OAuth 2.0与JWT安全实践 • 授权码模式防钓鱼攻击 • JWT签名算法选择(HS256 vs RS256)、令牌刷新机制

  3. 细粒度权限控制 • Spring Security的RBAC实现(角色继承、动态权限) • 接口级权限注解(@PreAuthorize("hasRole('ADMIN')")


三、输入校验与数据安全

  1. 请求参数安全处理 • 使用Hibernate Validator进行参数校验(@NotBlank, @Pattern) • 防御SQL注入:MyBatis的#{}占位符 vs ${}拼接风险

  2. XSS防御与输出编码 • Thymeleaf自动转义(th:text vs th:utext) • 自定义HttpServletResponseWrapper过滤敏感数据

  3. 文件上传安全 • 限制文件类型、大小、重命名存储 • 病毒扫描(集成ClamAV)


四、会话管理与通信安全

  1. Session安全防护 • Session固定攻击防御(登录后重置Session ID) • Cookie属性设置(Secure, HttpOnly, SameSite)

  2. HTTPS配置最佳实践 • 免费证书申请(Let's Encrypt) • Spring Boot中强制HTTPS跳转

  3. CSRF防御方案 • Spring Security的CSRF Token机制 • 前后端分离架构下的CSRF防御(双重Cookie验证)


五、API安全与微服务防护

  1. API接口安全设计 • 接口版本控制、限流(Rate Limiting) • 使用Swagger/OAS3生成安全的API文档

  2. 微服务安全架构 • Spring Cloud Gateway的鉴权过滤(JWT解析) • 服务间通信安全(mTLS双向认证)

  3. 第三方API集成风险 • 密钥管理(Vault或KMS动态获取) • 请求签名(HMAC-SHA256)


六、日志监控与应急响应

  1. 安全日志记录 • 敏感操作日志(登录、支付)的审计追踪 • 使用Log4j2/SLF4J标记高风险事件

  2. 异常监控与告警 • 集成Prometheus监控异常请求(如频繁401错误) • 邮件/钉钉通知(通过Spring Boot Actuator)

  3. 漏洞应急响应 • 热修复(Arthas动态修改代码) • 快速回滚(Docker+K8s版本管理)


七、面试高频安全题解析

  1. 经典面试题 • 如何防止JWT令牌被盗用? • 解释Spring Security的过滤器链机制

  2. 场景设计题 • 设计一个安全的用户注册/登录流程 • 如何优化一个存在SQL注入漏洞的老系统?

  3. 陷阱题 • 为什么说@PreAuthorize注解不能完全替代权限校验? • HTTPS能否防御CSRF攻击?为什么?


一、Web安全基础与常见威胁


1. OWASP Top 10核心漏洞解析

1.1 SQL注入(SQLi)

攻击原理:攻击者通过构造恶意输入参数,篡改SQL语句逻辑,实现非授权数据访问或破坏。 • Java代码示例(漏洞场景)

// 错误示例:直接拼接SQL语句  
String sql = "SELECT * FROM users WHERE username = '" + request.getParameter("username") + "'";  
jdbcTemplate.query(sql, ...);  

防御方案: • 预编译语句(PreparedStatement)java String sql = "SELECT * FROM users WHERE username = ?"; jdbcTemplate.query(sql, new Object[]{request.getParameter("username")}, ...);ORM框架防御:使用JPA/Hibernate的命名参数查询: java @Query("SELECT u FROM User u WHERE u.username = :username") User findByUsername(@Param("username") String username);

1.2 跨站脚本(XSS)

攻击原理:用户输入未过滤,恶意脚本被浏览器执行(如窃取Cookie)。 • Java代码示例(漏洞场景)

// 错误示例:直接输出用户输入到HTML  
model.addAttribute("message", request.getParameter("content"));  
  
 

防御方案: • 输出编码:Thymeleaf自动转义(默认启用): html

自定义过滤器:对特殊字符(<, >, &)进行转义: java String safeOutput = StringEscapeUtils.escapeHtml4(rawInput);

1.3 跨站请求伪造(CSRF)

攻击原理:诱导用户点击恶意链接,以用户身份执行非授权操作(如转账)。 • 防御方案: • Spring Security默认防护:自动生成CSRF Token并校验。 java // 启用CSRF防护(默认开启) http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());前端集成:在表单或Header中携带Token: html

1.4 不安全的反序列化

攻击原理:恶意序列化数据触发远程代码执行(如Apache Commons Collections漏洞)。 • 防御方案: • 白名单校验:使用Jackson的@JsonTypeInfo限制反序列化类: java @JsonTypeInfo(use = Id.NAME, property = "type") @JsonSubTypes({@Type(value = SafeClass.class, name = "safe")}) public abstract class BaseClass {}JVM参数限制:禁止危险类的反序列化: bash -Djdk.serialFilter=!org.apache.commons.collections4.*

1.5 敏感数据泄露

攻击场景: • 日志中打印用户密码(logger.info("User password: {}", password))。 • API响应中暴露数据库字段(如返回User实体所有属性)。 • 防御方案: • DTO隔离敏感字段java public class UserDto { private String username; // 不包含password字段 }Spring Boot配置:屏蔽敏感信息的日志输出: yaml logging: level: org.springframework: INFO pattern: console: "%d %-5level [%thread] %logger{36} - %msg%n" filter: deny: password,secret


2. Java后端常见攻击场景

2.1 通过HttpServletRequest的请求参数篡改

场景示例:用户修改URL中的id参数越权访问他人数据:

GET /api/orders/123 → 篡改为 GET /api/orders/456  

防御方案: • 权限校验:在Service层验证数据归属: java public Order getOrder(Long orderId) { Order order = orderRepository.findById(orderId); if (!order.getUserId().equals(currentUserId())) { throw new AccessDeniedException("无权访问"); } return order; }

2.2 JWT令牌伪造与越权访问

攻击原理:窃取JWT令牌或伪造签名访问他人数据。 • 防御方案: • 强签名算法:使用RS256(非对称加密)而非HS256(对称加密): yaml # application.yml jwt: algorithm: RS256 public-key: classpath:public_key.pem令牌失效机制:维护令牌黑名单(Redis记录失效Token): java // 登出时加入黑名单 redisTemplate.opsForValue().set("jwt:invalid:" + token, "1", 5, TimeUnit.MINUTES); // 校验时检查黑名单 if (redisTemplate.hasKey("jwt:invalid:" + token)) { throw new InvalidTokenException("Token已失效"); }


3. 安全防御目标

3.1 代码层防御

输入校验:在Controller层拦截非法参数:

@PostMapping("/users")  
public ResponseEntity createUser(@Valid @RequestBody UserCreateRequest request) {  
    // 校验通过后才执行业务逻辑  
    return ResponseEntity.ok(userService.create(request));  
}  

输出编码:全局配置JSON序列化转义:

@Bean  
public Jackson2ObjectMapperBuilderCustomizer jsonEscape() {  
    return builder -> builder.featuresToEnable(JsonWriteFeature.ESCAPE_NON_ASCII);  
}  
3.2 架构层防护

HTTPS强制启用

@Configuration  
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {  
    @Override  
    protected void configure(HttpSecurity http) throws Exception {  
        http.requiresChannel().anyRequest().requiresSecure();  
    }  
}  

权限控制:基于角色的接口访问控制:

@PreAuthorize("hasRole('ADMIN')")  
@DeleteMapping("/users/{id}")  
public void deleteUser(@PathVariable Long id) {  
    userService.delete(id);  
}  

总结

漏洞本质:信任未经验证的外部输入。 • 防御核心: • 不信任原则:所有输入均需校验,所有输出均需编码。 • 最小权限原则:用户只能访问必需资源。 • 开发习惯:使用安全框架(如Spring Security)而非手动造轮子,定期依赖扫描(Maven Dependency Check)。


二、认证与授权安全实战


1. 安全的用户认证方案

1.1 密码存储:BCrypt算法与盐值加密

为何选择BCrypt: • 抗彩虹表攻击:BCrypt自动生成随机盐值(Salt),相同密码哈希值不同。 • 计算成本可调:通过strength参数控制哈希迭代次数(默认10,约100ms)。 • Spring Security集成

@Configuration  
public class SecurityConfig extends WebSecurityConfigurerAdapter {  
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder(12);  // 强度参数12(2^12次迭代)  
    }  
}  

密码存储示例

// 用户注册时加密密码  
String rawPassword = "user123";  
String encodedPassword = passwordEncoder().encode(rawPassword);  
userRepository.save(new User(username, encodedPassword));  
1.2 多因素认证(MFA)整合

短信验证码流程

  1. 用户输入用户名密码完成初步认证。

  2. 生成6位随机码存入Redis(5分钟过期)。

    String code = String.format("%06d", new Random().nextInt(999999));  
    redisTemplate.opsForValue().set("mfa:" + username, code, 5, TimeUnit.MINUTES);  
  3. 调用短信服务商API发送验证码到用户手机。

  4. 用户提交验证码,系统校验通过后发放Token。

Spring Security扩展点

public class MfaAuthenticationFilter extends UsernamePasswordAuthenticationFilter {  
    @Override  
    public Authentication attemptAuthentication(HttpServletRequest request,  
            HttpServletResponse response) throws AuthenticationException {  
        // 验证短信验证码逻辑  
        String code = request.getParameter("code");  
        String cachedCode = redisTemplate.opsForValue().get("mfa:" + username);  
        if (!code.equals(cachedCode)) {  
            throw new BadCredentialsException("验证码错误");  
        }  
        return super.attemptAuthentication(request, response);  
    }  
}  

2. OAuth 2.0与JWT安全实践

2.1 授权码模式防钓鱼攻击

PKCE(Proof Key for Code Exchange)扩展: • 流程: 1. 客户端生成code_verifier(随机字符串)和code_challenge(其SHA256哈希)。 2. 授权请求携带code_challenge。 3. 令牌请求时提交code_verifier,服务端验证哈希一致性。 • 防御场景:防止授权码被中间人劫持后用于获取Token。 • Spring Security配置

@Bean  
public AuthorizationServerSettings authorizationServerSettings() {  
    return AuthorizationServerSettings.builder()  
            .pkceRequired(true)  // 强制启用PKCE  
            .build();  
}  
2.2 JWT安全配置

签名算法选择

算法 类型 安全性建议
HS256 对称加密 仅限内部服务
RS256 非对称加密 公开API(推荐)
JWT密钥管理
// 使用RSA密钥对(2048位)  
@Bean  
public KeyPair keyPair() {  
    return KeyPairGenerator.getInstance("RSA").generateKeyPair();  
}  
@Bean  
public JwtDecoder jwtDecoder(KeyPair keyPair) {  
    return NimbusJwtDecoder.withPublicKey((RSAPublicKey) keyPair.getPublic()).build();  
}  

令牌刷新机制: • Refresh Token有效期:设置较长周期(如7天),但需绑定设备指纹。 • 刷新接口防护java @PostMapping("/token/refresh") public ResponseEntity refreshToken(@RequestParam String refreshToken) { // 校验refreshToken有效性及设备指纹 if (!deviceService.validate(refreshToken, currentDeviceId)) { throw new InvalidTokenException("无效的Refresh Token"); } // 生成新Access Token return ResponseEntity.ok(jwtService.refreshToken(refreshToken)); }


3. 细粒度权限控制

3.1 RBAC与动态权限实现

数据库表设计

CREATE TABLE role (  
  id BIGINT PRIMARY KEY,  
  name VARCHAR(20) NOT NULL UNIQUE  
);  
CREATE TABLE permission (  
  id BIGINT PRIMARY KEY,  
  resource VARCHAR(50) NOT NULL,  
  action VARCHAR(10) NOT NULL  -- 如read, write  
);  
CREATE TABLE role_permission (  
  role_id BIGINT,  
  permission_id BIGINT,  
  PRIMARY KEY (role_id, permission_id)  
);  

动态权限加载

@Component  
public class DynamicPermissionService implements PermissionEvaluator {  
    @Override  
    public boolean hasPermission(Authentication auth, Object target, Object permission) {  
        // 从数据库查询用户权限  
        List permissions = permissionService.getPermissions(auth.getName());  
        return permissions.contains(permission.toString());  
    }  
}  
3.2 接口级权限控制

注解使用示例

  @PreAuthorize("hasPermission(#id, 'USER', 'READ')")  
  @GetMapping("/users/{id}")  
  public User getUser(@PathVariable Long id) {  
      return userService.findById(id);  
  }  

  @PreAuthorize("hasRole('ADMIN') or hasPermission(#request, 'WRITE')")  
  @PostMapping("/users")  
  public void createUser(@RequestBody UserCreateRequest request) {  
      userService.create(request);  
  }  

角色继承配置

@Bean  
public RoleHierarchy roleHierarchy() {  
    RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();  
    hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF \n ROLE_STAFF > ROLE_USER");  
    return hierarchy;  
}  

总结与面试要点

高频面试题: • 如何防止JWT令牌被盗用:绑定IP/设备指纹、设置短有效期、使用Refresh Token轮换。 • OAuth2授权码模式流程:客户端重定向→用户授权→授权码→换Token。 • 安全红线: • 禁止在URL中传递敏感参数(如response_type=token隐式模式)。 • 生产环境禁用ROLE_ANONYMOUS接口。

代码规范检查项: • 使用@PreAuthorize而非手动校验权限。 • 所有API文档(Swagger)标记所需权限。

通过分层权限控制与安全协议加固,构建可扩展、易维护的认证授权体系。


三、输入校验与数据安全


1. 请求参数安全处理

1.1 Hibernate Validator参数校验实战

常用校验注解

注解 功能 示例
@NotBlank 非空且至少一个非空格字符 @NotBlank String username
@Pattern 正则表达式校验 @Pattern(regexp = "^1[3-9]\\d{9}$") String phone
@Size 字符串/集合长度限制 @Size(min=6, max=20) String password
@Email 邮箱格式校验 @Email String email

全局异常处理:统一返回校验失败信息

@RestControllerAdvice  
public class GlobalExceptionHandler {  
    @ExceptionHandler(MethodArgumentNotValidException.class)  
    public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) {  
        Map errors = new HashMap<>();  
        ex.getBindingResult().getFieldErrors().forEach(error ->  
            errors.put(error.getField(), error.getDefaultMessage())  
        );  
        return ResponseEntity.badRequest().body(errors);  
    }  
}  
1.2 MyBatis SQL注入防御

#{}${}的区别

占位符 处理方式 安全性 适用场景
#{} 预编译参数化查询 动态条件值
${} 直接拼接SQL片段 低(需严格校验) 动态表名/排序字段

安全示例

    
    

    
    

动态表名处理:若必须使用${},需白名单过滤:

public void checkTableName(String tableName) {  
    if (!Arrays.asList("users", "orders").contains(tableName)) {  
        throw new IllegalArgumentException("非法表名");  
    }  
}  

2. XSS防御与输出编码

2.1 Thymeleaf自动转义机制

安全输出

    
  

禁用部分标签:防止富文本XSS

@Bean  
public SpringTemplateEngine templateEngine() {  
    SpringTemplateEngine engine = new SpringTemplateEngine();  
    engine.setEnableSpringELCompiler(true);  
    // 配置HTML过滤器,仅允许安全标签  
    Set allowedTags = new HashSet<>(Arrays.asList("div", "span", "p"));  
    engine.addTemplateResolver(new HtmlFilterTemplateResolver(allowedTags));  
    return engine;  
}  
2.2 敏感数据响应过滤

自定义HttpServletResponseWrapper

  public class SensitiveDataResponseWrapper extends HttpServletResponseWrapper {  
      public SensitiveDataResponseWrapper(HttpServletResponse response) {  
          super(response);  
      }  

      @Override  
      public PrintWriter getWriter() throws IOException {  
          return new PrintWriter(new OutputStreamWriter(getOutputStream())) {  
              @Override  
              public void write(String s) {  
                  // 过滤身份证号(示例)  
                  String filtered = s.replaceAll("\\d{17}[\\dXx]", "***");  
                  super.write(filtered);  
              }  
          };  
      }  
  }  

注册过滤器

@Bean  
public FilterRegistrationBean sensitiveDataFilter() {  
    FilterRegistrationBean bean = new FilterRegistrationBean<>();  
    bean.setFilter(new SensitiveDataFilter());  
    bean.addUrlPatterns("/*");  
    return bean;  
}  

3. 文件上传安全实战

3.1 基础防护策略

Spring Boot配置限制

spring:  
  servlet:  
    multipart:  
      max-file-size: 10MB      # 单文件最大10MB  
      max-request-size: 100MB  # 总请求最大100MB  

文件类型白名单

  public boolean isAllowedFileType(String filename) {  
      String extension = FilenameUtils.getExtension(filename).toLowerCase();  
      return Arrays.asList("jpg", "png", "pdf").contains(extension);  
  }  

  @PostMapping("/upload")  
  public String uploadFile(@RequestParam("file") MultipartFile file) {  
      if (!isAllowedFileType(file.getOriginalFilename())) {  
          throw new InvalidFileTypeException("不支持的文件类型");  
      }  
      // 保存文件逻辑  
  }  
3.2 病毒扫描集成(ClamAV)

ClamAV客户端配置

  @Bean  
  public ClamAVClient clamAVClient() {  
      return new ClamAVClient("clamav-server", 3310);  
  }  

  public boolean scanFile(byte[] fileData) {  
      try {  
          ClamAVClient client = clamAVClient();  
          return client.scan(fileData).isClean();  
      } catch (IOException e) {  
          throw new VirusScanException("病毒扫描失败");  
      }  
  }  

上传流程整合

@PostMapping("/upload-safe")  
public ResponseEntity uploadSafeFile(@RequestParam("file") MultipartFile file) {  
    if (!scanFile(file.getBytes())) {  
        throw new VirusDetectedException("文件包含恶意代码");  
    }  
    // 保存安全文件  
}  

总结与最佳实践

输入校验铁律: • 前端非信任:即使前端已校验,后端必须二次验证。 • 边界防御:在数据进入系统时(Controller层)立即校验。 • XSS防御层级

  1. 输入过滤:拒绝含

你可能感兴趣的:(web,java)