매개변수화된 쿼리
SQL 인젝션 방지하기
매개변수화된 쿼리은(는) CoddyKit의 무료 Python Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Python Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
문자열 조합의 위험성
문자열을 연결해 SQL을 만드는 것은 위험합니다. 사용자 입력에 SQL이 포함되어 있으면 쿼리가 변경될 수 있습니다. 이를 SQL 주입이라고 합니다.
신뢰할 수 없는 데이터에는 절대 이 방법을 사용하지 마세요.
name = 'Alice'
# Unsafe pattern - do NOT do this
query = "SELECT * FROM users WHERE name = '" + name + "'"
print(query)주입 공격의 모습
악의적인 값을 입력한다고 가정해 보세요. 그 값을 연결하면 쿼리의 의미가 완전히 달라집니다.
name = "x' OR '1'='1"
query = "SELECT * FROM users WHERE name = '" + name + "'"
print(query)
print('The OR clause makes every row match!')자리 표시자로 안전하게 처리하기
안전한 방법은 매개변수화된 쿼리를 사용하는 것입니다. 자리 표시자로 ?를 사용하고 값을 튜플로 전달합니다. 데이터베이스 드라이버가 값을 안전하게 이스케이프합니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
cur.execute('INSERT INTO users (name) VALUES (?)', ('Alice',))
cur.execute('SELECT * FROM users')
print(cur.fetchall())
conn.close()여러 자리 표시자 사용하기
값마다 ?를 하나씩 사용합니다. 쿼리에 나타나는 순서와 같은 순서로 튜플에 담아 전달합니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)')
cur.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Bob', 25))
cur.execute('SELECT * FROM users')
print(cur.fetchall())
conn.close()단일 값 튜플
요소가 하나인 튜플에는 끝에 쉼표가 필요합니다: ('Alice',). 쉼표가 없으면 Python은 괄호를 단순한 그룹화로 처리합니다.
good = ('Alice',)
bad = ('Alice')
print(type(good), type(bad))주입 시도 차단하기
자리 표시자를 사용하면 악의적인 값도 SQL이 아니라 일반 데이터로 처리됩니다. 따라서 단순히 일치하는 결과가 나오지 않습니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
cur.execute('INSERT INTO users (name) VALUES (?)', ('Alice',))
evil = "x' OR '1'='1"
cur.execute('SELECT * FROM users WHERE name = ?', (evil,))
print('Rows matched:', cur.fetchall())
conn.close()이름이 있는 자리 표시자
SQLite는 :name 구문을 사용하는 이름이 있는 자리 표시자도 지원합니다. 값의 딕셔너리를 전달합니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)')
cur.execute('INSERT INTO users (name, age) VALUES (:n, :a)', {'n': 'Carol', 'a': 40})
cur.execute('SELECT * FROM users')
print(cur.fetchall())
conn.close()WHERE에서 매개변수 사용하기
자리 표시자는 WHERE를 포함해 값을 받는 모든 절에서 사용할 수 있습니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)')
cur.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Alice', 30))
cur.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Bob', 25))
cur.execute('SELECT name FROM users WHERE age > ?', (28,))
print(cur.fetchall())
conn.close()executemany
여러 행을 삽입하려면 튜플의 목록과 함께 executemany()를 사용합니다. 더 빠르면서도 안전합니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
rows = [('Alice',), ('Bob',), ('Carol',)]
cur.executemany('INSERT INTO users (name) VALUES (?)', rows)
cur.execute('SELECT COUNT(*) FROM users')
print(cur.fetchone())
conn.close()식별자에는 자리 표시자를 사용할 수 없습니다
자리 표시자는 값에만 사용할 수 있으며, 테이블 이름이나 열 이름에는 사용할 수 없습니다. 이러한 이름은 직접 작성한 코드에서 신뢰할 수 있는 허용 목록으로 관리해야 합니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
allowed = {'name', 'id'}
col = 'name'
if col in allowed:
cur.execute('SELECT ' + col + ' FROM users')
print('Safe column query ran')
conn.close()재사용 가능한 삽입 함수
매개변수화된 삽입 작업을 함수로 감싸면 코드가 깔끔해지고 일관되게 안전성을 유지할 수 있습니다.
import sqlite3
conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
def add_user(cursor, name):
cursor.execute('INSERT INTO users (name) VALUES (?)', (name,))
add_user(cur, 'Dave')
cur.execute('SELECT * FROM users')
print(cur.fetchall())
conn.close()빠른 확인
안전한 쿼리에 대한 지식을 확인해 보세요.
복습
SQL 주입에 안전한 쿼리를 작성하는 방법을 배웠습니다.
- 신뢰할 수 없는 입력을 SQL에 절대 연결하지 않습니다
- 값의 튜플과 함께
?자리 표시자를 사용합니다 - 이름이 있는
:name자리 표시자에는 딕셔너리를 전달합니다 executemany()는 여러 행을 안전하게 삽입합니다- 자리 표시자는 식별자가 아니라 값에 사용합니다
자주 묻는 질문
“매개변수화된 쿼리” 강의는 무료인가요?
네 — “매개변수화된 쿼리” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Python Academy 강의 전체를 잠금 해제할 수 있습니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“매개변수화된 쿼리”에서 뭘 배우나요?
SQL 인젝션 방지하기 브라우저에서 직접 실행하는 실습 코드로 Python Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Python Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Python Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“매개변수화된 쿼리” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Python Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Python Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.