문자열 연결 기반 SQL 인젝션 (SQL Injection)

SQL Injection

설명

문자열 연결(+, StringBuilder 등)로 SQL을 동적으로 만들 때, 그 일부라도 사용자 입력이 섞이면 SQL 구문 자체가 바뀔 수 있습니다. 공격자는 따옴표('), 논리 연산(OR 1=1), 세미콜론(;) 등의 특수문자를 주입해 인증 우회, 데이터 탈취/변조, 심지어 DB 기능을 악용한 시스템 명령 실행까지 시도할 수 있습니다. 특히 JDBC Statement나 createQuery에 완성된 문자열을 넘길 때 쉽게 악용됩니다.

잠재적 영향

  • 데이터 유출: 쿼리를 변조해 다른 사용자의 민감정보를 조회할 수 있음

  • 인증/인가 우회: WHERE 절을 무력화(예: 'OR 1=1')하여 로그인이나 권한 검증을 우회할 수 있음

  • 데이터 변조/삭제: UPDATE/DELETE/INSERT를 임의로 실행해 데이터 무결성을 파괴할 수 있음

  • DB 권한 상승 및 시스템 접근: DB 확장 함수/프로시저를 악용하여 권한 상승이나 호스트 명령 실행을 유도할 수 있음

  • 서비스 거부(DoS): 무거운 쿼리 유도 또는 다중 문 실행으로 리소스를 고갈시킬 수 있음

해결 방법

  • PreparedStatement 사용: SQL은 상수 템플릿로 두고 값은 ? 플레이스홀더에 setString/setInt 등으로 바인딩

  • JPA/Hibernate 파라미터 바인딩: :name 또는 ?1 사용 후 setParameter로 값 설정. 가능하면 @NamedQuery 사용

  • 동적 조각 화이트리스트: 컬럼명, 정렬 방향(ASC/DESC), LIMIT 등 스키마 요소는 미리 정의한 목록에서만 선택해 결합

  • Statement 금지: Statement.execute(…) 대신 PreparedStatement/TypedQuery 사용. createQuery/createNativeQuery에도 값 연결 금지

  • 입력 검증: 예상 타입/형식(숫자, UUID, enum 등) 검증 및 길이 제한. 하지만 검증은 바인딩을 대체하지 못함

  • 최소 권한 원칙: DB 계정에 불필요한 DDL/고위험 권한 금지, 다중문 실행/Stored Procedure는 필요 최소로 제한

취약한 코드 및 안전한 코드 예시

취약한 코드

안전한 코드

설명:

  • 취약한 코드: username과 sort가 그대로 SQL 문자열에 삽입되어 쿼리 구조를 변경할 수 있습니다. 공격자는 username에 ' OR 1=1 -- 같은 페이로드를 넣어 WHERE 절을 무력화하거나, sort에 임의의 식/키워드를 넣어 쿼리를 변조할 수 있습니다.

  • 안전한 코드: 값 자리는 ? 플레이스홀더로 두고 PreparedStatement로 바인딩하여 값이 데이터로만 처리되도록 했습니다. 또한 컬럼명 같은 동적 스키마 조각은 화이트리스트로 제한해 쿼리 구조 변조를 차단합니다.

참조

Last updated