Hello,

kok nae-ga ha-myun an-dweneun MAGIC...🧚

개발 일지

SQL Injection 공격이 들어왔다 😵

도담 🌱 2024. 8. 14. 10:32

주말에 홈페이지 접속이 안된다며 계속해서 울렸던 알람, 우선은 당장 운영이 되어야 했기에 노트북을 챙겨다녀야 했고 모니터링하며 주말을 보냈는데요😭 그리고 다음날 출근하여 원인을 분석하기 위해 확인중일때 또 같은 현상이 발생하기 시작했습니다.

.
.
.

문제 발생 초기 당시...

주말에 해당 문제가 발생했을 당시 AWS에 접속하여 인스턴스와 데이터베이스를 확인해보았는데 데이터베이스쪽 세션이 가득차있는 상태로 있는 것을 확인했습니다. 당시 지표를 잠깐 참고하자면 커넥션이 확 오른 상태였는데요.

RDS의 성능 개선 도우미쪽을 확인해보았는데 이벤트 조회 쿼리의 호출이 많이 보였고 이벤트 오픈 기간이다 보니 접속이 몰리면서 문제가 되었으려나 추측을 했었습니다. 따라서 우선 일시적으로 DB성능을 올려둔 상태로 주말을 모니터링을 하기로 했습니다.
허나 앞서 말씀드렸듯 다음날 출근하여 원인 분석을 하려는데 같은 현상이 발생하기 시작했습니다!🙉

.
.
.

계속된 문제에 원인을 찾아나섰다 👀

똑같이 데이터베이스쪽 락이 꽉 차 있었고, 성능 개선 도우미를 확인했을때 적혀있는 SQL문은 초당 실행 횟수는 많았으나 쿼리 실행 시간은 0.n초로 짧은 상태였습니다. 여기서 아무리 사용자가 많이 몰렸다고 해도 무거운 쿼리라고 판단되지 않았으며, 월요일 오전부터 사용자가 많을 것이라곤 생각이 되지 않았습니다.

그래서 이번에는 RDS 로그 파일을 확인해보기로 했습니다. 다행히 slowquery에 대한 로깅을 하도록 설정이 되어있었고 error-running.log와 slowquery.log에 대해 확인 할 수 있었습니다.
원인은 slowquery.log에서 확인할 수 있었는데요. 문제가 되었던 시간대의 로그를 확인해보니 무려 85s가 걸리는 쿼리가 계속해서 실행되고 있었습니다. 아래는 문제가 되었던 쿼리의 일부인데요...

WHERE cha = 1234 AND SLEEP(5) AND isShow = 1

보시면 SQL의 조건문에 뜬끈없는 SLEEP함수가 있었습니다. 다만 코드를 확인해봐도 SLEEP함수는 찾을 수 없었고, 아무리 생각해봐도 해당 함수를 사용할 이유가 없다고 생각되었습니다.

여기서 혹시 URL Parameter로 받은 query string값을 그대로 SQL문으로 넘길 수 있나? 라는 의심을 하기 시작했고 비슷한 쿼리의 행방을 찾아보기 시작했습니다.

그리고 의심이 확신이 되어버렸는데요... 오래 방치된 사이트에 PHP로 이루어져있다보니 보안에 많이 취약한 상태였는데요. 실제로 URL Parameter값을 그대로 사용하고 있다보니 url에서 ?id=1234 AND SLEEP(5) 으로 호출 할 경우 똑같은 현상을 재현할 수 있었습니다. 오엠지..🥹

.
.
.

원인을 찾았으니 해결을 해야지!

query string 타입을 검증 할 수 있도록 함수를 추가하여 수정해두었습니다.
FILTER_VALIDATE_INT 참고 링크

filter_var((int)$param, FILTER_VALIDATE_INT);

해결한 뒤에는 같은 공격이 발생하지 않은 것으로 보아 해당 원인이 맞은 것 같습니다 😆 비교적 빠른 시간에 원인을 찾고 해결하게 되어 다행이라는 생각과 함께 이번 사건으로 인해 개인적으로 ORM을 사용하는게 이런 보안적인 측면에서도 이점이 있다라는 생각을 문득 하며 글을 마무리 하도록 하겠습니다 :)