mybatis $ # 차이를 알아봅시다.

웹/스프링부트 2020. 6. 7. 22:45

 mybatis의 sql문을 저장해 놓는 mapper 파일이 있습니다. 여기서 parameter를 $을 쓰는 거랑, #을 쓰는 것이랑 차이가 있습니다. 어떤 차이가 있는지 간단하게 알아보도록 하겠습니다.

 

 


 대략적인 프로젝트 구조는 다음과 같습니다. DatabaseConfig.java는 데이터 소스를 초기화 하기 위한 클래스입니다. 그리고 속성 파일인 application.properties에 데이터 베이스 연결 정보가 들어 있습니다.

 

 

 /user/chk를 post 방식으로 호출을 하면 Controller의 chk를 호출합니다. 그리고 이것은 userdao의 checkId를 호출합니다.

 

 

 이것은 sqlsession의 selectList를 호출하는데요. tempMapper.xml의 namespace가 com.example.demo.dao.userDAO입니다. tempMapper.xml 파일을 보겠습니다.

 

 

 checkId라는 id를 가지는 select 구문이 12번째 줄에 작성되어 있음을 알 수 있습니다. 여기서 parameter를 '${id}' 이렇게 작성했습니다. 왜냐하면, id는 varchar형이기 때문입니다. id가 문자열 abc와 같은지는 id = 'abc' 이렇게 작성을 합니다.

 

 

 이렇게 작성하고, 다음과 같은 요청을 보내보도록 하겠습니다. id가 ' or 1 = 1 -- 라니, 이것은 무엇을 의미할까요?

 

 

 xml 파일의 12번째 줄의 sql 문은 위와 같이 대치가 됩니다.

 

 

 이것을 보기 좋게 정리하면, id가 '' 이거나 1 = 1인 레코드를 모두 뽑으라는 의미입니다. A or B는 A가 참이거나, B가 참이면 참이 됩니다. 1 = 1은 당연하게도 참입니다. 1은 1이기 때문입니다. 뒤에 and 절로 연결되어 있는 문장은, postgresql에서의 주석을 표시하는, --으로 인해 무시됩니다.

 

 따라서, 의도와는 다르게 전체 레코드가 출력이 됩니다.

 

 

 cho 하나만 있는 모양이군요.

 


 log4j 등을 이용해서, 어떻게 쿼리가 날라가는지 보도록 하겠습니다. 사실, 내부적으로 mybatis에서 xml 파일에서 sql문을 쓸 때, $ 파라미터를 쓴 경우에, GenericTokenParser을 내부적으로 호출하긴 합니다만. 이는 이번 시간에 볼 것은 아닙니다. 그러면 언젠가 본 다는 이야기입니다. 그러니, 로거로 어뗳게 수행되는지만 볼 것입니다. 똑같은 요청을 날린 다음에, logger에 로그가 어떻게 찍히나 보겠습니다.

 

 

 그러면, 그림에서 4번째 줄에 prepareStatement에 뭔가가 실행되었다는 로그가 떴다는 것을 알 수 있는데요. 해당 쿼리를 복사해서 붙여넣기 해 보겠습니다.

 

 

 그러면, 이런 쿼리가 떡하고 나옵니다. 단순히 $은 그냥 String을 append 하는 역할밖에 더 안 한다는 것을 유추할 수 있습니다.

 

 

 반면에 #{id} 이런 식으로 파라미터를 주면 어떨까요? 이 경우도 로그를 찍어 보겠습니다.

 

 

 똑같이 이런 요청을 보내보겠습니다.

 

 

 logger에 찍힌 쿼리를 그대로 편집기에 복사 붙여넣기 해 보겠습니다.

 

 

 아까와는 다르게 '이 escaping 되었음을 알 수 있습니다. '이 escaping이 되지 않았다면, pw에 어떤 값을 넣었던지간에 항상 참이 나왔을 겁니다.