T_era
JdbcTemplate.query에서 sql의 컬럼을 바인딩할 수 없는 이유 본문
프로젝트를 작성하면서 sql을 사용할 때 두 sql의 결과가 다르게 나온다
1. String sql = "SELECT " + attributeName + " FROM user WHERE user_id = ?", userId;
2. String sql = "SELECT ? FROM user WHERE user_id = ?", attributeName, Key;
위의 코드 중 1번만 제대로 작동한다
물론 둘다 좋은 예가 아니다
왜그럴까?
문제점:
- SQL Injection 위험:
- attributeName 변수는 외부로부터 입력받거나 동적으로 생성될 수 있습니다.
- 만약 attributeName에 악의적인 SQL 코드가 포함된다면, 쿼리 실행 시 해당 코드가 그대로 실행되어 데이터베이스가 손상되거나 정보가 유출될 수 있다.
- 예를 들어, attributeName이 "user_password FROM user; DELETE FROM user;"와 같은 값이라면, 의도하지 않은 SQL 명령이 실행될 수 있다.
- PreparedStatement의 이점 상실:
- JdbcTemplate의 query() 메서드에서 두 번째 인자로 (rs, rowNum) -> rs.getString(attributeName) 익명 함수를 사용하고, 그 이후에 바인딩할 파라미터로 attributeName과 userId를 전달하고 있다.
- 하지만 SQL 구문 자체에서 attributeName을 문자열 결합(+)으로 직접 삽입했기 때문에, JdbcTemplate이 제공하는 PreparedStatement의 파라미터 바인딩 기능을 활용하지 못하고 있다.
- PreparedStatement는 SQL 구문을 미리 컴파일하여 SQL Injection 공격을 방지하고, 동일한 형태의 쿼리를 반복 실행할 때 성능 향상을 가져다준다.
올바른 작성 방식:
컬럼 이름을 동적으로 지정해야 하는 경우에는 일반적으로 다음과 같은 방식을 고려해야 합니다.
- 허용된 컬럼 목록 관리:
- 애플리케이션에서 접근해야 하는 컬럼 이름들의 화이트리스트를 관리합니다.
- 외부에서 제공된 attributeName이 이 화이트리스트에 존재하는지 확인한 후 SQL 구문을 동적으로 생성한다.
- PreparedStatement 파라미터 바인딩 (컬럼 이름은 불가):
- PreparedStatement는 데이터 값을 바인딩하는 데 사용됩니다. 컬럼 이름, 테이블 이름과 같은 SQL 식별자는 파라미터로 바인딩할 수 없다.
수정 예시 (화이트리스트 기반):
List<Object> result = null;
String sql = "";
String safeAttributeName = attributeName; // 일단 변수에 할당
List<String> allowedAttributes = Arrays.asList("username", "email", "phone_number"); // 허용된 컬럼 목록
if (allowedAttributes.contains(attributeName)) {
sql = "SELECT " + safeAttributeName + " FROM user WHERE user_id = ?";
result = jdbcTemplate.query(
sql,
(rs, rowNum) -> rs.getString(safeAttributeName),
userId
);
} else {
// 허용되지 않은 컬럼 이름에 대한 처리 (예: 예외 발생, 기본값 사용 등)
System.err.println("허용되지 않은 컬럼 이름: " + attributeName);
// 또는 throw new IllegalArgumentException("허용되지 않은 컬럼 이름: " + attributeName);
result = Collections.emptyList(); // 예시로 빈 리스트 반환
}
요약:
- SQL 구문에서 컬럼 이름을 직접 문자열 결합하는 것은 SQL Injection에 매우 취약하다.
- JdbcTemplate의 파라미터 바인딩은 데이터 값에만 적용 가능하며, 컬럼 이름과 같은 식별자에는 사용할 수 없다.
- 컬럼 이름을 동적으로 처리해야 할 경우에는 **허용된 목록(화이트리스트)**을 관리하여 안전하게 SQL 구문을 생성해야 한다.
따라서, "SELECT ? FROM user WHERE user_id = ?" 형태로는 컬럼 이름을 파라미터로 바인딩할 수 없으며, 이는 SQL Injection 위험을 초래하는 잘못된 방식이다. 컬럼 이름을 동적으로 지정해야 한다면, 위에서 제시된 화이트리스트 기반의 접근 방식을 고려해야 한다.
'Programing > Spring' 카테고리의 다른 글
| Validation과 예외 처리: 함께 하지 않아야 하는가? (0) | 2025.05.15 |
|---|---|
| @Transactional에 대하여 (0) | 2025.05.13 |
| @Valid와 BindingResult의 관계 및 처리 방법 (0) | 2025.05.13 |
| Service계층과 Repository계층에서의 로직 작성 (0) | 2025.05.08 |
| Layered Architecture (계층형 아키텍처) (0) | 2025.05.08 |