จะใช้ตัวแปรในคำสั่ง SQL ใน Python ได้อย่างไร?


96

โอเคฉันไม่ค่อยมีประสบการณ์ใน Python

ฉันมีรหัส Python ต่อไปนี้:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

var1จำนวนเต็มอยู่ที่ไหนvar2& var3เป็นสตริง

ฉันจะเขียนชื่อตัวแปรโดยไม่ให้ python รวมเป็นส่วนหนึ่งของข้อความค้นหาได้อย่างไร

คำตอบ:


113
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

โปรดทราบว่าพารามิเตอร์ถูกส่งผ่านเป็นทูเพิล

API ฐานข้อมูลทำการหลีกเลี่ยงและอ้างถึงตัวแปรอย่างเหมาะสม ระวังอย่าใช้ตัวดำเนินการจัดรูปแบบสตริง ( %) เนื่องจาก

  1. มันไม่ได้ทำการหลบหนีหรืออ้างใด ๆ
  2. มันมีแนวโน้มที่จะสตริงไม่มีการควบคุมรูปแบบการโจมตีเช่นฉีด SQL

น่าสนใจทำไมมันถึงทำงานกับ vars แยกกันแทนที่จะเป็นในอาร์เรย์ (var1, var2, var3)
Andomar

ตามข้อกำหนด DB API ดูเหมือนว่าอาจเป็นได้ทางใดทางหนึ่ง: python.org/dev/peps/pep-0249
Ayman Hourieh

9
@thekashyap อ่านอีกครั้งอย่างละเอียด %อะไรที่ไม่ปลอดภัยคือใช้ตัวดำเนินการจัดรูปแบบสตริง ในความเป็นจริงฉันพูดเช่นนั้นในคำตอบ
Ayman Hourieh

ฉันไม่ดี .. ฉันนึกภาพ% แทน,สตริงและตัวแปรที่ดีกว่า .. ไม่สามารถยกเลิกการโหวตลงของฉันได้เนื่องจากเหตุผลหลายประการ .. โดยส่วนตัวฉันอยากเห็นคำที่ไม่ปลอดภัย / โจมตี ฯลฯ ที่กล่าวถึงในคำอธิบายที่คุณพูด ไม่ใช้%..
Kashyap

1
@eric คำตอบบอกว่าอย่าใช้ตัว% ดำเนินการเพื่อจัดรูปแบบสตริง สิ่งที่%อยู่ในสตริงถูกใช้cursor.executeโดยตรงและเนื่องจากรู้ว่ากำลังสร้าง SQL จึงสามารถทำอะไรได้มากกว่านี้เพื่อปกป้องคุณ
Mark Ransom

69

การใช้งาน Python DB-API ที่แตกต่างกันได้รับอนุญาตให้ใช้ตัวยึดตำแหน่งที่แตกต่างกันดังนั้นคุณจะต้องค้นหาว่าคุณกำลังใช้ตัวใดอยู่ซึ่งอาจเป็น (เช่นกับ MySQLdb):

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

หรือ (เช่นด้วย sqlite3 จากไลบรารีมาตรฐาน Python):

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))

หรืออื่น ๆ ก็ได้ (หลังจากที่VALUESคุณสามารถมี(:1, :2, :3)หรือ "ตั้งชื่อสไตล์" (:fee, :fie, :fo)หรือ(%(fee)s, %(fie)s, %(fo)s)ตำแหน่งที่คุณส่งคำสั่งแทนที่จะเป็นแผนที่เป็นอาร์กิวเมนต์ที่สองexecute) ตรวจสอบparamstyleค่าคงที่ของสตริงในโมดูล DB API ที่คุณใช้และมองหาพารามิเตอร์ที่http://www.python.org/dev/peps/pep-0249/เพื่อดูว่าสไตล์การส่งผ่านพารามิเตอร์คืออะไร!


เป็นไปได้ที่จะทำสิ่งเดียวกัน แต่ใช้สคริปต์ SQL ภายนอก?
Novitoll

50

หลายวิธี อย่าใช้รหัสที่ชัดเจนที่สุด ( %swith %) ในรหัสจริงมันเปิดให้โจมตีได้

คัดลอกวางที่นี่จาก pydoc ของ sqlite3 :

# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

