SQL Injection·XSS 시도가 담긴 자동화 요청 대응: 인증 없는 API에 JWT 인증 적용
JSP로 구성된 회사 홈페이지를 운영하면서 겪은 보안 이슈와, 이를 JWT 기반 API 인증으로 해결한 과정을 정리한다.
증상
문의사항 게시판에 고객 문의가 아닌 비정상적인 글이 반복해서 등록되었다. 글 내용에는 SQL 쿼리문 조각이나 스크립트처럼 보이는 문자열 등, 사람이 문의 폼을 채워서 쓴 것으로 보기 어려운 값들이 들어 있었다.
원인
누군가 문의 작성 화면을 거치지 않고 문의 등록 API를 인위적으로 직접 호출하고 있었다.
당시 구조의 문제는 이 API가 호출한 사람이 누구인지 확인하지 않았다는 점이다. 주소와 파라미터 형식만 알면 브라우저가 아니라 스크립트로도 얼마든지 글을 등록할 수 있었다. 화면에 둔 입력 제한은 화면을 통해 들어올 때만 의미가 있고, API를 직접 부르면 모두 건너뛸 수 있다.
글에 섞여 있던 SQL·스크립트 문자열은 이런 자동화 요청이 흔히 보내는 취약점 탐색용 입력값으로, 각각 SQL Injection과 XSS(Cross-Site Scripting) 가 통하는지 시험하는 시도다. 다만 시도가 담긴 글이 저장되었다는 것과 공격이 실제로 성공했다는 것은 다른 이야기다. 즉 이 사건의 본질은 특정 공격 기법보다 인증 없이 열려 있는 쓰기 API에 있었다.
조치: 로그인 방식을 JWT로 전환하고 API 호출에 토큰 요구
기존 로그인 방식을 JWT(JSON Web Token) 기반으로 바꾸고, API를 호출할 때 토큰을 함께 보내도록 했다. 서버는 요청마다 토큰을 검증하고, 유효한 토큰이 없는 요청은 처리하지 않는다.
- 사용자가 로그인하면 서버가 JWT를 발급한다
- 클라이언트는 API를 호출할 때
Authorization: Bearer <토큰>헤더에 토큰을 담아 보낸다 - 서버는 요청을 처리하기 전에 토큰의 서명과 만료 시간을 검증한다
- 검증을 통과하지 못한 요청은 거부한다
서버 쪽 검증은 요청이 컨트롤러에 닿기 전에 토큰을 확인하는 필터로 처리한다. 구조를 보여 주기 위한 예시는 다음과 같다.
// 예시: 요청 헤더의 JWT 를 검증하는 필터
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
if (jwtProvider.validate(token)) { // 서명·만료 검증
Authentication auth = jwtProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
}결과
토큰 없이 API를 직접 호출하는 요청은 더 이상 처리되지 않았고, 문의 게시판에 비정상적인 글이 쌓이던 문제가 해결되었다.
정리
- 화면에서 막는 것은 보안이 아니다. 화면에 버튼이 없어도 API는 직접 호출할 수 있다. 서버가 요청마다 "누가 보냈는지"를 확인해야 한다.
- 쓰기 API는 기본적으로 인증이 필요한 상태로 시작해야 한다. 이번 문제는 인증을 붙이는 것으로 근본 원인이 해결되었다.
- 인증과 입력값 검증은 별개의 문제다. JWT 인증은 "아무나 호출하지 못하게" 막는 장치다. 로그인한 사용자가 보내는 입력값 자체의 안전성(SQL Injection, XSS 등)은 파라미터 바인딩과 출력 시 이스케이프 처리처럼 별도의 방법으로 다뤄야 한다.
- 남은 과제: 회원가입이 열려 있다면 자동화 도구도 계정을 만들어 토큰을 받을 수 있다. 요청 횟수 제한(Rate Limiting)이나 CAPTCHA를 함께 두면 한 단계 더 막을 수 있다.