sql 인젝션 막는 방법과 서버에서 확인할 순서

sql 인젝션 막는 방법과 서버에서 확인할 순서

얼마 전 운영 중인 사이트의 검색 요청이 갑자기 늘어 로그를 본 적이 있습니다. 트래픽 자체는 크지 않았지만 검색어 자리에 SQL 문법처럼 보이는 문자열이 반복해서 들어왔습니다. 이런 경우 서버 사양을 올려도 해결되지 않습니다. 입력값이 데이터베이스 명령으로 해석될 수 있는지부터 확인해야 합니다.

sql 인젝션은 사용자가 보낸 값을 SQL 문장에 이어 붙일 때 생깁니다. 공격이 성공하면 조회 범위가 달라지거나 데이터가 수정될 수 있습니다. 오류 메시지와 응답 시간만으로 내부 정보를 추측하는 경우도 있습니다. 작은 게시판이나 관리자 로그인 화면도 예외가 아닙니다. 방문자가 적다는 사실은 방어 근거가 되지 않습니다.

먼저 입력값이 쿼리에 들어가는 경로를 찾는다

점검할 곳은 로그인과 검색만이 아닙니다. 게시글 번호, 상품 필터, 날짜 범위, 페이지 이동, 관리자용 엑셀 다운로드처럼 요청값으로 조회 조건을 만드는 기능을 함께 봐야 합니다. 특히 오래된 코드에는 문자열을 이어 붙여 WHERE 절을 만드는 부분이 남아 있기 쉽습니다.

저는 요청값이 들어오는 지점부터 데이터베이스 호출까지 한 번 따라갑니다. 중간에 숫자로 바꿨는지보다 최종 SQL을 어떻게 만들었는지가 중요합니다. 숫자 변환은 해당 항목의 형식을 제한하는 데 도움이 되지만, 다른 문자열 입력까지 안전하게 만들지는 못합니다. 화면에서 선택 메뉴만 보여줘도 요청값은 사용자가 직접 바꿔 보낼 수 있습니다.

  • 로그인, 검색, 상세 조회, 관리자 기능에서 요청값을 받는 항목을 적습니다.
  • SQL 문자열에 요청값을 직접 이어 붙이는 코드를 찾습니다.
  • 공통 데이터베이스 함수와 ORM의 원시 쿼리 호출도 확인합니다.
  • 수정한 뒤에는 정상 입력과 잘못된 입력에서 조회 결과가 의도대로 나오는지 확인합니다.

입력값에 작은따옴표가 있는지만 검사하는 방식은 피하는 편이 낫습니다. 데이터베이스마다 문자열과 형식 처리 방식이 다르고, 공격 형태도 한 가지가 아닙니다. 문자 몇 개를 막는 규칙보다 쿼리 구조와 값을 분리하는 방식이 확실합니다.

값은 바인딩하고 쿼리 구조는 허용 목록으로 제한한다

사용자 입력이 WHERE 절의 값이라면 준비된 문장과 매개변수 바인딩을 씁니다. 예를 들어 회원 번호를 조회할 때 SQL 문장에는 값이 들어갈 자리를 두고, 실제 번호는 별도 매개변수로 전달합니다. 그러면 번호 자리에 SQL처럼 보이는 문장이 와도 조회 조건의 구조를 바꾸지 못합니다. 사용하는 언어와 데이터베이스 드라이버에서 실제로 서버 측 매개변수 처리가 이루어지는지도 확인할 필요가 있습니다.

여기서 자주 놓치는 부분이 정렬 열과 테이블 이름입니다. 대부분의 드라이버는 값은 바인딩할 수 있지만 열 이름이나 ASC·DESC 같은 쿼리 구조를 같은 방식으로 바인딩하지 못합니다. 정렬 옵션이 필요하면 요청값을 그대로 붙이지 말고, 코드에서 미리 정한 열 이름 중 하나로만 변환해야 합니다. 예를 들어 최신순과 이름순 두 가지만 제공한다면 그 두 경우에 대응하는 SQL 조각만 코드에 둡니다.

