728x90
회사 보안 점검 보고서를 보니 MyBatis XML 내에서 ${}를 #{}로 변경하라는 권고가 있었습니다. 읽어보니 그 이유가 SQL Injection 취약성 때문이라는 걸 알게 되어 정리합니다. 결론부터 말하면
- #{} : 안전 — PreparedStatement의 파라미터 바인딩으로 처리되어 SQL 구문을 변조할 수 없음
- ${} : 위험 — 단순 텍스트 치환으로 입력값이 SQL의 일부(구문)로 삽입될 수 있음
MyBatis XML 파싱 및 동적 SQL 처리
- <if>, <choose>, <foreach>, <bind> 같은 태그로 동적 SQL을 만들고, ${}는 이 단계에서 그대로 텍스트 치환이 됩니다.
- BoundSql 객체가 생성되며 이 안에는 완성된 SQL 텍스트와 parametermappings가 들어가있습니다.
- parameterMappings는 바인딩 해야하는 파라미터 정보 목록이 들어가있습니다.
#{} 처리는 어떻게 될까?
- MyBatis는 #{} 위치에 대해 parameterMappings 항목을 만듭니다. 실제 SQL 문자열에는 "?" 로 대체 됩니다.
SQL 문
WHERE USER_ID = #{USER_ID}
WHERE USER_ID = ? 와 parameterMappings { user_id }
${} 처리는 어떻게 될까?
- ${}는 동적 SQL 생성 시 문자열 그대로 대체가 됩니다.
- ${USER_ID}의 값이 shs00925일 경우 SQL에 shs00925가 "직접" 삽입이 됩니다.
- 그 결과 SQL은 이미 완성된 텍스트로 DB에 전달이 됩니다.
JDBC 실행
- ? 가 포함된 SQL은 PreparedStatement로 생성되어 파라미터 바인딩 후 실행이 됩니다.
- 이미 완성된 텍스트는 Statement 또는 PreparedStatement로 바로 실행됩니다.
그럼 왜 #{}이 안전할까?
- PreparedStatement는 SQL 템플릿과 파라미터를 분리해서 DB에 전달을 합니다. DB는 템플릿을 먼저 파싱한 후 파라미터는 값으로 취급을 합니다.
- 공격자가 ' OR '1' = '1 같은 문자열을 넣어도 그것은 데이터로만 취급하여 SQL 구문을 바꾸지 못합니다.
- 또한 MyBatis의 TypeHandler가 적절한 JDBC 타입으로 변환해서 바인딩하기에 형 변환도 안전하게 처리할 수 있습니다.
왜 ${}는 위험할까?
- ${}는 단순 문자열로 치환되어 입력값이 SQL 문법의 일부로 들어가게 됩니다.
- 즉 공격자가 ' OR '1' = '1 이 SQL문으로 적용이 되며 SQL Injection이 가능해집니다.
- ${}는 컬럼명, 테이블명 등 식별자가 필요할 때 쓰이지만 해당 경우 화이트리스트 검증이 필수로 작동되어야 합니다.
'웹 보안' 카테고리의 다른 글
| [웹 보안] 세션 하이잭킹 (1) | 2025.05.01 |
|---|---|
| [웹 보안] 파일 업로드 취약점 (1) | 2025.04.30 |
| [웹 보안] CSRF (1) | 2025.04.28 |
| [웹 보안] XSS (1) | 2025.04.23 |
| [웹 보안] SQL 인젝션 (0) | 2025.04.21 |