본문 바로가기

웹 보안

[웹 보안] MyBatis에서 ${} vs #{}: 차이와 SQL Injection 방지 방법

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