개발 공부 일지/CS

[CS] SQL Injection(SQL 삽입)이란?

dev-hpk 2025. 1. 13. 17:16

SQL Injection 커버 이미지

 

SQL Injection(SQL 삽입)🤔

SQL Injection은 공격자가 애플리케이션의 데이터베이스를 악용하기 위해 SQL 쿼리를 조작하는 보안 취약점입니다.

공격자가 입력 필드나 URL 매개변수를 통해 악의적인 SQL 코드를 삽입하여 데이터베이스의 민감한 정보를 탈취하거나, 데이터를 삭제 및 수정하며, 심지어 시스템을 제어하는 행위로 이어질 수 있습니다.

 

SQL🤔

SQL(Structured Query Language)은 관계형 데이터베이스 시스템에서 자료를 관리 및 처리하기 위해 설계된 언어입니다. SQL은 데이터베이스의 데이터를 조회, 삽입, 수정, 삭제하는 데 사용되며, 관계형 데이터베이스 관리 시스템(RDBMS)에서 핵심적인 역할을 합니다.

 

💥SQL Injection 유형 및 동작 원리

SQL Injection은 다양한 형태로 발생할 수 있으며, 각각 고유한 동작 방식과 피해를 발생시킵니다.

 

1. Error based SQL Injection

가장 일반적인 형태로 사용자 입력을 통해 악의적인 쿼리를 삽입하는 방식입니다. 이 유형의 동작 원리는 사용자가 입력한 값이 그대로 SQL 쿼리에 삽입될 때 발생합니다.

 

SELECT * FROM users WHERE username = 'admin' AND password = 'password';

 

위 쿼리가 아래와 같은 Spring 코드로 작성되었다고 생각해 봅시다👀

String username = request.getParameter("username");
String password = request.getParameter("password");
String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "';";

 

 

공격자는 아래와 같은 방법으로 공격할 수 있습니다.

1️⃣ username 필드에 ' OR 1=1 -- 를 입력합니다. 

2️⃣ 실제 실행되는 쿼리가 다음과 같이 변경됩니다.

3️⃣ SELECT * FROM users WHERE username = ' ' OR 1=1 --AND password = ' ';

  • WHERE절에 있는 싱글 쿼터('')를 닫아주게 되고, OR 1=1로 WHERE절을 참을 만듭니다.
  • --를 이용해 뒤의 모든 쿼리문을 주석 처리합니다.

4️⃣ 결과적으로 SELECT * FROM users를 통해 users 테이블에 있는 모든 user 정보에 대해 접근할 수 있습니다.

 

2. Blind SQL Injection

공격자가 데이터베이스에서 반환된 결과를 직접 확인할 수 없을 때 발생합니다.

데이터 베이스로부터 특정한 값이나 데이터를 전달받지 않고, 단순히 참/거짓을 판단하는 쿼리를 이용하여 서버의 데이터를 추론하는 공격 기법입니다.

 

공격자는 테이블에 특정 컬럼이 존재하는지 확인합니다. 이를 위해 다음과 같은 참/거짓 쿼리를 사용합니다.

1️⃣ 컬럼 존재 여부 확인

SELECT * FROM users WHERE id = 1 AND EXISTS (SELECT column_name FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'username');
  • 결과가 참이면 username 컬럼이 존재함을 의미합니다.
  • 결과가 거짓이면 username 컬럼이 없음을 나타냅니다.

2️⃣ 결과가 거짓이라면 컬럼 이름을 추론하는 과정을 아래와 같이 반복합니다.

SELECT * FROM users WHERE id = 1 AND EXISTS (SELECT column_name FROM information_schema.columns WHERE table_name = 'users' AND column_name LIKE 'a%');
  • LIKE 'a%'는 컬럼 이름이 a로 시작하는지 확인합니다.
  • 공격자는 알파벳을 한 글자씩 변경하며 컬럼 이름 전체를 추론합니다.

3️⃣ 컬럼 이름을 식별했다면, 다음과 같은 쿼리를 사용해 해당 컬럼의 데이터를 추출할 수 있습니다.

SELECT username FROM users WHERE id = 1;

 

3. Union-Based SQL Injection

UNION SQL 연산자를 사용하여 여러 쿼리의 결과를 결합하여 데이터를 추출하는 방식입니다.

UNION 하려는 두 테이블의 컬럼 수와 데이터 형식은 같아야 합니다.

 

SELECT username, password FROM users WHERE id = 1 UNION SELECT version(), database();

 

공격자는 users 테이블에서 uswername과 password 컬럼 데이터를 조회하는 SELECT 구문에 추가 정보를 요청하는 쿼리문을 겹합해 보냅니다. 이 방법을 통해 공격자는 데이터베이스의 추가 정보를 포함한 결과를 획득할 수 있습니다.

 

💣SQL Injection 방어 방법

1. 입력값 검증

모든 사용자 입력값을 철저히 검증하여 허용된 값만 처리되도록 합니다. 

  • /, –, ‘, “, ?, #, (, ), ;, @, =, + 와 같은 특수 기호 필터링
  • DB Query에 동적으로 영향을 주는  union, select 같은 명령어 필터링

2. 최소 권한 액세스 시행

사용자에게 역할에 필요한 만큼의 최소한의 권한만 부여해 SQL Injection 피해를 최소화합니다.

3. Prepared Statement 및 매개변수화된 쿼리 사용

Prepared Statement는 SQL 쿼리의 구조와 값을 분리하여 쿼리를 실행하는 방식입니다. 이를 통해 사용자 입력값이 SQL 코드로 해석되지 않도록 방지합니다.

 

Prepared Statement 코드

동작 방식

  1. SQL 쿼리와 값의 분리: 쿼리 작성 시 ?와 같은 플레이스홀더를 사용하여 입력값이 삽입될 위치를 지정합니다.
  2. 입력값 바인딩: 플레이스홀더에 입력값을 매개변수로 바인딩합니다. 이 과정에서 입력값은 SQL 코드가 아닌 데이터로 처리됩니다.
  3. 쿼리 실행: 바인딩된 값이 포함된 쿼리를 안전하게 실행합니다.