지금까지는 SQL Injection이 가능한 곳을 알고 있는 경우에 어떤 공격을 시행할 수 있는지 알아봤다면,
이제는 SQL Injection이 가능한 곳, 즉 SQL Injection Point를 찾는 과정에 대해 알아보도록 하겠습니다
‼️ SQL Injection 포인트는 어디에 있을까?
- DB에 질의문을 사용하는 곳
- 파라미터
✅ DB에 질의문을 사용하는 곳
일단 SQLi가 일어나려면 SQL문을 삽입할 수 있어야 하니 당연히 DB에 SQL 질의문을 사용하는 곳이어야 합니다
이때, 단순히 직접 사용자가 입력을 넣을 수 있는 곳(ex. 로그인, 게시판 검색 등) 뿐만 아니라, SQL문에 필요한 그 모든 곳을 고려해야 합니다
✅ 파라미터
앞에서 단순히 직접적인 입력 공간 뿐만 아니라, QL문에 필요한 그 모든 곳을 고려해야한다고 했는데요
그 모든 곳이라는 것은 우리가 웹페이지 내에서는 입력하거나 확인할 수 없더라도 패킷 데이터의 헤더, 파라미터 등과 같이 SQL문에 들어가는 정보를 말합니다
이 경우에는 서버와 클라이언트가 주고 받는 패킷을 관찰하고, DB에서 데이터를 받아내려면 어떤 정보가 필요할 지 추측해 그 부분이 SQLi의 포인트가 되는 지 확인해야합니다
그러면 예시를 통해 알아보도록 하겠습니다
로그인 하여 개인정보를 수정할 수 있는 마이페이지가 있습니다
여기서 SQLi 포인트를 찾기 위해 SQL에서 SELECT
가 활용될만한 부분을 생각해봅니다
아마 가장 눈에 띄는 비밀번호 변경은 UPDATE
구문일 것입니다
SELECT
가 필요할 만한 곳을 생각해보니, 아이디가 적혀있는 부분이 떠오르네요
마이페이지에서 내 정보를 알 수 있으려면 쿠키나 세션이 필요할 것입니다
그리고 그곳에서 아이디 정도는 SELECT
했을 것입니다
그러니 request 패킷을 관찰해보도록 하겠습니다
cookie에 아이디가 들어가 있네요
이 아이디를 SELECT 했을 것이라 가정하고 참인 SQL문과 거짓인 SQL문을 삽입하여 차이를 관찰해보도록 하겠습니다
쿠키 부분에 normaltic429' and '1'='1
이라는 참인 SQL문을 삽입해보았습니다
그리고 normaltic429' and '1'='2
이라는 거짓인 SQL문을 삽입해보았습니다
둘의 차이가 보이시나요?
참일 때는 Nothing here 이라는 문구가 있던 곳에, 거짓일 때는 아무것도 출력되지 않는 것을 볼 수 있습니다
따라서 삽입된 SQL문의 참/거짓에 따라 다른 결과가 나오는 것을 보아, cookie 값이 SQLi 포인트라는 것을 알 수 있습니다
로그인을 하고 글을 쓸 수 있는 게시판이 있습니다
이 게시판에서는 작성자, 제목, 내용을 기준으로 검색이 가능합니다
작성자를 기준으로 검색하는 경우의 패킷을 관찰해보았습니다
POST 방식으로 아래의 파라미터가 전달되는 것을 확인할 수 있었습니다
전달되는 값을 보니 대충 SELECT 게시글 등 FROM 게시판테이블 WHERE username like '%검색한 값%'
과 같은 형태의 SQL문이 작성될 것 같네요
만약 username
부분을 조작해 SQLi 포인트인지 알려면 어떻게 변경해야 할까요?
추측한 SQL 구문에 맞춰 1=1 and username
으로 수정해볼 수 있습니다
참인 구문인 1=1 and username
을 삽입해 보았습니다
그리고 거짓인 구문인 1=2 and username
를 삽입해 보았습니다
삽입한 SQL문의 참/거짓이 바뀌었더니 출력 결과도 달라졌습니다
따라서 SQL문의 Where절 column에 해당하는 부분이 SQLi 포인트임을 알 수 있습니다
추가로 order by
절에 SQLi 포인트가 존재하는 경우에 대해 알아보겠습니다
order by
절에 SQLi 포인트가 존재하는 경우에는 order by 컬럼
의 컬럼 부분에 SQL문이 삽입되어야 합니다
그런데 이 order by
절은 뒤에 추가로 붙을 수 있는 SQL문이 많지 않아 지금까지와는 다른 방법을 생각해야 합니다
이 때 필요한 것이 case when
입니다
‼️ case when
if문과 같은 역할을 하는 SQL 문법
case when (조건) then (조건이 참인 경우) else (조건이 거짓인 경우) end
와 같이 사용
order by
절에 case when
을 사용해 order by 컬럼명
의 컬럼명 부분이 SQL문의 참/거짓에 따라 달라지도록 만들어 보겠습니다
order by case when (1=1) then 1 else (select 1 union select 2) end
이 경우 조건인 (1=1)
이 참이기 때문에 결과가 1이 되고, 결론적으로 order by 1
이 되어 정상 작동하게 됩니다
order by case when (1=2) then 1 else (select 1 union select 2) end
그렇다면 이렇게 작성하면 어떨까요?
조건인 (1=2)
가 거짓이기 때문에 결과가 행이 2개인 테이블이 되고, 이 결과는 order by
에 적용될 수 없기 때문에 에러가 발생하게 됩니다
그렇다면 이렇게 조건이 참/거짓일 때 달라지는 SQL 결과에 따라 reponse에도 다른 점이 있는지 확인하고, SQLi 포인트인지 확인할 수 있게 됩니다
앞에서 언급한 case when (1=2) then 1 else (select 1 union select 2) end
구문에서 (select 1 union select 2)
과 같이 에러를 유발하는 코드를 작성했는데요,
order by 99999
와 같은 구문도 분명 에러가 발생할 만한 구문인데 왜 굳이 위와 같이 작성했을까요?
order by 99999
같은 경우는 분명 에러가 날 상황인데, 참과 같은 결과가 나오는 경우가 있습니다
그런데 같은 위치에서 다른 DB에러가 발생하는 것은 확인된다면, 일부러 에러를 유발하는 다른 SQL문으로 바꾸어야 합니다
1️⃣ cookie, HTTP request 헤더 내 SQLi 포인트 확인
2️⃣ SQL 문 where 절의 column에서 SQLi 포인트 확인
3️⃣ SQL 문 order by 절에서 SQLi 포인트 확인
지금까지 SQL Injection을 어떻게 하는 지 알아봤다면,
이제 SQL Injection에 어떻게 대응해야 하는지 알아보겠습니다
Prepared statement는 SQL 질의문을 미리 기계어로 컴파일 해두는 방법입니다
사용자에게 입력받아야 할 파라미터 부분은 ?
를 활용해 미리 구멍을 뚫어놓고 나머지 부분은 미리 컴파일 해둡니다
이렇게 미리 컴파일 하는 경우 서버의 SQL 질의문 구조가 절대 바뀌지 않으며, SQL Injection을 위한 SQL문을 삽입하더라도 모두 문자열과 같이 인식되어 SQL Injection이 불가능해집니다
Prepared Statement 방법을 활용하면 SQLi가 불가능한데, SQL Injection은 왜 알아야 하는 걸까요?
Prepared Statement를 잘못 사용한 경우가 있습니다
예를 들어 where id = ?와 같이 입력 받아야 하는 파라미터 부분은 ?
로 표기해야 하는데,
잘못 사용하여 Prepared Statement가 제대로 적용되지 않은 경우 SQLi가 가능하게 됩니다
위 세가지를 파라미터로 받는 경우는 Prepared Statement를 활용할 수 없습니다
그렇다면 SQLi를 시도할 포인트가 발생하게 됩니다
prepared statement를 활용할 수 없다면 다른 방법을 취해야 합니다
바로 필터링인데요, 필터링에는 2가지 방법이 있습니다
Black list 필터링 : 특정 단어를 사용할 수 없게 필터링 하는 방법
White list 필터링 : 특정 단어만 사용하도록 필터링 하는 방법
이 중 정해진 컬럼 명 등 특정 단어만 사용 가능하도록 white list 필터링을 하여 SQLi에 대응할 수 있습니다
지금까지 SQLi 포인트 찾기 및 SQLi 대응 방법에 대해 알아보았습니다