불필요한 HTTP Method 악용 (WM) | 위험도: 2 (낮음) | 통제구분: 5.5.1 HTTP 메서드 제한
인터넷뱅킹 API 서버
// ❌ 모든 HTTP Method 허용
// Spring Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
// Method 제한 없음!
@RequestMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
// DELETE 허용
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
// 권한 체크 없음!
userService.delete(id);
}
// PUT 허용
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id,
@RequestBody User user) {
// 소유자 확인 없음!
return userService.update(id, user);
}
}
// Apache 설정 - Method 제한 없음
<Directory /var/www/html>
<!-- 모든 Method 허용 -->
AllowOverride All
Require all granted
</Directory>
// web.xml - TRACE 활성화
<!-- TRACE method 비활성화 안 됨 -->
공격 시나리오를 선택하세요
// ✅ HTTP Method 명시적 제한
// 1. Spring - 필요한 Method만 허용
@RestController
@RequestMapping("/api/users")
public class UserController {
// GET만 허용
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
// DELETE - 권한 검증 필수
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(@PathVariable Long id,
Authentication auth) {
// 본인 또는 관리자만 삭제 가능
User currentUser = (User) auth.getPrincipal();
if (!currentUser.isAdmin() &&
!currentUser.getId().equals(id)) {
throw new AccessDeniedException();
}
userService.delete(id);
}
// PUT - 소유자 확인
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id,
@RequestBody User user,
Authentication auth) {
User currentUser = (User) auth.getPrincipal();
// 본인 데이터만 수정 가능
if (!currentUser.getId().equals(id)) {
throw new AccessDeniedException();
}
return userService.update(id, user);
}
}
// 2. Spring Security - Method 제한
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(
HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// 읽기는 인증된 사용자
.requestMatchers(HttpMethod.GET, "/api/**")
.authenticated()
// 쓰기는 관리자만
.requestMatchers(HttpMethod.POST, "/api/**")
.hasRole("ADMIN")
.requestMatchers(HttpMethod.PUT, "/api/**")
.hasRole("ADMIN")
.requestMatchers(HttpMethod.DELETE, "/api/**")
.hasRole("ADMIN")
// TRACE, OPTIONS 차단
.requestMatchers(HttpMethod.TRACE, "/**")
.denyAll()
.requestMatchers(HttpMethod.OPTIONS, "/**")
.denyAll()
);
return http.build();
}
}
// 3. Apache - Method 제한
<Directory /var/www/html>
<!-- GET, POST만 허용 -->
<LimitExcept GET POST>
Require all denied
</LimitExcept>
<!-- TRACE 비활성화 -->
TraceEnable off
</Directory>
// 4. Nginx - Method 제한
server {
location /api {
# GET, POST만 허용
limit_except GET POST {
deny all;
}
# TRACE 차단
if ($request_method = TRACE) {
return 405;
}
proxy_pass http://backend;
}
}
// 5. Filter로 Method 검증
@Component
public class HttpMethodFilter extends OncePerRequestFilter {
private static final Set ALLOWED_METHODS =
Set.of("GET", "POST");
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException {
String method = request.getMethod();
// 허용된 Method인지 확인
if (!ALLOWED_METHODS.contains(method)) {
response.sendError(
HttpServletResponse.SC_METHOD_NOT_ALLOWED,
"Method " + method + " not allowed"
);
return;
}
// TRACE 특별 차단
if ("TRACE".equals(method)) {
response.sendError(
HttpServletResponse.SC_FORBIDDEN,
"TRACE method is disabled"
);
return;
}
chain.doFilter(request, response);
}
}
// 6. WebDAV 비활성화
<IfModule mod_dav.c>
# WebDAV 완전 비활성화
DavLockDB /dev/null
Dav Off
</IfModule>
| Method | 일반 사용자 | 관리자 |
|---|---|---|
| GET | ✓ 허용 | ✓ 허용 |
| POST | ✓ 허용 | ✓ 허용 |
| PUT | ✗ 차단 | △ 제한적 |
| DELETE | ✗ 차단 | △ 제한적 |
| TRACE | ✗ 차단 | ✗ 차단 |
| OPTIONS | ✗ 차단 | ✗ 차단 |