パラメータ化クエリ
SQLインジェクションを防ぎます
「パラメータ化クエリ」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応の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()複数のプレースホルダー
値ごとに?を1つ使用します。クエリ内に登場する順番と同じ順番で、値をタプルにして渡します。
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()1要素タプル
要素が1つのタプルには末尾のカンマが必要です。('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()再利用可能なINSERT関数
パラメーター化したINSERTを関数にまとめると、コードがすっきりし、安全な方法を一貫して使えるようになります。
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に連結してはいけません
- 値のタプルと
?プレースホルダーを使用します - 名前付きの
:nameプレースホルダーには辞書を渡します executemany()を使うと、多数の行を安全に挿入できます- プレースホルダーは値用であり、識別子には使えません
よくある質問
「パラメータ化クエリ」レッスンは無料ですか?
はい。「パラメータ化クエリ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。
「パラメータ化クエリ」で何を学びますか?
SQLインジェクションを防ぎます ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Python Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「パラメータ化クエリ」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このPython Academyレッスンでコードを書いて実行できますか?
はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。