ORM을 쓴다고 자동으로 끝나는 것도 아닙니다. ORM의 조건식 API는 대체로 값을 분리해 처리하지만, 성능 때문에 작성한 원시 SQL에서 문자열을 합치면 같은 문제가 돌아옵니다. 검색어에 부분 일치 검색을 적용할 때도 매개변수 바인딩을 유지하고, 퍼센트 기호와 밑줄의 와일드카드 동작이 제품 요구사항에 맞는지 따로 판단해야 합니다. 이 부분은 인젝션 방어와 검색 결과의 정확성을 함께 좌우합니다.

광고

데이터베이스 계정 권한과 오류 노출도 줄인다

쿼리를 고친 뒤에는 애플리케이션이 쓰는 데이터베이스 계정을 봅니다. 웹 요청을 처리하는 계정에 테이블 생성이나 사용자 관리 권한까지 줄 이유는 드뭅니다. 읽기만 하는 기능과 쓰기 기능을 분리할 수 있다면 피해 범위도 줄어듭니다. 다만 권한을 줄이기 전에는 배치 작업과 관리자 기능이 같은 계정을 쓰는지 확인해야 합니다. 운영 중 갑자기 백업이나 게시 작업이 멈추는 사고가 종종 여기서 납니다.

사용자 화면에는 데이터베이스 오류 원문을 보여주지 않는 편이 좋습니다. 테이블 이름, 쿼리 일부, 드라이버 정보가 그대로 드러날 수 있기 때문입니다. 화면에는 일반적인 오류 안내를 주고, 내부 로그에는 요청 식별자와 발생 위치를 남겨 추적합니다. 비밀번호나 세션 값, 검색어 전체를 무심코 로그에 쌓지 않도록 기록 범위도 정해야 합니다.

웹 방화벽은 반복되는 의심 요청을 줄이는 데 쓸 수 있습니다. 하지만 정상 요청까지 막을 수 있고, 애플리케이션의 취약한 쿼리를 대신 고쳐 주지는 못합니다. 규칙을 켰다면 차단 건수만 볼 게 아니라 실제 이용자의 검색과 로그인에 영향이 없는지도 같이 봐야 합니다.

의심 요청이 보이면 로그와 백업을 함께 확인한다

운영 중 의심 흔적을 발견했다면 먼저 시간대를 잡습니다. 접근 로그에서 같은 경로와 매개변수로 반복된 요청을 확인하고, 그 시간대의 애플리케이션 오류와 데이터베이스 로그를 맞춰 봅니다. 비정상 요청이 있었다는 사실만으로 데이터 유출을 단정할 수는 없습니다. 반대로 오류가 없었다는 이유만으로 안전하다고 볼 수도 없습니다.

데이터 변경 가능성이 있다면 해당 계정의 권한, 최근 변경 이력, 백업 시점을 확인합니다. 복구가 필요할 때는 마지막 백업 파일이 있다는 사실보다 실제로 읽히고 복원되는지가 중요합니다. 저는 운영 데이터에 바로 덮어쓰기 전에 별도 환경에서 복원해 누락 범위와 애플리케이션 동작을 확인합니다. 백업 주기가 하루라면 최악의 경우 하루 가까운 변경분이 빠질 수 있으니, 서비스에서 감당할 수 있는 손실 시간을 기준으로 주기를 정해야 합니다.

작은 사이트에서는 고가의 보안 장비보다 쿼리 수정, 최소 권한, 오류 처리, 복원 가능한 백업이 먼저입니다. 이 네 가지가 갖춰져 있으면 의심 요청이 들어와도 확인할 순서가 분명해집니다. 서버 운영에서는 그런 기본 작업이 장애를 줄이는 데 가장 오래 남습니다.

sql 인젝션 막는 방법과 서버에서 확인할 순서 - 요약
광고

댓글 0

평가를 남기려면 로그인해 주세요.

모두 ID로 로그인

불러오는 중…