เหตุใดเราจึงชอบใช้พารามิเตอร์ในคำสั่ง SQL เสมอ


114

ฉันยังใหม่มากกับการทำงานกับฐานข้อมูล ตอนนี้ฉันสามารถเขียนSELECT, UPDATE, DELETEและINSERTคำสั่ง แต่ฉันเคยเห็นฟอรัมมากมายที่เราชอบเขียน:

SELECT empSalary from employee where salary = @salary

...แทน:

SELECT empSalary from employee where salary = txtSalary.Text

เหตุใดเราจึงชอบใช้พารามิเตอร์อยู่เสมอและฉันจะใช้พารามิเตอร์อย่างไร

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


2
คุณพูดถูกสิ่งนี้เกี่ยวข้องกับการแทรก SQL วิธีจัดการกับพารามิเตอร์มักเป็นความรับผิดชอบของภาษา / กรอบงานที่โปรแกรมของคุณกำลังทำงานอยู่และอาจขึ้นอยู่กับภาษา โปรดโพสต์ทั้ง RDBMS ของคุณ (มีประโยชน์) และ ORM framwork (จำเป็น)
Clockwork-Muse

1
ฉันใช้ C # เป็นภาษาโปรแกรมและ Sql Server 2008 เป็นฐานข้อมูล ฉันใช้ Microsoft dotNet framework 4.0 ฉันขอโทษจริงๆที่ฉันไม่แน่ใจในสิ่งที่คุณถาม (RDBMS หรือ ORM) บางทีคุณอาจให้เวอร์ชันเฟรมเวิร์ก RDBMS และ ORM ของฉันตอนนี้ :-) ขอบคุณมาก
แซนดี้

1
RDBMS คือฐานข้อมูลของคุณในกรณีของคุณ SQL Server 2008 ORM ของคุณคือวิธีการที่คุณเข้าถึงฐานข้อมูลของคุณในกรณีนี้คือ ADO.NET อื่น ๆ includ LINQ กับ SQLและEntity Framework ในความเป็นจริงเมื่อคุณเรียนรู้พื้นฐานของ ADO.NET และ SQL แล้วขอแนะนำให้ใช้ ORM เช่น LINQ หรือ EF เนื่องจากพวกเขาดูแลปัญหาต่างๆที่คุณพบด้วยการเขียน SQL ด้วยตนเอง
Chad Levy

คำตอบ:


129

การใช้พารามิเตอร์ช่วยป้องกันการโจมตี SQL Injectionเมื่อฐานข้อมูลถูกใช้ร่วมกับอินเทอร์เฟซของโปรแกรมเช่นโปรแกรมเดสก์ท็อปหรือเว็บไซต์

ในตัวอย่างของคุณผู้ใช้สามารถเรียกใช้โค้ด SQL บนฐานข้อมูลของคุณได้โดยตรงโดยสร้างคำสั่งในรูปแบบtxtSalary.

ตัวอย่างเช่นหากต้องการเขียน0 OR 1=1SQL ที่เรียกใช้งานจะเป็น

 SELECT empSalary from employee where salary = 0 or 1=1

โดยที่ empSalaries ทั้งหมดจะถูกส่งคืน

นอกจากนี้ผู้ใช้สามารถดำเนินการคำสั่งที่แย่กว่านั้นมากกับฐานข้อมูลของคุณรวมถึงการลบออกหากพวกเขาเขียนว่า0; Drop Table employee:

SELECT empSalary from employee where salary = 0; Drop Table employee

employeeจากนั้นตารางจะถูกลบ


ในกรณีของคุณดูเหมือนว่าคุณกำลังใช้. NET การใช้พารามิเตอร์นั้นง่ายพอ ๆ กับ:

ค#

string sql = "SELECT empSalary from employee where salary = @salary";

using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
    var salaryParam = new SqlParameter("salary", SqlDbType.Money);
    salaryParam.Value = txtMoney.Text;

    command.Parameters.Add(salaryParam);
    var results = command.ExecuteReader();
}

