SQL Injection은 데이터베이스와 상호작용하는 애플리케이션에서 사용자가 입력한 값이 의도하지 않은 SQL쿼리를 실행하게 하는 보안취약점입니다. 이로 인해 데이터 유출, 데이터베이스 손상, 인증 우회 등의 문제가 발생할 수 있습니다. 본 글에서는 SQL Injection 공격의 원리를 설명하고 이를 방지하기 위한 주요 기법들을 자세히 살펴보겠습니다.
1. SQL Injection의 원리
SQL Injection은 사용자 입력값이 쿼리의 일부분으로 포함되어, 의도하지 않은 SQL명령이 실행될 때 발생합니다.
SELECT * FROM users WHERE username = 'admin' AND password = 'password';
위와 같은 쿼리를 처리하는 애플리케이션에 아래와 같은 입력이 주어졌다고 가정합니다.
- username : admin
- Password : ‘ OR ‘1’ =‘1
결과적으로 생성된 SQL 쿼리는 다음과 같습니다.
SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1';
‘1’ = ‘1’은 항상 참이므로 데이터베이스는 모든 사용자 정보를 반환하거나 인증을 우회하게 됩니다.
2. SQL Injection 방지 기법
SQL Injection을 방지하려면 아래와 같은 보안 기법을 사용해야 합니다.
(1) Prepared Statement 사용
Prepared Statement는 쿼리와 데이터를 분리하여 동작합니다. SQL쿼리를 미리 컴파일한 후 데이터를 변수로 전달받기 때문에 SQL Injection 공격을 방지할 수 있습니다. 아래는 Prepared Statement의 예시입니다
Java 예시 (JDBC)
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, username);
statement.setString(2, password);
ResultSet resultSet = statement.executeQuery();
Python 예시 (Psycopg2)
sql = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(sql, (username, password))
(2) ORM(Object-Relational Mapping) 사용
ORM은 SQL 쿼리를 직접 작성하지 않고 객체지향 프로그래밍 방식으로 데이터베이스와 상호작용할 수 있게 해줍니다. 대표적인 ORM도구로는 django ORM, SQLAlchemy, Hibernate 등이 있습니다.
Python 예시 (Django ORM)
user = User.objects.filter(username=username, password=password).first()
Java 예시 (Hibernate)
Query query = session.createQuery("FROM User WHERE username = :username AND password = :password");
query.setParameter("username", username);
query.setParameter("password", password);
List<User> users = query.list();
ORM은 기본적으로 Prepared Statement를 활용하므로 SQL Injection에 강합니다.
(3) 입력값 검증 및 인코딩
사용자 입력값을 철저히 검증하고 SQL 쿼리에 사용되기 전에 인코딩을 적용합니다.
- Whitelist Validation : 허용된 값만 입력으로 받을 수 있도록 설정
- Blacklist Validation : SQL 명령어나 특수 문자를 입력값에서 제거
Python 예시 (Validation)
import re
def is_valid_username(username):
return re.match("^[a-zA-Z0-9_]+$", username) is not None
PHP 예시 (Escaping)
$username = mysqli_real_escape_string($connection, $_POST['username']);
(4) 권한 분리
데이터베이스 사용자 계정의 권한을 최소화하여 쿼리가 실행될 수 있는 범위를 제한합니다.
- 읽기 전용 계정 : 데이터 조회만 가능한 계정
- 쓰기 전용 계정 : 데이터 삽입, 수정, 삭제만 가능한 계정
MySql 예시
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE ON my_database.* TO 'app_user'@'localhost';
(5) Stored Procedure 사용
Stored Procedure를 활용하면 SQL 쿼리를 미리 정의하고 외부 입력값을 변수로 처리할 수 있습니다.
MySQL Stored Procedure 예시
DELIMITER $$
CREATE PROCEDURE GetUser(IN username VARCHAR(50), IN password VARCHAR(50))
BEGIN
SELECT * FROM users WHERE username = username AND password = password;
END$$
DELIMITER ;
호출 예시
CALL GetUser('admin', 'password');
(6) SQL 에러 메시지 숨기기
공격자가 시스템 내부 정보를 파악하지 못하도록 에러 메시지를 사용자에게 노출하지 않도록 설정합니다.
(7) 웹 애플리케이션 방화벽(WAF) 사용
WAF는 SQL Injection 공격 패턴을 자동으로 탐지하고 차단합니다. 대표적인 WAF 솔루션은 ModSecurity, AWS WAF등이 있습니다.
3. SQL Injection 방지 모범 사례
- 항상 Prepared Statement 또는 ORM을 사용
- 입력값을 검증하고 인코딩 처리
- 데이터베이스 사용자 계정에 최소 권한만 부여
- Stored Procedure 활용
- 에러 메시지를 숨기고, 공격자에게 시스템 정보를 노출하지 않음
- WAF를 도입하여 실시간으로 공격을 탐지하고 방어
4. 결론
SQL Injection은 잘 알려진 공격 방식이지만, 적절한 보안 기법을 적용하면 충분히 방지할 수 있습니다. 특히 Prepared Statement와 ORM은 가장 효과적이고 간단한 방어 방법 중 하나입니다. 데이터 보안을 강화하기 위해 글에서 다룬 방지 기법들을 프로젝트에 적용하시길 권장드립니다.
출처 및 라이선스
이글은 공개된 보안 모범 사례와 기술 문서를 바탕으로 작성하였습니다. Creative Commons Attribution 4.0 International (CC BY 4.0) 라이선스를 따릅니다.
'프로그래밍' 카테고리의 다른 글
모바일 앱의 배터리 사용 최적화 방법 (1) | 2024.12.27 |
---|---|
쿠키, 세션, JWT의 차이점 (0) | 2024.12.25 |
객체 지향 프로그래밍(OOP)과 설계 원칙 (0) | 2024.12.24 |
ChatGPT API를 활용한 챗봇 개발 (개요와 가이드) (1) | 2024.12.23 |
TensorFlow와 PyTorch의 차이점 비교: 무엇을 선택해야 할까? (1) | 2024.12.22 |