ตัวอย่างเพิ่มเติมหากคุณต้องการ:

# Multiple values single statement/execution
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', ('RHAT', 'MSO'))
print c.fetchall()
c.execute('SELECT * FROM stocks WHERE symbol IN (?, ?)', ('RHAT', 'MSO'))
print c.fetchall()
# This also works, though ones above are better as a habit as it's inline with syntax of executemany().. but your choice.
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', 'RHAT', 'MSO')
print c.fetchall()
# Insert a single item
c.execute('INSERT INTO stocks VALUES (?,?,?,?,?)', ('2006-03-28', 'BUY', 'IBM', 1000, 45.00))

5
การใช้งาน DB-API บางตัวใช้% s สำหรับตัวแปรโดยเฉพาะอย่างยิ่ง psycopg2 สำหรับ PostgreSQL สิ่งนี้ไม่ต้องสับสน (แม้ว่าจะทำได้ง่าย) ด้วยการใช้% s กับตัวดำเนินการ% สำหรับการแทนที่สตริง ฉันจะดีมากถ้าสำหรับการพกพาเราสามารถกำหนดวิธีมาตรฐานในการระบุพารามิเตอร์ SQL สำหรับ DB-API ได้
ThatAintWorking

26

http://www.amk.ca/python/writing/DB-API.html

โปรดใช้ความระมัดระวังเมื่อคุณเพียงต่อท้ายค่าของตัวแปรในคำสั่งของคุณลองนึกภาพว่าผู้ใช้ตั้งชื่อตัวเอง';DROP TABLE Users;'นั่นเป็นเหตุผลที่คุณต้องใช้การหลีกเลี่ยง sql ซึ่ง Python ให้ไว้สำหรับคุณเมื่อคุณใช้เคอร์เซอร์ดำเนินการในลักษณะที่เหมาะสม ตัวอย่างใน url คือ:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )

13
ที่จริงแล้วมันไม่ใช่การหลบหนีของ SQL เป็นการผูกตัวแปรซึ่งง่ายกว่าและตรงกว่ามาก ค่าจะถูกผูกไว้ในคำสั่ง SQL หลังจากการแยกวิเคราะห์ทำให้ไม่มีภูมิคุ้มกันต่อการโจมตีด้วยการฉีด
ล็อตต์

ไม่ว่าจะเป็นการหลีกเลี่ยง SQL หรือการผูกตัวแปรขึ้นอยู่กับว่าเซิร์ฟเวอร์ฐานข้อมูล / ไดรเวอร์ DB-API ของคุณดีหรือไม่ดีเพียงใด ฉันเคยเห็นฐานข้อมูลการผลิตในโลกแห่งความเป็นจริงที่มีการใช้งานอย่างกว้างขวางซึ่งมีไดรเวอร์ DB-API เพียงแค่หลบหนีแทนที่จะเก็บข้อมูลและรหัสไว้นอกวง จำเป็นต้องพูดว่าฉันไม่ได้เคารพสิ่งที่เรียกว่า "ฐานข้อมูล" มากนัก
Charles Duffy

0

ไวยากรณ์สำหรับการระบุค่าเดียวอาจสร้างความสับสนให้กับผู้ใช้ Python ที่ไม่มีประสบการณ์

รับแบบสอบถาม

INSERT INTO mytable (fruit) VALUES (%s)

ค่าที่ส่งไปcursor.executeต้องยังคงเป็นtupleแม้ว่ามันจะเป็นเดี่ยวดังนั้นเราจึงต้องให้ tuple (value,)องค์ประกอบเดียวเช่นนี้

cursor.execute("""INSERT INTO mytable (fruit) VALUES (%s)""", ('apple',))

0

ในขณะเดียวกันก็มีอีกวิธีหนึ่งในการใช้f-strings :

cursor.execute(f"INSERT INTO table VALUES {var1}, {var2}, {var3},")

อย่าทำเช่นนี้มันจะปล่อยให้แอปพลิเคชันเปิดสำหรับการโจมตีด้วยการฉีด SQL
snakecharmerb

ฉันคิดว่ามันคุ้มค่าที่จะพูดถึงมัน แน่นอนฉันกำลังเขียนคำตอบนี้โดยสมมติว่ามีการตรวจสอบตัวแปรก่อน
Patrik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.