SQL인젝션은 웹 페이지 입력을 통해 SQL 구문에 악의적인 코드를 위치시키는 코드 인젝션 기술이다.
이를 통해 데이터베이스에 피해를 입힌다. 가장 흔한 웹 해킹 기술 중 하나이다.
대표적인 기법만 서술하였으며, 자세한 공격기법은 sql injection cheat sheet
를 참고하면 된다.
로그인 폼(form)을 대상으로 공격을 수행한다. 공격에 성공하면 정확한 정보 없이 로그인에 성공한다.
- 보안이 허술한 특정 웹 페이지를 대상으로 SQL 인젝션 공격을 시도해서 관리자 세션을 탈취, 개인정보 유출
조작된 쿼리가 실행되게 하여 개인정보 혹은 기밀정보에 접근하여 데이터를 획득한다.
또한, 데이터 값을 변경하거나 테이블을 지워버릴 수 있다.
- 보안이 허술한 웹 페이지에서 정상적인 입력값 외의 질의 구문 삽입 및 실행
일부 데이터베이스의 경우 확장 프로시저 호출도 가능하다. 이를 이용하면 원격으로 시스템 명령어를 수행할 수 있다.
즉, 해당 서버의 모든 자원에 접근할 수 있게된다.
SQL인젝션은 다음 조건을 모두 만족해야 가능하다.
-
DB와 연동된 웹 애플리케이션
-
외부 입력값을 DB 쿼리문에 사용
웹 애플리케이션의 경우 대부분이 위 두가지 조건을 충족하기 때문에 SQL인젝션은 유효한 공격 기법이 될 수 있다.
Where 조건이 무조건 참이 되도록 조작한다.
주석을 이용해 조건절이 작동하지 않게 하거나, or '1' = '1'
과 같은 항상 참인 조건을 이용한다.
# 주석을 이용
[ 외부 입력값 ]
UserID: admin'--
Password: 1234
[ 실행되는 쿼리문 ]
Select * From Users Where UserID = 'admin'-- And Password = '1234'
# 항상 참인 조건을 이용
[ 외부 입력값 ]
UserID: test
Password: 1234' or '1'='1
[ 실행되는 쿼리문 ]
Select * From Users Where UserID = 'test' And Password='1234' or '1'='1'
또한, 이를 응용하여 두 개 이상의 명령어를 연속으로 실행시킬 수 있다.
# 세미콜론을 이용하여 여러개의 구문 실행
[ 외부 입력값 ]
UserID: admin' ; DELETE From Users--
Password: 1234
[ 실행되는 쿼리문 ]
Select * From Users Where UserID = 'admin' ; DELETE From Users -- And Password='1234'
UNION을 이용하여 원본 쿼리가 반환하는 컬럼의 수를 알아낸다.
[ 외부 입력값 ]
UserID: test' UNION SELECT 1,1,1,1 --
Password: 1234
[ 실행되는 쿼리문 ]
Select * From Users Where UserID = 'test' UNION SELECT 1,1,1,1 -- And Password='1234'
에러 페이지 컨트롤을 하지 않으면 클라이언트에게 에러를 보여준다. 이를 이용하여 컬럼을 알아낼 수 있다.
또한 알아낸 컬럼 수를 기반으로 시스템 테이블의 정보를 조회할 수도 있다.
Select *
From Users
Where UserID = 'test'
UNION
SELECT name, object, 1,1
FROM sys.tables
# 시스템 정보를 담은 테이블은 데이터베이스마다 다르다.
# 중요한것은 이와 같은 쿼리로 테이블을 관리하는 테이블에서 정보를 추출할 수 있다는 것이다.
-- And Password='123'
MS SQL 서버의 경우 프로시저를 이용하여 DB조회 결과에 프로그램 실행 결과를 포함시킬 수 있는데,
이를 이용하여 운영체제 명령어를 실행시킬 수 있다.
이외에도 조회결과를 파일로 저장하는 기능 등에 웹쉘을 삽입하여 공격할 수 있다.
일반적인 SQL 인젝션 공격은 공격하는 대상 웹페이지가 오류를 출력하거나, 쿼리 결과 리스트를 제공해야 한다.
그렇지 않을 경우 유효한 공격 기법이 Blind SQL 인젝션이다.
Blind SQL 인젝션은 쿼리 결과의 참/거짓 으로부터 DB 값을 유출해내는 기법이다.
가장 간단한 예시로는 ID 찾기 혹은 게시판 검색 같은 기능의 결과값이 제대로 출력되는지 확인하여 참/거짓 판별 요소를 찾는 것이다.
AND 조건에 논리식을 대입하여 참/거짓 여부를 알아내는 방식이다.
# 유효한 검색단어와 항상 참이되는 조건 부여
[ 외부 입력값 ]
검색 : 게시글명' AND 1=1--
# 유효한 검색단어와 항상 거짓이 되는 조건 부여
[ 외부 입력값 ]
검색 : 게시글명' AND 1=2--
#실행되는 쿼리
SELECT * FROM TB_Boards WHERE Title = 'hello' AND ...
위와 같이 게시판을 이용하여 참과 거짓 조건을 판단할 수 있다.
또한, 여기에 AND 조건을 덧붙여 알고싶은 정보를 추출할 수 있다.
만약 참/거짓에 따른 응답이 항상 같다면 응답결과만으로 참/거짓을 판단할 수 없다.
이럴 경우 시간을 지연시키는 쿼리를 주입하여 참/거짓 여부를 판단할 수 있다.
[ 외부 입력값 ]
검색: 게시글 명' ; IF SYSTEM_USER='sa' WAITFOR DELAY '00:00:5'-- # MS SQL 예시
만약 시스템 계정의 이름이 'sa' 라면 응답이 5초 지연될 것이고, 아니라면 응답이 즉시 이루어질 것이다.
웹 방화벽은 응용계층(application layer)의 패킷 내용을 기준으로 패킷의 보안성을 평가하고, 정해진 규칙에 따라 제어한다.
SQL 인젝션에 대한 규칙을 설정하여 공격에 대비할 수 있다.
기업의 규모가 크거나, 보안이 중요한 환경 혹은 예산이 충분하다면 물리적 방화벽 도입이 가장 좋은 방법이다.
어플라이언스 형태의 제품을 사용하거나, SECaaS(Security as a Service) 형태의 클라우드 기반의 솔루션 도입도 좋은 방법일 수 있다.
어플라이언스(appliance) : 설치나 설정 없이 하드웨어 자체로 이용할 수 있는 기기 혹은 특정 소프트웨어가 이미 장착되어 최적화된 상태의 기기
전용 웹 방화벽 장비 도입이 어렵다면, 공개 웹 방화벽을 고려해볼 수 있다.
물리적인 장비 대신 논리적인 구성으로 웹 방화벽 역할을 수행한다.
윈도우 서버에서는 WebKnight, 아파치 서버에서는 ModSecurity가 주로 사용된다. 많이 사용하기 때문에 미리 정의된 규칙을 적용하여 사용할 수 있다.
보안적 측면에서 외부 입력값은 신뢰하면 안된다. 모든 입력값을 의심해야한다.
이 때, 입력값은 사용자가 입력한 값 뿐만 아니라 프록시 변조와 같은 외부 값 또한 포함된다.
프록시 변조를 이용한 입력값 조작은 burp suite과 같은 툴을 이용하여 테스트 할 수 있다.
SQL 인젝션의 가장 기본적인 대응 전략은 입력값 유효성을 검사하는 것이다.
검사를 위한 방법은 블랙리스트 방식과 화이트리스트 방식이 있다.
특정 문자나 키워드를 제한하는 방식이다. 공격에 사용되는 주석 문자나, IF, UNION과 같은 키워드등을 필터링 하는 것이다.
블랙리스트 방식보다 강력한 방법이다. 블랙리스트는 특정 키워드를 제외한 모든 입력값을 허용하지만,
화이트리스트 방식에서는 허용된 문자를 제외한 모든 입력값을 금지한다.
웹 어플리케이션의 기능에 따라 화이트리스트를 다르게 유지해야 한다.
웹 애플리케이션에서 정적쿼리만 사용한다면 SQL 인젝션을 신경 쓸 필요가 없다.
하지만, 현실적으로 동적 쿼리를 사용하지 않을 수 없다. 따라서 매개변수화된 쿼리(미리준비된 쿼리)를 사용해야 한다.
구조화된 쿼리는 동적 쿼리를 정적 쿼리처럼 사용하는 기법이다. 쿼리 구문에서 외부 입력값이 SQL 구문의 구조를 변경하지 못하도록 정적 구조로 처리한다.
쉽게 말해, 입력값을 매개변수로 처리하는 것이다.
// JDBC
String sql = "SELECT column FROM table WHERE column = ?";
PreparedStatement pstmt = conn.preparedStatement(sql);
pstmt.setString(1,"name of column");
// Node-mySql
let sql = 'SELECT column FROM table WHERE column = ?';
let param = 'column name';
conn.query(sql, param, function(err, rows, fields){});
DB 오류 정보를 사용자에게 그대로 노출하면 안된다.
개발단계에서 디버깅을 위해 내부 정보를 상세히 알려주는 경우가 많은데, 이러한 정보가 노출되면 DB 구조를 파악하여 데이터 유출을 시도할 수 있다.
따라서 사용자에게 커스텀 오류 페이지를 제공해야 한다.
또한, 너무 자세한 안내 메세지도 주의하여야 한다.
가장 접하기 쉬운 예로, 로그인에 실패했을 때 ID와 비밀번호가 무엇이 틀렸는지 알려주는 것이 있다. 이는 공격의 범위를 좁히는 수단이 될 수 있다.
이러한 경우 로그인에 실패하였습니다.
와 같은 추상적인 메세지가 더 좋을 수 있다.
관리자가 사용하는 DB 계정과 어플리케이션에서 접근하는 DB 계정을 분리하여 관리해야 한다.
또한, 어플리케이션에서 접근하는 DB 계정에는 최소 권한 원칙(least privilege policy)을 적용하여 최소한의 권한만 부여한다.
이를 응용하여 모든 DB 액세스를 프로시저로 만들고, 해당 프로시저들의 실행 권한만 부여하는 방법, CRUD 각각의 권한을 분리하여 접근을 제한하는 방법이 있다.
또한, 반드시 필요한 프로시저가 아니면 제거하는 것이 좋다.
MS SQL Server의 xp_cmdshell 프로시저는 db에서 시스템 명령을 실행할 수 있도록 해주는 확장 프로시저이다. 이러한 프로시저는 제거하는 것이 좋다.
모의 해킹 혹은 취약점 점검 툴을 이용하여 취약점 점검을 정기적으로 수행해야 한다. 웹사이트의 업데이트는 빈번하게 이루어지기 때문이다.
다른 곳의 보안이 철저하더라도, 이벤트 페이지가 잘못 작성되었으면 공격이 이루어질 수 있다.
SQL 인젝션은 오류를 유발시키거나 동일한 페에지 및 기능을 반복적으로 호출하는 형태로 이루어진다.
따라서 500오류가 비정상적으로 많이 발생하거나, 동일한 IP에서 동일한 페이지를 과다하게 호출하는 경우에 대한 규칙을 설정하여 관리해야한다.
- [웹 보안]SQL Injection
- SQL Injection Cheat Sheet 번역
- SQL Injection Cheat Sheet - netsparker
- JAVA 시큐어 코딩 가이드 - 행정안전부