VB.NET

Dim sql As String = "SELECT empSalary from employee where salary = @salary"
Using connection As New SqlConnection("connectionString")
    Using command As New SqlCommand(sql, connection)
        Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
        salaryParam.Value = txtMoney.Text

        command.Parameters.Add(salaryParam)

        Dim results = command.ExecuteReader()
    End Using
End Using

แก้ไข 2016-4-25:

ตามความคิดเห็นของจอร์จ Stocker AddWithValueของผมเปลี่ยนโค้ดตัวอย่างที่จะไม่ใช้ นอกจากนี้โดยทั่วไปขอแนะนำให้คุณรวมคำสั่งIDisposables ไว้usingด้วย


ทางออกที่ดี แต่คุณช่วยอธิบายเพิ่มเติมอีกเล็กน้อยได้ไหมว่าทำไมและการใช้พารามิเตอร์จึงปลอดภัย ฉันหมายความว่าดูเหมือนว่าคำสั่ง sql จะเหมือนเดิม
แซนดี้

เราสามารถเพิ่มพารามิเตอร์หลายตัวในคำสั่ง sql เช่นเดียวกับที่เราต้องการในคำสั่ง INSERT?
Sandy

2
SQL Server ถือว่าข้อความภายในพารามิเตอร์เป็นอินพุตเท่านั้นและจะไม่ดำเนินการ
Chad Levy

3
Insert Into table (Col1, Col2) Values (@Col1, @Col2)ใช่คุณสามารถเพิ่มหลายพารามิเตอร์: ในรหัสของคุณคุณจะต้องเพิ่มหลาย ๆAddWithValueตัว
Chad Levy

1
โปรดอย่าใช้ AddWithValue! อาจทำให้เกิดปัญหาการแปลงโดยปริยาย parameter.Value = someValueมักจะกำหนดขนาดอย่างชัดเจนและเพิ่มค่าพารามิเตอร์ที่มี
George Stocker

75

คุณพูดถูกสิ่งนี้เกี่ยวข้องกับการแทรก SQLซึ่งเป็นช่องโหว่ที่อนุญาตให้ผู้ใช้ malicioius ดำเนินการคำสั่งโดยพลการกับฐานข้อมูลของคุณ การ์ตูน XKCD ที่ชื่นชอบในสมัยก่อนนี้แสดงให้เห็นถึงแนวคิด:

ลูกสาวของเธอชื่อ Help ฉันติดอยู่ในโรงงานผลิตใบขับขี่


ในตัวอย่างของคุณหากคุณใช้เพียง:

var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query

คุณเปิดรับการฉีด SQL ตัวอย่างเช่นพูดว่ามีคนป้อน txtSalary:

1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.

เมื่อคุณดำเนินการค้นหานี้จะมีการดำเนินการSELECTและUPDATEหรือDROPหรือสิ่งที่ต้องการ ที่สิ้นสุดเพียงแค่ความเห็นส่วนที่เหลือของการค้นหาของคุณซึ่งจะเป็นประโยชน์ในการโจมตีว่าคุณกำลังเชื่อมโยงอะไรหลังจาก--txtSalary.Text


