사이버보안
SQL Injection
JeongBear
2024. 12. 14. 22:20
SQL 인젝션(SQL Injection)은 공격자가 웹 애플리케이션의 입력 필드를 악용하여 SQL 쿼리를 조작함으로써 데이터베이스에 비정상적인 접근을 시도하는 공격 기법입니다. 이 공격은 데이터 유출, 삭제, 수정, 또는 관리자 권한 탈취 등의 심각한 결과를 초래할 수 있습니다.
취약점: 애플리케이션에서 사용자 입력을 적절히 검증하거나, 쿼리에 직접 삽입할 때 이를 처리하지 않을 경우 발생합니다.
취약한 쿼리:
SELECT * FROM users WHERE username = '$username' AND password = '$password';
username에 ' OR '1'='1을 입력하면 쿼리가 다음과 같이 변조:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';
결과: 조건이 항상 참이 되어 데이터베이스의 모든 사용자 정보를 반환합니다.
실습 코드:
# SQL 인젝션 취약 코드
import sqlite3
# 데이터베이스 생성 및 초기화
def initialize_database():
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, password TEXT)")
cursor.execute("INSERT INTO users (username, password) VALUES ('admin', 'admin123')")
cursor.execute("INSERT INTO users (username, password) VALUES ('user', 'user123')")
conn.commit()
return conn
# 취약한 로그인 함수
def vulnerable_login(conn, username, password):
cursor = conn.cursor()
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
print(f"실행된 쿼리: {query}")
cursor.execute(query)
result = cursor.fetchone()
return result
# SQL 인젝션 테스트
if __name__ == "__main__":
conn = initialize_database()
# 정상 입력
print("=== 정상 로그인 시도 ===")
user = vulnerable_login(conn, "admin", "admin123")
print("로그인 성공" if user else "로그인 실패")
# SQL 인젝션 공격
print("\n=== SQL 인젝션 공격 시도 ===")
user = vulnerable_login(conn, "' OR '1'='1", "any_password")
print("로그인 성공" if user else "로그인 실패")
SQL Injection을 막는 방법
- Prepared Statement 사용: 쿼리를 실행할 때, SQL과 데이터를 분리
- 입력값 검증: 사용자의 입력 데이터를 검증
- 최소 권한 접근: 데이터베이스 사용자의 권한을 최소화
- ORM 사용: SQLAlchemy 같은 ORM을 통해 쿼리를 작성
- 보안 모니터링: 의심스러운 활동을 감지하고 차단
- WAF 사용: 웹 방화벽을 사용하여 방어
- etc..
위의 실습 코드의 경우, Prepared Statement 또는 Parameterized Query를 사용하여 막으면
방어 코드:
# SQL 인젝션 방어 코드
def secure_login(conn, username, password):
cursor = conn.cursor()
query = "SELECT * FROM users WHERE username = ? AND password = ?"
print(f"실행된 쿼리: {query} | 파라미터: ({username}, {password})")
cursor.execute(query, (username, password))
result = cursor.fetchone()
return result
# 방어된 코드 테스트
if __name__ == "__main__":
conn = initialize_database()
# 정상 입력
print("=== 정상 로그인 시도 ===")
user = secure_login(conn, "admin", "admin123")
print("로그인 성공" if user else "로그인 실패")
# SQL 인젝션 공격
print("\n=== SQL 인젝션 공격 시도 ===")
user = secure_login(conn, "' OR '1'='1", "any_password")
print("로그인 성공" if user else "로그인 실패")