SQL Injection

송준표·2026년 10월 20일
post-thumbnail
  • 사용자 입력값이 SQL 문의 일부로 해석되도록 조작해, 개발자가 의도하지 않은 쿼리를 실행시키는 공격
  • 입력값과 SQL 구문을 문자열로 이어 붙이는 코드에서 발생

원리: 취약한 Java 코드

String sql = "SELECT * FROM users WHERE id='" + id + "' AND pw='" + pw + "'";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);

만약 공격자가 위 쿼리의 id 칸에 ' OR '1'='1' -- 를 입력하면 쿼리가 아래와 같이 변경된다.

SELECT * FROM users WHERE id='' OR '1'='1' -- ' AND pw='...'
  • '로 id의 조건을 ''로 변경합니다.
  • OR '1'='1'로 조건을 항상 참이 되게끔 합니다.
  • --로 이후의 조건을 주석으로 처리합니다.

대표적인 유형

Error-based

  • 일부러 오류를 내서 에러 메시지로 DB 정보를 얻음
    - 예시: ' AND 1=CONVERT(int, @@version) --

Union-based

  • UNION으로 다른 테이블 결과를 합쳐서 출력
    - 예시: ' UNION SELECT username, password FROM users --

Blind(참/거짓)

  • 화면에 결과가 안 나올 때, 참/거짓 응답 차이로 한 글자씩 유추
    - 예시: ' AND 1=1 -- (정상) vs ' AND 1=2 -- (비정상)

Blind(시간 기반)

  • 응답 지연 여부로 판단
    - 예시: ' AND IF(1=1, SLEEP(5), 0) --

해결책

1. PreparedStatement

String sql = "SELECT * FROM users WHERE id = ? AND pw = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, id);
    ps.setString(2, pw);
    ResultSet rs = ps.executeQuery();
}

SQL 구조가 먼저 컴파일되고, 입력값은 나중에 "데이터"로만 바인딩됩니다.
따라서, ' OR '1'='1 을 넣어도 그냥 "id가 ' OR '1'='1인 사용자"를 찾는 문자열 비교가 됩니다.

2. 입력값 화이트리스트 검증

// 형식 검증: 영문·숫자 4~20자만 허용
if (!id.matches("^[a-zA-Z0-9]{4,20}$")) {
    throw new IllegalArgumentException("잘못된 아이디 형식");
}

// 정렬 컬럼처럼 ?로 바인딩할 수 없는 값은 허용 목록으로 제한
Set<String> ALLOWED = Set.of("name", "created_at");
if (!ALLOWED.contains(sortColumn)) {
    throw new IllegalArgumentException("허용되지 않은 정렬 기준");
}
String sql = "SELECT * FROM users ORDER BY " + sortColumn;

ORDER BY 뒤의 컬럼명은 PreparedStatement의 ?로 바인딩할 수 없습니다. 그래서 이런 값은 허용 목록에 있는 것만 통과시키는 방식이 필요합니다.

3. 에러 메시지 숨기기

@ExceptionHandler(Exception.class)
public ResponseEntity<String> handle(Exception e) {
    log.error("DB 오류", e);   // 상세 내용은 서버 로그에만 기록
    return ResponseEntity.status(500).body("요청 처리 중 오류가 발생했습니다.");
}

상세 오류는 로그에만 남기고 사용자에게는 일반 메시지만 보여줘 Error-based 공격을 차단합니다.

(나쁜 예) You have an error in your SQL syntax; ... near ''' at line 1
(좋은 예) 요청 처리 중 오류가 발생했습니다.

4. WAF

요청: GET /users?id=1' UNION SELECT username, password FROM users --
WAF : 'UNION SELECT' 패턴 탐지 → 403 Forbidden
      (요청이 애플리케이션까지 도달하지 못함)

: 알려진 공격 패턴(예: UNION SELECT, OR 1=1, SLEEP()을 네트워크 단에서 차단하는 보조 수단(근본 해결은 PreparedStatement)

0개의 댓글