SQL Injection이 무엇인지 알아보기 전에 이것이 얼마나 위험한 공격 방식인지 간단하게 적어놓고 시작하겠습니다.
SQL Injection은 간단합니다. 우리가 입력하는 데이터들을 데이터베이스에 저장하기 위해서는 Database의 서버에서 쿼리문을 실행하여 해당 동작을 수행하면서 이루어집니다.
아주 간단한 예시로 주석 처리를 이용한 방식이 있습니다.
예를 들어서, 사용자를 로그인하는 쿼리문이 다음과 같다고 생각해 봅시다.
Select user From user_table Where username = '[ID 입력값]' AND password = '[pw 입력값]';
이와 같이 실행된다고 하고, 주석의 처리는 --
으로 수행된다고 해봅시다.
여기서 해커가 관리자의 아이디가 Admin
인 것을 확인한 후 다음과 같은 입력값을 보낸다고 해봅시다.
username: Admin'; --
password: random
해당 작성을 토대로 작성되는 쿼리문은 다음과 같습니다.
Select user From user_table Where username = 'Admin'; --' AND password = 'random';
주석 처리 --
에 의해서 username만 일치하더라도 로그인이 가능하도록 동작하게 변경되었고, 해당 방식의 공격을 재대로 예방하지 않았다면 해커는 관리자 권한을 가진 계정으로 로그인에 성공할 것입니다.
이제 정리를 시작해 봅시다.
SQL 인젝션은 웹 애플리케이션의 보안 취약점을 이용하는 공격 방식 중 하나입니다. 이 공격은 애플리케이션의 데이터베이스 쿼리에 악의적인 SQL 코드를 "주입"하여 데이터베이스를 조작하거나 민감한 정보를 탈취하는 것을 목표로 합니다.
SQL 인젝션의 공격 방식에는 다음과 같은 것들이 존재합니다.
UNION
SQL 연산자를 사용하여 여러 쿼리의 결과를 결합하는 방식으로, 추가적인 데이터를 조회할 떄 사용합니다.해당 방법들에 대해서 좀 더 자세하게 정리를 해보겠습니다.
1번의 경우, 맨 처음에 언급했던 것과 같이 폼에 입력한 데이터들이 그대로 쿼리문에 들어갈 경우에 발생할 수 있습니다.
다음의 예시를 보자면,
입력 값에 ' OR 1 = 1--
을 넣어서 보냄으로써, OR의 연산자를 활용하여 로직이 성공하게 한 뒤에 나머지 부분을 주석 처리 하였습니다.
이와 같이 입력한 문자를 그대로 쿼리문에 넣어줄 경우 테이블에 관련된 모든 데이터를 탈취 당할수도 있고, 테이블에 있는 모든 데이터가 날라가는 등의 위험이 존재합니다.
SQL에서 Union 키워드
는 두 개의 쿼리문에 대한 결과를 통합해서 하나의 테이블에 보여주게 하는 키워드입니다.
정상적인 쿼리문에 Union 키워드를 사용하여 인젝션에 성공한다면, 원하는 쿼리문을 실행시킬 수 있습니다.
Union Injection을 성공시키기 위해서는 다음 두 조건이 필요합니다.
Board 테이블을 조회하는 쿼리문을 이용하여 사용자의 식별 정보를 가져오는 예시입니다.
Board 테이블의 데이터를 조회하는 것에 성공하면서, Union 키워드가 실행되어 사용자의 정보 또한 가져올 것입니다.
Blind SQL Injection은 데이터베이스로부터 특정한 값이나 데이터를 전달받지 않고, 단순히 참과 거짓의 정보만을 알 수 있을 때 사용합니다. 로그인 폼에 SQL Injection이 가능하다고 가정했을 때, 서버가 응답하는 로그인 성공과 실패 메시지를 이용하여, DB의 테이블 정보 등을 추출해 낼 수 있습니다.
해당 공격 방식에서 에러 메시지를 활용할 수 있기 때문에 세부적인 방식에 따라 Error based SQL Injection이라고도 할 수 있습니다.
먼저, 해당 방식을 좀 더 구체적인 방법으로 구분하자면 2가지로 나눌 수 있습니다.
위의 그림은 Blind Injection에서 Boolean을 기반으로한 공격 방식의 예시입니다. 로그인 폼을 이용하여 임의의 사용자 계정인 abc123
과 함께 특정 구문을 삽입합니다.
해당 구문은 MySQL 에서 테이블 명을 조회하는 구문으로 limit 키워드를 통해 하나의 테이블만 조회하고, SUBSTR
함수로 첫 글자만, 그리고 마지막으로 ASCII를 통해 아스키 값으로 만들어 줍니다. 만약에 조회되는 테이블이 "Users"라면 'U'자가 아스키 값으로 조회될 것이고, 뒤의 연산자와 비교하게 될 것입니다.
공격자는 이 프로세스를 자동화 스크립트를 통하여 단기간 내에 테이블명을 알아낼 수 있습니다.
Time based SQL Injection도 마찬가지로 서버로부터 특정한 응답 대신에 참 혹은 거짓의 응답을 통해서 데이터베이스의 정보를 유추하는 기법입니다.
사용되는 함수는 MySQL 기준으로 SLEEP
혹은 BENCHMARK
입니다.
위의 그림은 현재 사용되고 있는 데이터베이스의 길이를 알아내는 공격입니다.
구문을 보면 알 수 있듯이, 숫자를 바꿔가며 응답이 돌아오는 속도를 비교해가며 문자열의 길이를 추측할 수 있습니다.
Error based SQL은 주로 데이터베이스에 대한 정보를 획득하기 위해서 사용됩니다. SQL의 잘못된 문법이나 자료형 불일치 등에 의해 데이터베이스가 알려주는 데이터베이스 오류 메시지에 의존하여 수행되는 공격 기법입니다.
웹 애플리케이션이 문법적으로 오류가 있는 SQL 쿼리를 데이터베이스에 요청한다면 데이터베이스는 SQL 쿼리를 실행하지 못하고 SQL 쿼리가 왜 틀렸는지 알려주는 오류를 반환해 줍니다.
만약, 개발자가 이것을 HTTP 응답내에 출력하도록 냅뒀다면, 오류는 일반 사용자도 알 수 있게 됩니다.
오류 메시지는 되도록이면 추상적으로 처리해줘야 합니다. 불친절한 정도가 딱 적당합니다.
최종적으로 다음가 같은 대응 방식을 고려해볼 수 있습니다.
stored procedures
를 사용하여 입력 데이터와 SQL 코드를 분리합니다. 여기서 특히 주의할 점은, 1번의 방식으로 쿼리문의 특정 명령어를 공백으로 치환하는 방법을 이용하여 공격을 회피하려고 하지만 이것 또한 취약한 방법임을 알고 있어야 합니다.
SESELECTLECT
를 보낼 경우에 중간에 SELECT가 공백으로 치환되면서 다시 SELECT
가 만들어 지는 것을 확인할 수 있습니다.