sql injection과 jdbc PreparedStatement

코딩/Sql 2020. 6. 2. 01:14

 jdbc 프로그래밍을 하시다 보면, PreparedStatement랑 statement는 많이 들어보셨으리라 생각이 듭니다. 이 중 전자를 '준비된 구문' 이라고 이야기를 하는데요. 이 둘에 대해서 간단하게 알아보겠습니다.

 

 


 예제 프로그램 1을 보겠습니다. 그 전에 테이블 t의 필드 a와 b는 varchar형입니다. 필드 최대 길이는 20으로 잡았습니다.

 

 

  딱히 어려운 것은 없고, Statement라는 것이 있고, 이것은 SQL을 집어넣습니다. 그런데, SQL은 where a' 라는 문자열과 str과 '가 있는 문자열을 연결하고 있어요. str은 어디서 들어올까요?

 

 

 main에서 입력을 받습니다. 그러면 재미있는 장난 한 번 쳐 보겠습니다.

 

 

 a' or 1 = 1# 이라고 입력해 봅시다. mysql에서 주석은 #이거든요. 이렇게 입력하고 엔터를 누르니까, 테이블 t에 있는 전체 데이터가 출력이 되어 버렸습니다. 왜 그럴까요? 이유는 간단합니다. SQL 문이 아래와 같이 되어버리기 때문입니다.

 

 

 a가 'a'이거나, 1 = 1인 데이터를 모두 뽑아라. 명제 A가 있고 B가 있을 때, A가 거짓이여도 B가 참이면 A or B는 참이 됩니다. 필드 a의 값이 'a'가 아니더라도, 1 = 1은 참이기 때문에, 모든 데이터가 나온 셈입니다.

 

 


 이번에는 다른 장난을 쳐 보겠습니다.

 

 

 역시, SQL injection 중에서 유명한 쿼리 중 하나입니다. 그런데, 저는 t에 있는 모든 레코드가 삭제되는 상상을 했는데, exceptions가 주루룩 떠 버립니다. 맞았는데 왜 틀려요인 셈인데, 이는 ;을 이용해서 여러 개의 구문이 실행되는 것을 어디선가 막았 놓았기 때문입니다.

 

 jdbc mysql에서는, allowMultiQueries 옵션이 있습니다. 이것을 true로 두고 실행시켜 보도록 하겠습니다.

 

 

 다시 실행시켜 보았더니, 아주 깔끔하게 실행이 됨을 알 수 있어요. 실제로, sql은 다음과 같이 입력이 되는데요.

 

 

 필드 a가 'a'인 것을 출력한 다음에 t에 있는 모든 레코드들을 지워버리는 문장을 수행합니다. 정말 t에 있는 내용들이 모두 지워졌을까요? 확인해 보는 방법은 매우 간단합니다.

 

 

 t에 있는 모든 레코드들을 긁어와 보면 됩니다.

 

 

 아무것도 없네요.

 


 그러면, prepareStatement는 어떻게 동작할까요? t에 (a,b), (c,d), (e,f)를 insert를 하겠습니다.

 

 

 ?로 mapping을 시키고, setXXX로 mapping을 시킨다는 것 정도는 아실 겁니다. 이제 똑같이 injection 문장을 수행해 보겠습니다.

 

 

 a' or 1 = 1#을 입력하면 프로그램 1에서는 모든 데이터가 출력되었습니다. 그런데, 프로그램 2를 수행시켜 보니, 아무것도 출력이 되지 않았습니다. 이게 어떻게 된 일일까요? SQL문을 출력해 보겠습니다. logger를 쓰는 게 best이기는 합니다만..

 

 

 뒤져보니 이런 메서드가 떡하니 있습니다. 느낌상 이것인 듯 싶은데. 일단 써 보겠습니다.

 

 

 먼저 a' or 1 = 1 #을 입력했을 때입니다. Query가 보이는데요. ' 앞에 \가 붙었음을 알 수 있습니다. escaping이 되었다고 이야기 합니다. 이 쿼리를 알아보기 쉽게 workbench에 옮겨보도록 하겠습니다.

 

 

 요렇게 됩니다. '라는 문자가 이스케이핑이 되었기 때문에, 결론적으로 필드 a의 값이 문자열 a' or 1 = 1# 과 같은 레코드를 찾는 쿼리로 바뀌었습니다.

 

 

 그러면 문제의 이 쿼리는 어떨까요? 문제의 쿼리를 그대로 복사해 보겠습니다.

 

 

 ' 문자가 이스케이핑이 되었음을 알 수 있습니다.