API安全设计与防护实战
一、API安全概述
API作为系统间交互的接口,是攻击的主要目标。一个安全的API设计需要考虑多个层面的防护,包括认证、授权、数据保护、攻击防护等。
二、API认证机制
2.1 API Key认证
java
@Component
public class ApiKeyFilter extends OncePerRequestFilter {
private static final String API_KEY_HEADER = "X-API-Key";
private static final String VALID_API_KEY = "your-api-key-here";
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String apiKey = request.getHeader(API_KEY_HEADER);
if (!VALID_API_KEY.equals(apiKey)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: Invalid API Key");
return;
}
filterChain.doFilter(request, response);
}
}
2.2 基于证书的双向认证
yaml
server:
ssl:
enabled: true
key-store: classpath:server.jks
key-store-password: password
trust-store: classpath:truststore.jks
trust-store-password: password
client-auth: need
三、API授权策略
3.1 RBAC基于角色的访问控制
java
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/user/**").hasRole("USER")
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated();
}
}
3.2 ABAC基于属性的访问控制
java
@Component
public class AbacAuthorizationManager {
public boolean isAllowed(User user, Resource resource, Action action) {
// 根据用户属性、资源属性、环境属性进行动态授权判断
if ("admin".equals(user.getRole())) {
return true;
}
if ("read".equals(action) && resource.isPublic()) {
return true;
}
if (resource.getOwnerId().equals(user.getId())) {
return true;
}
return false;
}
}
四、输入验证与数据保护
4.1 参数校验
java
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest request) {
// 参数已通过@Valid注解自动校验
User user = userService.createUser(request);
return ResponseEntity.ok(user);
}
}
public class UserCreateRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50之间")
private String username;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@NotBlank(message = "密码不能为空")
@Size(min = 8, message = "密码长度至少8位")
private String password;
}
4.2 SQL注入防护
错误写法:
java
// 存在SQL注入风险
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
正确写法:
java
// 使用预编译语句
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
4.3 XSS攻击防护
java
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
public class XssUtils {
public static String clean(String input) {
if (input == null) {
return null;
}
return Jsoup.clean(input, Safelist.relaxed());
}
}
五、API限流与熔断
5.1 使用Bucket4j进行限流
java
@Configuration
public class RateLimitConfig {
@Bean
public Bucket bucket() {
Refill refill = Refill.greedy(100, Duration.ofMinutes(1));
Bandwidth limit = Bandwidth.classic(100, refill);
return Bucket.builder().addLimit(limit).build();
}
}
@Component
public class RateLimitFilter extends OncePerRequestFilter {
@Autowired
private Bucket bucket;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if (bucket.tryConsume(1)) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
response.getWriter().write("Rate limit exceeded");
}
}
}
5.2 Hystrix熔断配置
java
@Configuration
public class HystrixConfig {
@Bean
public HystrixCommand.Setter defaultCommandProperties() {
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("DefaultGroup"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(5000)
.withCircuitBreakerRequestVolumeThreshold(20)
.withCircuitBreakerErrorThresholdPercentage(50)
.withCircuitBreakerSleepWindowInMilliseconds(30000));
}
}
六、安全审计与日志
6.1 审计日志记录
java
@Aspect
@Component
public class AuditAspect {
@Autowired
private AuditLogRepository auditLogRepository;
@Around("@annotation(auditable)")
public Object audit(ProceedingJoinPoint joinPoint, Auditable auditable) throws Throwable {
AuditLog log = new AuditLog();
log.setAction(auditable.action());
log.setResource(auditable.resource());
log.setTimestamp(LocalDateTime.now());
try {
Object result = joinPoint.proceed();
log.setStatus("SUCCESS");
return result;
} catch (Exception e) {
log.setStatus("FAILED");
log.setErrorMessage(e.getMessage());
throw e;
} finally {
auditLogRepository.save(log);
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {
String action();
String resource();
}
6.2 安全日志脱敏
java
public class LogSanitizer {
private static final Pattern EMAIL_PATTERN = Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
private static final Pattern PHONE_PATTERN = Pattern.compile("1[3-9]\\d{9}");
private static final Pattern CARD_PATTERN = Pattern.compile("\\d{4}\\s*\\d{4}\\s*\\d{4}\\s*\\d{4}");
public static String sanitize(String message) {
if (message == null) {
return null;
}
String sanitized = EMAIL_PATTERN.matcher(message).replaceAll("***@***.com");
sanitized = PHONE_PATTERN.matcher(sanitized).replaceAll("1*** **** ****");
sanitized = CARD_PATTERN.matcher(sanitized).replaceAll("**** **** **** ****");
return sanitized;
}
}
七、API安全测试
7.1 使用OWASP ZAP进行安全扫描
bash
# 启动ZAP代理
zap.sh -daemon -port 8080 -config scanner.maxRuleDurationInMins=5
# 运行主动扫描
curl -X POST http://localhost:8080/JSON/ascan/action/scan \
-d '{"apikey":"your-api-key","url":"http://localhost:8080/api","recurse":"true"}'
# 获取扫描结果
curl http://localhost:8080/JSON/ascan/view/alerts \
-d '{"apikey":"your-api-key","baseurl":"http://localhost:8080/api"}'
7.2 安全测试清单
认证测试:
- [ ] 无Token访问受限API
- [ ] 使用过期Token访问
- [ ] 使用无效Token访问
- [ ] Token劫持测试
授权测试:
- [ ] 越权访问其他用户数据
- [ ] 普通用户访问管理员接口
- [ ] 未授权访问敏感资源
输入验证测试:
- [ ] SQL注入测试
- [ ] XSS攻击测试
- [ ] 参数边界值测试
- [ ] 恶意文件上传测试
安全配置测试:
- [ ] HTTPS强制测试
- [ ] CORS配置验证
- [ ] 敏感信息泄露检查
- [ ] 错误信息泄露检查
八、总结
API安全是一个系统性工程,需要从认证授权、输入验证、限流熔断、审计日志等多个维度进行防护。通过采用业界成熟的安全框架和最佳实践,可以有效降低API被攻击的风险。同时,定期进行安全测试和漏洞扫描,及时修复发现的安全问题,是保障API安全的重要环节。
参考资料:
- OWASP API Security Top 10: https://github.com/OWASP/API-Security
- Spring Security Documentation: https://spring.io/projects/spring-security
- Bucket4j: https://bucket4j.com/