CodeGym /행동 /SQL SELF /SQL 인젝션 및 기타 공격 방어: PREPARE, EXECUTE, parameterized queri...

SQL 인젝션 및 기타 공격 방어: PREPARE, EXECUTE, parameterized queries

SQL SELF
레벨 48 , 레슨 3
사용 가능

자, 먼저 우리가 뭘 지키는지, 그리고 누구로부터 지키는지 알아보자. 이 레벨 초반에 이미 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 인젝션은 진짜 끔찍한 결과를 초래할 수 있어:

  1. 무단 데이터 접근. 예를 들어, 해커가 사용자 비밀번호 같은 민감한 데이터에 접근할 수 있어.
  2. 데이터 삭제 또는 변경. 누군가 테이블을 다 지우거나 엉망으로 만들 수도 있지.
  3. 임의의 SQL 코드 실행. 해커가 DROP DATABASE 같은 명령을 실행한다고 상상해봐... 진짜 악몽이지.

근데 우리 겁쟁이 아니잖아! PostgreSQL은 이런 공격을 막을 수 있는 여러 도구를 제공해.

어떻게 방어할까? SQL 인젝션 예방 방법

  1. 준비된 쿼리(PREPAREEXECUTE) 사용하기

준비된 쿼리(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 코드 일부가 아니거든.
  • 쿼리가 더 안전해지고, 실행 속도도 빨라져. 실행 계획이 캐싱되니까.
  1. 파라미터화된 쿼리(Parameterized Queries)

이 방법은 특히 Python이나 Java 같은 프로그래밍 언어로 만든 앱에서 많이 써. PREPAREEXECUTE를 직접 쓰는 대신, 라이브러리나 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가 안전하게 데이터 전달을 처리해줘.

  1. 입력 데이터 검증

사용자 입력 데이터를 전달할 때는, 네가 기대하는 값이 맞는지 꼭 확인해야 해. 예를 들어:

  • 텍스트 데이터는 정규식을 써서 금지된 문자가 없는지 체크해.
  • 숫자 데이터는 진짜 숫자인지 확인해.

Python 예시:

import re

username = input("사용자 이름을 입력하세요: ")

# 문자, 숫자, 밑줄만 허용
if re.match(r"^\w+$", username):
    print("사용자 이름이 올바름")
else:
    print("위험한 사용자 이름!")
  1. 최소 권한 원칙 적용

쿼리를 실행하는 역할(roles)이 꼭 필요한 최소한의 권한만 갖도록 해. 예를 들어, DROP TABLE이나 ALTER TABLE 같은 명령 권한은 필요 없으면 주지 마.

  1. 의심스러운 행동 로깅하기

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이 다 도구를 제공하니까, 자신 있게 해보자!

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION