자, 먼저 우리가 뭘 지키는지, 그리고 누구로부터 지키는지 알아보자. 이 레벨 초반에 이미 SQL 인젝션에 대해 언급했었지? 이건 데이터베이스를 노리는 가장 흔하고 파괴적인 공격 중 하나야. 공격 방식은 이래: 해커가 너의 쿼리에 악의적인 SQL 코드를 집어넣어서 쿼리를 "속이고" 원래 접근하면 안 되는 데이터에 접근하려고 해. 이제 좀 더 자세히 알아보자.
SQL 인젝션 예시
예를 들어, 너한테 간단한 사용자 이름과 비밀번호 입력창이 있는 웹앱이 있다고 해보자. 백엔드는 데이터베이스에 사용자가 있는지 확인하려고 SQL 쿼리를 실행해:
SELECT * FROM users WHERE username = 'admin' AND password = 'password123';
이 쿼리는 사용자가 정상적으로 입력할 때는 완벽해 보여. 근데 만약 누군가 이렇게 입력한다면?
- 사용자 이름:
admin' -- - 비밀번호: (이 필드는 비워둬)
결국 쿼리는 이렇게 변해버려:
SELECT * FROM users WHERE username = 'admin' -- AND password = 'password123';
여기서 --는 SQL에서 주석 시작이야. 그 뒤에 오는 건 다 무시돼. 그래서 비밀번호 체크가 그냥 건너뛰어지고, 해커가 관리자 권한으로 들어갈 수 있게 돼!
SQL 인젝션의 잠재적 결과
SQL 인젝션은 진짜 끔찍한 결과를 초래할 수 있어:
- 무단 데이터 접근. 예를 들어, 해커가 사용자 비밀번호 같은 민감한 데이터에 접근할 수 있어.
- 데이터 삭제 또는 변경. 누군가 테이블을 다 지우거나 엉망으로 만들 수도 있지.
- 임의의 SQL 코드 실행. 해커가
DROP DATABASE같은 명령을 실행한다고 상상해봐... 진짜 악몽이지.
근데 우리 겁쟁이 아니잖아! PostgreSQL은 이런 공격을 막을 수 있는 여러 도구를 제공해.
어떻게 방어할까? SQL 인젝션 예방 방법
- 준비된 쿼리(
PREPARE와EXECUTE) 사용하기
준비된 쿼리(prepared statements)는 마치 검증된 레시피처럼 SQL 코드를 안전하게 만들어줘. 한 번 쿼리를 "준비"하고, 그 다음에 데이터를 따로 전달하는 방식이야. 이러면 악의적인 코드 삽입이 불가능해져.
이게 올바른 예시야:
PREPARE로 쿼리를 준비해.
PREPARE user_login (text, text) AS
SELECT *
FROM users
WHERE username = $1 AND password = $2;
여기서 $1과 $2는 나중에 실제 값이 들어갈 자리야.
EXECUTE로 쿼리를 실행해.
EXECUTE user_login('admin', 'password123');
이렇게 하면 PostgreSQL이 자동으로 모든 사용자 데이터를 이스케이프 처리해서 악의적인 SQL 코드 삽입을 막아줘.
이 방식의 장점:
- SQL 인젝션이 불가능해. 파라미터는 그냥 데이터로만 처리되고, SQL 코드 일부가 아니거든.
- 쿼리가 더 안전해지고, 실행 속도도 빨라져. 실행 계획이 캐싱되니까.
- 파라미터화된 쿼리(Parameterized Queries)
이 방법은 특히 Python이나 Java 같은 프로그래밍 언어로 만든 앱에서 많이 써. PREPARE와 EXECUTE를 직접 쓰는 대신, 라이브러리나 ORM이 파라미터 처리를 자동으로 해줘.
Python과 psycopg2 라이브러리 예시:
import psycopg2
connection = psycopg2.connect(
dbname="your_db",
user="your_user",
password="your_password",
host="localhost",
port="5432"
)
cursor = connection.cursor()
# 파라미터화된 쿼리 사용
username = "admin"
password = "password123"
query = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(query, (username, password))
# 네 데이터는 완전히 안전해!
result = cursor.fetchall()
print(result)
여기서 SQL 쿼리의 %s가 파라미터가 들어갈 자리야. psycopg2가 안전하게 데이터 전달을 처리해줘.
- 입력 데이터 검증
사용자 입력 데이터를 전달할 때는, 네가 기대하는 값이 맞는지 꼭 확인해야 해. 예를 들어:
- 텍스트 데이터는 정규식을 써서 금지된 문자가 없는지 체크해.
- 숫자 데이터는 진짜 숫자인지 확인해.
Python 예시:
import re
username = input("사용자 이름을 입력하세요: ")
# 문자, 숫자, 밑줄만 허용
if re.match(r"^\w+$", username):
print("사용자 이름이 올바름")
else:
print("위험한 사용자 이름!")
- 최소 권한 원칙 적용
쿼리를 실행하는 역할(roles)이 꼭 필요한 최소한의 권한만 갖도록 해. 예를 들어, DROP TABLE이나 ALTER TABLE 같은 명령 권한은 필요 없으면 주지 마.
- 의심스러운 행동 로깅하기
PostgreSQL 파라미터로 사용자 활동을 추적할 수 있어:
log_statement = 'all'— 모든 쿼리 로깅.log_connections = on— 데이터베이스 접속 로깅.
이런 설정으로 잠재적으로 위험한 행동을 잡아낼 수 있어.
구현 예시
예시 1: SQL에서 준비된 쿼리 사용
-- 준비된 쿼리 만들기
PREPARE check_credentials (text, text) AS
SELECT * FROM users WHERE username = $1 AND password = $2;
-- 안전한 파라미터로 쿼리 실행
EXECUTE check_credentials('admin', 'password123');
예시 2: Python에서 파라미터화된 쿼리
query = "UPDATE users SET last_login = NOW() WHERE username = %s"
username = "admin"
cursor.execute(query, (username,))
보안 권장 사항
항상 입력 데이터를 검증해. 사용자로부터 온 데이터는 절대 믿지 마.
준비된 쿼리나 파라미터화된 쿼리만 써. 이 방법들이 SQL 인젝션을 막는 최고의 방어선이야.
최소 권한만 가진 역할을 할당해. 만약 해커가 접근하더라도 피해를 최소화할 수 있어.
로깅을 설정하고 로그를 자주 확인해. 데이터베이스에서 무슨 일이 일어나는지 항상 파악하고 있어야 해.
SQL 인젝션은 진짜 무서운 괴물이지만, 준비된 쿼리, 파라미터화된 쿼리, 그리고 좋은 습관만 지키면 네 데이터베이스를 안전하게 지킬 수 있어. 밤에 걱정 없이 잘 수 있을 거야. PostgreSQL이 다 도구를 제공하니까, 자신 있게 해보자!
GO TO FULL VERSION