MyBatis Framework을 사용해서 개발하다 보면 SQL Mapping Parameter를 대체하는 방식으로 # (Hash) 와 $ (Dollar)를 사용하게 된다. 그런데 막상 그 차이에 대해서 명확하게 알지 못하고 그냥 남들이 썼던 Code를 보고 따라하다 보니... 그냥 사용하고 있다는 사람들을 종종 보게 된다. 개발자가 무심코 작성한 코드가 보안상 취약점이 되어 뒷통수를 때리는 경우가 발생 할 수 있으니 정확한 차이와 사용처에 대해서 짚고 넘어가고자 몇자 적어본다.
MyBatis에서 '#' 과 '$'는 SQL Mapping에서 Parameter를 대체하는 방식에서 차이가 있다. 이 두 기호는 Parameter를 SQL Query에 삽입하는 데 사용되지만 보안적 측면과 성능적 측면에서 아주 중요한 차이를 보인다.
# (Hash)
'#'는 SQL Query에서 Parameter를 안전하게 Binding 하는 데 사용된다. MyBatis는 #을 사용하여 제공된 값의 Type에 관계 없이 처리할 수 있도록 Query를 제공한다.
예를 들어 사용자명 Column에서 사용자 이름을 조건으로 조회하는 Query를 살펴보자.
<select id="selectUser" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>
- #{username}은 안전하게 SQL Query에 Binding 하게 된다. #{username}에 대해서 그 값이 그대로 노출되지 않고 ?로 Binding 되어 Parsing 되는 것이다.
- SQL Query는 'SELECT * FROM users WHERE username = ?' 로 변환이 된 후 Parameter 값이 준비된 Query에 Binding 된다.
- 즉, username에 '이순신'이라는 Parameter를 # 기호를 사용하여 작성하게 되면, 최종적으로 실행되는 Query는 "SELECT * FROM users WHERE username ='이순신' " 이 된다. 살짝 헤깔리기 쉬운 부분인데 [이순신]이라는 단어가 String으로 받아들여져서 자동으로 '(작은 따옴표)를 작성해서 전달한다는 차이이다.
- 이 방식은 SQL Injection 공격으로부터 예방 할 수 있는 유용한 방식이다.
$ (Dollar)
'$'는 SQL Query에서 Parameter를 직접 대체하는 방식이다. 이 방식은 Table Partition 을 사용하는 것처럼 Table 명을 Parameter로 전달해서 사용하는 경우 같이 Query의 동적 생성에는 유리하지만 반대로 SQL Injection 공격에는 취약할 수 밖에 없다.
위와 동일한 예를 들어보자.
<select id="selectUser" resultType="User">
SELECT * FROM users WHERE username = ${username}
</select>
- ${username}는 Parameter로 요청되는 값을 그대로 직접 대체된다.
- 만약 'username' 값에 실제 사용자이름 대신 "admin ' or 1=1 -- " 라고 전달한다면, 최종적으로 실행되는 Query는 " SELECT * FROM users WHERE username = admin ' or 1=1 -- " 이 된다. 이렇게 되면 전형적인 SQL Injection 공격에 노출되는 상황이 발생되는 것이다.
- 만약 위에서 처럼 $에 SQL Injection 을 위한 Parameter가 전달되면 username이 admin 이 아니더라도 TRUE가 된다.
- 일반적으로 $ 기호는 Table 이름이나 Column 이름을 전달하는 용도로만 사용하는게 바람직하다.
#는 조건값으로, $는 Table이나 Column 값으로 사용
예를들어서 살펴보자. 조회 대상 Table 과 Column 의 값이 유동적으로 바뀌어야 하는 경우(예를 들어 Partition Table을 사용하는 경우 직접 해당 Table을 지정해야 하는 경우)를 예로 살펴보자.
위의 경우 처럼 대표 Table이 있고 지역별로 Partition되어 있다고 했을때 전체를 대상으로 조회하는 것보다 특정 지역을 한정할 수 있다면 한정지어서 조회하는 것이 검색 속도를 확연하게 빠르게 하는 결과를 가져올 것이다. Data가 많지 않다면 차이가 없겠지만 Data양이 많다면 그 차이는 더욱 확연해 진다. 어쨌뜬 저렇게 특정 테이블을 지정해서 보다 효과적인 결과를 낼 수 있는 사황이라고 가정한다면 테이블명을 유동적으로 사용하는게 유용할 것이다.
SELECT * FROM tb0000_zipcode_02 WHERE roadname ='논현로8길'
위와 같은 Query를 생성하기 위해서 아래와 같이 사용하는 것이다.
SELECT * FROM tb0000_zipcode_${Param1} WHERE roadname = #{Param2}
그리고 Parameter로 Param1의 값은 "02", Param2 값으로 "논현로8길"을 전달하면 된다.
요약하자면 '#'은 SQL Injection을 방지하지만 '$'는 그렇지 않다. 일반적으로 '#'이 더 나은 성능을 제공한다. 그 이유는 Parameter를 미리 Compile 된 Query에 Binding하기 때문이다. '#'과 '$'를 올바르게 사용하여 보안과 성능을 최적화 하는 것이 중요하다. '#'를 사용하여 Binding하고 '$'는 신뢰할 수 있는 경우에만 사용해야 한다.
'Blog > JAVA' 카테고리의 다른 글
이클립스(Eclipse)에 롬복(Lombok) 설치하기 (2) | 2024.10.03 |
---|---|
RESTFul-API SampleCoding (Java,SpringBoot,Maven,myBatis) #4 (0) | 2024.07.17 |
RESTFul-API SampleCoding (Java,SpringBoot,Maven,myBatis) #3 (0) | 2024.07.16 |
RESTFul-API SampleCoding (Java,SpringBoot,Maven,myBatis) #2 (2) | 2024.07.16 |
RESTFul-API SampleCoding (Java,SpringBoot,Maven,myBatis) #1 (0) | 2024.07.15 |