SQL Injection 은 공격자가 웹 양식을 통한 작업 요청 형태로 SQL를 웹/앱에 직접 제공하여 백엔드 데이터베이스 및/또는 앱 데이터에 액세스할 수 있는 보안 공격을 말합니다.
그러니까 간단하게 말하자면, 악의적인 사용자가 웹 어플리케이션의 입력 폼에 악성 SQL 쿼리를 삽입하여, 데이터베이스에 대한 비인가된 접근 및 조작을 시도하는 공격 기법이다.
한마디로, 해킹이라고 보면 편할 듯 하다.
SQL INJECTION을 하는 목적은 여러가지 겠지만, 대표적으로 4개라고 생각한다.
공격의 핵심은 클라이언트 측에서 SQL 쿼리에 신뢰할 수 없는 데이터가
입력되었을 때, 데이터가 쿼리 로직의 일부로 해석되어 DB에서 실행될 때
발생한 것이라고 한다.
SQL 에서 Union 키워드는 두 개의 쿼리문에 대한 결과를 통합해서 하나의 테이블로 보여주게 하는 키워드 이다. 정상적인 쿼리문에 Union 키워드를 사용하여 인젝션에 성공하면, 원하는 쿼리문을 실행할 수 있게 된다. Union Injection을 성공하기 위해서는 두 가지의 조건이 있는데, 하나는 Union 하는 두 테이블의 컬럼 수가 같아야 하고, 데이터 형이 같아야 한다.
예시 데이터베이스 테이블 "users"가 다음과 같다고 가정해봅시다.
SELECT * FROM users WHERE id='input_id' AND password='$input_password'
보안 취약점이 있는 로그인 페이지에서 사용자가 입력한 값을 다음과 같이 SQL 쿼리에 삽입하는 경우,
SELECT * FROM users WHERE id='' OR '1'='1' --' AND password='' OR '1'='1' --
이 쿼리는 id와 password가 입력값과 상관없이 '1'='1' 이 참(true)인 모든 레코드를 반환하게 된다. 즉, 공격자는 아이디와 패스워드를 모르더라도 로그인을 성공시킬 수 있게 된다는 것이다.
아래와 같은 테이블이 있다고 가정해봅시다.
로그인 페이지에서 사용자가 입력한 아이디와 패스워드를 다음과 같이 SQL 쿼리에 삽입하는 경우에는,
SELECT * FROM users WHERE id='input_id' AND password='$input_password'
다음과 같이 INJECTION을 시도할 수 있다.
SELECT * FROM users WHERE id='1' OR 1=1 --' AND password='foobar'
이 쿼리는 id가 '1'이거나 1=1(항상 참)인 모든 레코드를 반환하게 된다. 따라서, 공격자는 아이디를 모르더라도 로그인을 성공시킬 수 있게 된다.
Boolean-based SQL Injection은 주로 WHERE 절에 사용되는 불리언 연산자 (AND, OR, NOT) 및 비교 연산자 (=, <, >)를 이용하여 SQL Injection을 시도한다.
시스템 외부로 데이터를 전송하여 공격자가 해당 데이터를 수집하는 방식으로, 쿼리 결과가 네트워크 트래픽, DNS 등의 채널을 통해 전송된다. 이 방식은 시스템 내부에 접근할 수 없는 경우, 외부적인 수집 방법으로 정보를 수집할 때 유용하다.
앞에 했던 예제들과 같은
테이블이 있다고 가정할 때,
사용자가 로그인 할 때 다음과 같이 SQL 쿼리에 삽입하는 경우,
SELECT * FROM users WHERE id='input_password'
다음과 같이 INJECTION을 시도할 수 있다.
SELECT * FROM users WHERE id='1'; SELECT pg_sleep(10)--' AND password='foobar'
이 쿼리는 id가 '1'인 레코드를 반환하면서, pg_sleep 함수를 이용하여 10초간 대기한다. 이 때, 시스템 외부로 네트워크 트래픽이 발생하게 되어 공격자는 이를 수집하여 SQL Injection을 성공시킬 수 있는 것이다.
Out-of-band SQL Injection은 보통 UNION-based나 Blind SQL Injection과 함께 사용된다고 한다.