วิธีที่ถูกต้องคือการใช้การสืบค้นแบบกำหนดพารามิเตอร์เช่น (C #):

SqlCommand query =  new SqlCommand("SELECT empSalary FROM employee 
                                    WHERE salary = @sal;");
query.Parameters.AddWithValue("@sal", txtSalary.Text);

ด้วยวิธีนี้คุณสามารถดำเนินการค้นหาได้อย่างปลอดภัย

สำหรับการอ้างอิงเกี่ยวกับวิธีการหลีกเลี่ยงการฉีด SQL ในภาษาอื่น ๆ อีกหลายตรวจสอบbobby-tables.comเว็บไซต์ดูแลโดยใช้ SO


1
ทางออกที่ดี แต่คุณช่วยอธิบายเพิ่มเติมอีกเล็กน้อยได้ไหมว่าทำไมและการใช้พารามิเตอร์จึงปลอดภัย ฉันหมายความว่าดูเหมือนว่าคำสั่ง sql จะเหมือนเดิม
Sandy

1
@ user815600: ความเข้าใจผิดทั่วไป - คุณยังคงเชื่อว่าการสืบค้นที่มีพารามิเตอร์จะรับค่าและแทนที่พารามิเตอร์สำหรับค่าจริง - ใช่ไหม ไม่สิ่งนี้จะไม่เกิดขึ้น! - แทนคำสั่ง SQL พร้อมพารามิเตอร์จะถูกส่งไปยัง SQL Server พร้อมกับรายการพารามิเตอร์และค่า - คำสั่ง SQL จะไม่เหมือนกัน
marc_s

1
นั่นหมายความว่าการฉีด sql กำลังถูกตรวจสอบโดยกลไกหรือความปลอดภัยภายในเซิร์ฟเวอร์ sql ขอบคุณ
Sandy

4
ฉันชอบการ์ตูนมากพอ ๆ กันถ้าคุณใช้รหัสของคุณโดยมีสิทธิ์เพียงพอที่จะวางตารางได้
philw

9

นอกเหนือจากคำตอบอื่น ๆ จำเป็นต้องเพิ่มพารามิเตอร์นั้นไม่เพียง แต่ช่วยป้องกันการฉีด sql เท่านั้น แต่ยังสามารถปรับปรุงประสิทธิภาพของการสืบค้นได้อีกด้วย เซิร์ฟเวอร์ SQL แคชแผนการสืบค้นที่กำหนดพารามิเตอร์และนำมาใช้ซ้ำในการดำเนินการสืบค้นซ้ำ หากคุณไม่ได้กำหนดพารามิเตอร์การสืบค้นของคุณเซิร์ฟเวอร์ sql จะรวบรวมแผนใหม่ในแต่ละการสืบค้น (โดยมีการยกเว้นบางส่วน) หากข้อความของแบบสอบถามแตกต่างกัน

ข้อมูลเพิ่มเติมเกี่ยวกับการแคชแผนแบบสอบถาม


1
นี่เป็นเรื่องที่เกี่ยวข้องมากกว่าที่คิด แม้แต่การสืบค้น "ขนาดเล็ก" ก็สามารถดำเนินการได้หลายพันหรือหลายล้านครั้งโดยล้างแคชของแบบสอบถามทั้งหมดได้อย่างมีประสิทธิภาพ
เจมส์

5

สองปีหลังจากไปครั้งแรกฉันกำลังกลับมา ...

ทำไมเราถึงชอบพารามิเตอร์? ฉีด SQL จะเห็นได้ชัดเป็นเหตุผลใหญ่ แต่มันอาจจะเป็นไปได้ว่าเราแอบปรารถนาที่จะได้รับกลับไปยัง SQLเป็นภาษา SQL ในตัวอักษรสตริงเป็นแนวปฏิบัติทางวัฒนธรรมที่แปลกอยู่แล้ว แต่อย่างน้อยคุณสามารถคัดลอกและวางคำขอของคุณลงในสตูดิโอการจัดการได้ SQL สร้างแบบไดนามิกด้วยเงื่อนไขภาษาโฮสต์และโครงสร้างควบคุมเมื่อ SQL มีเงื่อนไขและโครงสร้างการควบคุมเป็นเพียงความป่าเถื่อนระดับ 0 คุณต้องเรียกใช้แอปของคุณในการดีบักหรือด้วยการติดตามเพื่อดูว่า SQL สร้างอะไร

อย่าหยุดอยู่แค่พารามิเตอร์ ไปทุกทางและใช้QueryFirst (ข้อจำกัดความรับผิดชอบ: ที่ฉันเขียน) SQL ของคุณอยู่ในไฟล์. sql. คุณแก้ไขในหน้าต่างตัวแก้ไข TSQL ที่ยอดเยี่ยมพร้อมการตรวจสอบความถูกต้องของไวยากรณ์และ Intellisense สำหรับตารางและคอลัมน์ของคุณ คุณสามารถกำหนดข้อมูลการทดสอบในส่วนความคิดเห็นพิเศษแล้วคลิก "เล่น" เพื่อเรียกใช้การสืบค้นของคุณในหน้าต่าง การสร้างพารามิเตอร์ทำได้ง่ายเพียงแค่ใส่ "@myParam" ใน SQL ของคุณ จากนั้นทุกครั้งที่คุณบันทึก QueryFirst จะสร้าง C # wrapper สำหรับแบบสอบถามของคุณ พารามิเตอร์ของคุณปรากฏขึ้นพิมพ์อย่างรุนแรงเป็นอาร์กิวเมนต์ของเมธอด Execute () ผลลัพธ์ของคุณจะถูกส่งกลับใน IEnumerable หรือ List of POCO ที่พิมพ์อย่างรุนแรงประเภทที่สร้างจากสคีมาจริงที่ส่งคืนโดยข้อความค้นหาของคุณ หากคำค้นหาของคุณไม่ทำงานแอปของคุณจะไม่รวบรวม หากสคีมาฐานข้อมูลของคุณเปลี่ยนแปลงและการสืบค้นของคุณทำงาน แต่บางคอลัมน์หายไปข้อผิดพลาดในการคอมไพล์จะชี้ไปที่บรรทัดในโค้ดของคุณที่พยายามเข้าถึงข้อมูลที่ขาดหายไป และยังมีข้อดีอื่น ๆ อีกมากมาย ทำไมคุณถึงต้องการเข้าถึงข้อมูลด้วยวิธีอื่น


4

ใน Sql เมื่อคำใด ๆ มีเครื่องหมาย @ หมายความว่าเป็นตัวแปรและเราใช้ตัวแปรนี้เพื่อกำหนดค่าและใช้ในพื้นที่ตัวเลขบนสคริปต์ sql เดียวกันเนื่องจากถูก จำกัด ไว้ในสคริปต์เดียวเท่านั้นในขณะที่คุณสามารถประกาศตัวแปรจำนวนมากได้ ประเภทและชื่อเดียวกันในหลายสคริปต์ เราใช้ตัวแปรนี้ในหลายขั้นตอนการเก็บเพราะขั้นตอนการเก็บจะก่อนรวบรวมแบบสอบถามและเราสามารถส่งผ่านค่าในตัวแปรเหล่านี้จากสคริปต์เดสก์ทอปและเว็บไซต์สำหรับข้อมูลเพิ่มเติมอ่านประกาศตัวแปรท้องถิ่น , SQL เก็บขั้นตอนและการฉีด SQL

อ่านProtect from sql injectionด้วยจะแนะนำวิธีปกป้องฐานข้อมูลของคุณ

หวังว่าจะช่วยให้คุณเข้าใจคำถามใด ๆ แสดงความคิดเห็นฉัน


3

คำตอบอื่น ๆ ครอบคลุมว่าเหตุใดพารามิเตอร์จึงสำคัญ แต่มีข้อเสีย! ใน. net มีหลายวิธีในการสร้างพารามิเตอร์ (Add, AddWithValue) แต่ทั้งหมดนี้ทำให้คุณต้องกังวลเกี่ยวกับชื่อพารามิเตอร์โดยไม่จำเป็นและทั้งหมดนี้ลดความสามารถในการอ่านของ SQL ในโค้ด ในขณะที่คุณพยายามทำสมาธิกับ SQL คุณต้องค้นหาด้านบนหรือด้านล่างเพื่อดูว่ามีการใช้ค่าใดในพารามิเตอร์

ฉันอ้างว่าคลาส SqlBuilder เล็ก ๆ ของฉันอย่างนอบน้อมเป็นวิธีที่สง่างามที่สุดในการเขียนแบบสอบถามที่กำหนดพารามิเตอร์วิธีที่สง่างามมากที่สุดในการเขียนคำสั่งแปรรหัสของคุณจะมีลักษณะดังนี้ ...

ค#

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();

รหัสของคุณจะสั้นลงและอ่านง่ายขึ้นมาก คุณไม่จำเป็นต้องมีบรรทัดเพิ่มเติมและเมื่อคุณอ่านย้อนหลังคุณไม่จำเป็นต้องค้นหาค่าของพารามิเตอร์ ชั้นเรียนที่คุณต้องการอยู่ที่นี่ ...

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
    _rq = new StringBuilder();
    _cmd = cmd;
    _seq = 0;
}
public SqlBuilder Append(String str)
{
    _rq.Append(str);
    return this;
}
public SqlBuilder Value(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append(paramName);
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public SqlBuilder FuzzyValue(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append("'%' + " + paramName + " + '%'");
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public override string ToString()
{
    return _rq.ToString();
}
}

การตั้งชื่อพารามิเตอร์ของคุณช่วยได้อย่างแน่นอนเมื่อทำโปรไฟล์แบบสอบถามที่เซิร์ฟเวอร์กำลังทำงานอยู่
Dave R.

เจ้านายของฉันพูดในสิ่งเดียวกัน หากชื่อพารามิเตอร์ที่มีความหมายมีความสำคัญสำหรับคุณให้เพิ่มอาร์กิวเมนต์ paramName ให้กับวิธีการค่า ฉันสงสัยว่าคุณกำลังทำสิ่งต่างๆให้ยุ่งยากโดยไม่จำเป็น
bbsimonbb

ความคิดที่ไม่ดี ตามที่ได้กล่าวไว้ก่อนหน้านี้AddWithValueอาจทำให้เกิดปัญหาการแปลงโดยปริยาย
Adam Calvet Bohl

@Adam คุณพูดถูก แต่นั่นไม่ได้หยุด AddWithValue () จากการใช้งานกันอย่างแพร่หลายและฉันไม่คิดว่ามันจะทำให้ความคิดนั้นไม่ถูกต้อง แต่ในระหว่างนี้ฉันได้พบวิธีที่ดีกว่ามากในการเขียนแบบสอบถามที่กำหนดพารามิเตอร์และนั่นไม่ได้ใช้ AddWithValue () :-)
bbsimonbb

ขวา! สัญญาว่าจะไปดูเร็ว ๆ นี้!
Adam Calvet Bohl

3

โพสต์เก่า แต่ต้องการให้ผู้มาใหม่ทราบถึงขั้นตอนที่จัดเก็บไว้วิธีการจัดเก็บ

ค่า 10 ของฉันที่นี่คือถ้าคุณสามารถเขียนคำสั่ง SQL ของคุณเป็นกระบวนงานที่เก็บไว้นั่นคือวิธีที่ดีที่สุดในมุมมองของฉัน ฉันใช้ procs ที่เก็บไว้เสมอและไม่เคยวนซ้ำบันทึกในรหัสหลักของฉัน ตัวอย่างเช่น: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.

เมื่อคุณใช้โพรซีเดอร์ที่จัดเก็บไว้คุณสามารถ จำกัด ผู้ใช้ให้มีสิทธิ์ดำเนินการเท่านั้นซึ่งจะช่วยลดความเสี่ยงด้านความปลอดภัยลดความเสี่ยงด้านความปลอดภัย

กระบวนงานที่จัดเก็บของคุณเป็นพารามิเตอร์โดยเนื้อแท้และคุณสามารถระบุพารามิเตอร์อินพุตและเอาต์พุตได้

กระบวนงานที่เก็บไว้ (หากส่งคืนข้อมูลผ่านSELECTคำสั่ง) สามารถเข้าถึงและอ่านได้ในลักษณะเดียวกับที่คุณทำตามปกติSELECTคำสั่งในรหัสของคุณ

นอกจากนี้ยังทำงานได้เร็วขึ้นเมื่อคอมไพล์บน SQL Server

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

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