ปลอดภัยไหมที่จะไม่กำหนดพารามิเตอร์แบบสอบถาม SQL เมื่อพารามิเตอร์ไม่ใช่สตริง


113

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

ตัวอย่างเช่นฉันไม่คิดว่าตัวเองอยู่ใกล้ผู้เชี่ยวชาญใน SQL แต่ฉันไม่สามารถนึกถึงกรณีใด ๆ ที่อาจเสี่ยงต่อการฉีด SQL เพื่อยอมรับboolหรือintและเพียงแค่เชื่อมต่อเข้ากับแบบสอบถามโดยตรง

สมมติฐานของฉันถูกต้องหรืออาจทำให้เกิดช่องโหว่ด้านความปลอดภัยขนาดใหญ่ในโปรแกรมของฉัน?

เพื่อความกระจ่างคำถามนี้ถูกแท็ก ซึ่งเป็นภาษาที่พิมพ์ยาก เมื่อผมบอกว่า "พารามิเตอร์" public int Query(int id)คิดว่าสิ่งที่ต้องการ


14
คุณจะไม่ได้รับประโยชน์จากแผนการสืบค้นที่แคชไว้หากไม่ใช้พารามิเตอร์คุณจะต้องจัดทำแผนการสืบค้นแยกต่างหากสำหรับชุดค่าผสมใหม่ทุกรายการที่คุณระบุ
Scott Chamberlain

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

7
แบบสอบถามที่กำหนดพารามิเตอร์ส่วนใหญ่จะใช้สำหรับประสิทธิภาพและการเพิ่มประสิทธิภาพ การป้องกันการฉีด SQL เป็นผลข้างเคียง
Salman A

13
ฉันคิดว่า OP ได้ถามคำถามที่ถูกต้อง เขาพยายามประเมินต้นทุน / ประโยชน์ของการแก้ไขความเสี่ยงที่อาจเกิดขึ้น สมการนั้นเปลี่ยนแปลงไปตามศักยภาพของความเสี่ยงนั้น หากไม่มีความเสี่ยงฉันก็จะไม่ทำเช่นนั้น เขาถามคำถามทางเทคนิคเกี่ยวกับศักยภาพไม่ใช่เพื่อการตัดสินโดยอัตวิสัยว่าคุณคิดว่าคุ้มค่ากับเวลาของเขาหรือไม่ OP เป็นผู้เดียวที่สามารถโทรออกได้
Sir Swears-a-lot

10
เพื่ออธิบายตัวเอง: ฉันเป็น dba ขอขอบคุณและเคารพแนวทางปฏิบัติที่ดีที่สุดและในโลกที่สมบูรณ์แบบรหัสทั้งหมดจะสมบูรณ์แบบ น่าเศร้าในโลกที่ฉันทำงานอยู่ฉันมีปัญหาที่ต้องแก้ไขมากกว่าที่จะมีเวลาแก้ไขนั่นหมายถึงการจัดลำดับความสำคัญ การเขียนโค้ด IMO ที่ใช้งานได้แล้วมีความปลอดภัยและดำเนินการในระดับที่ยอมรับได้ดูเหมือนจะหรูหรา (ที่ฉันไม่สามารถจ่ายได้)
Sir Swears-a-lot

คำตอบ:


101

ฉันคิดว่ามันปลอดภัย ... ในทางเทคนิคแต่มันเป็นนิสัยที่แย่มากที่จะเข้ามา คุณต้องการเขียนข้อความค้นหาเช่นนี้หรือไม่?

var sqlCommand = new SqlCommand("SELECT * FROM People WHERE IsAlive = " + isAlive + 
" AND FirstName = @firstName");

sqlCommand.Parameters.AddWithValue("firstName", "Rob");

นอกจากนี้ยังทำให้คุณเสี่ยงในสถานการณ์ที่ประเภทเปลี่ยนจากจำนวนเต็มเป็นสตริง (คิดว่าหมายเลขพนักงานซึ่งแม้จะมีชื่อ - อาจมีตัวอักษร)

ดังนั้นเราจึงเปลี่ยนประเภทของ EmployeeNumber จากintเป็นstringแต่ลืมอัปเดตคำค้นหา sql ของเรา อุ่ย


24
เราสามารถหยุดใช้AddWithValue แล้วได้หรือไม่? blogs.msmvps.com/jcoehoorn/blog/2014/05/12/…
RemarkLima

5
@RemarkLima อะไรคือวิธีแก้ปัญหาเมื่อคุณสร้างโค้ดแบบไดนามิกซึ่งจับคู่ค่ากับพารามิเตอร์ บล็อกโพสต์ไม่สามารถจัดการกับสถานการณ์นั้นได้ ใช่บรรทัดเดียวในการตั้งค่าประเภท SQL เมื่อทราบแต่เมื่อไม่เป็นเช่นนั้นแสดงว่าคุณมีปัญหา (หรือคุณต้องใช้การใส่คำอธิบายประกอบโมเดลด้วยข้อมูลนั้น)
casperOne

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

8
@RemarkLima ประเด็นคือ "เลิกใช้AddWithValueแล้วได้มั้ย" มันควรจะเป็น "ถ้าคุณทราบชนิดแล้วคุณควรละเว้นจากการใช้AddWithValue.
casperOne

2
เฮ้อย่ายิง Messenger ฉันไม่ได้เขียนมัน ;-) แต่ประเด็นยังคงอยู่และหากสร้างไว้ในการออกแบบของคุณตั้งแต่เริ่มต้นก็ไม่มีเหตุผลที่คุณไม่ควรรู้ประเภท แนวทางปฏิบัติที่ดีที่สุดและแจ๊สทั้งหมด :-)
RemarkLima

65

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

แต่ส่วนที่ "บนคอมพิวเตอร์ที่คุณควบคุม" นั้นมีความสำคัญเพราะมิฉะนั้นผู้ใช้สามารถเปลี่ยนพฤติกรรมที่ระบบใช้ในการสร้างสตริงจากค่าเหล่านั้นเพื่อรวมข้อความที่กำหนดเองได้

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

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

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

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


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

2
ฉันคิดว่าคำถาม "มันเป็นบั๊ก" คือคำถามของฉัน
johnnyRose

7
มันคือ "กลิ่น": สิ่งที่ขาดจากจุดบกพร่องด้วยตัวมันเอง แต่บ่งชี้ว่าน่าจะมีแมลงอยู่ใกล้ ๆ รหัสที่ดีพยายามกำจัดกลิ่น เครื่องมือวิเคราะห์แบบคงที่ที่ดีจะตั้งค่าสถานะไว้อย่างแน่นอน
Joel Coehoorn

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

1
แค่นี้ก็ผิดแล้ว ดูคำตอบของฉันเกี่ยวกับวิธีที่คุณยังสามารถสร้างการแทรก SQL ด้วยDateTimeหรือint
Kaspars Ozols

53

ในบางกรณีเป็นไปได้ที่จะทำการโจมตีด้วยการฉีด SQL ด้วยตัวแปรที่ไม่ใช่พารามิเตอร์ (ต่อกัน) นอกเหนือจากค่าสตริง - ดูบทความนี้โดย Jon: http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables - วัฒนธรรม / .

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


14
ฉันคิดว่านี่เป็นโพสต์เดียวที่ตอบคำถามซึ่งโดยพื้นฐานแล้วคือ“ การฉีดยาจะเป็นไปได้intอย่างไรกับs?”
Arturo Torres Sánchez

3
แม้ว่าคุณจะอยู่ในตำแหน่งที่จะฉีดโค้ดที่กำหนดเองเช่น booby กับดักCultureInfoยากที่จะรู้ว่าทำไมคุณถึงต้องฉีด SQL อยู่ดี
Martin Smith

บวก 1 คำตอบเดียวที่จริงตอบคำถาม
ken2k

@MartinSmith: ดูคำตอบของฉันซึ่งแสดงให้เห็นวิธีหนึ่งที่เป็นไปได้ในการเปลี่ยน CultureInfo จากภายนอก
user1027167

หากบุคคลสามารถเขียนโค้ดดังกล่าวในแอปพลิเคชันทำไมเขาถึงต้องใช้ SQL Injection
Reza Aghaei

51

สิ่งนี้ไม่ปลอดภัยแม้ว่าจะเป็นประเภทที่ไม่ใช่สตริงก็ตาม มักจะใช้พารามิเตอร์ ระยะเวลา

พิจารณาตัวอย่างโค้ดต่อไปนี้:

var utcNow = DateTime.UtcNow;
var sqlCommand = new SqlCommand("SELECT * FROM People WHERE created_on <= '" + utcNow + "'");

ในตอนแรกรหัสดูปลอดภัย แต่ทุกอย่างจะเปลี่ยนไปหากคุณทำการเปลี่ยนแปลงบางอย่างในการตั้งค่าภูมิภาคของ Windows และเพิ่มการแทรกในรูปแบบวันที่สั้น ๆ :

Datetime Injection

ตอนนี้ข้อความคำสั่งที่ได้จะมีลักษณะดังนี้:

SELECT * FROM People WHERE created_on <= '26.09.2015' OR '1'<>' 21:21:43'

สามารถทำได้เช่นเดียวกันสำหรับinttype เนื่องจากผู้ใช้สามารถกำหนดเครื่องหมายลบที่กำหนดเองซึ่งสามารถเปลี่ยนเป็น SQL injection ได้อย่างง่ายดาย

หนึ่งได้ยืนยันว่าวัฒนธรรมคงที่ควรจะนำมาใช้แทนของวัฒนธรรมในปัจจุบัน แต่ฉันได้เห็น concatenations สตริงเช่นนี้หลายครั้งและมันค่อนข้างง่ายที่จะพลาดเมื่อ concatenating +สตริงกับวัตถุที่ใช้


10
ใครสามารถเปลี่ยนการตั้งค่าเซิร์ฟเวอร์ได้บ้าง? หากบุคคลสามารถทำได้ในเซิร์ฟเวอร์ของคุณเขาไม่จำเป็นต้องใช้ SQL Injection เพื่อทำลายข้อมูล
Reza Aghaei

5
นี่เป็นคำตอบที่ดีที่สุดซึ่งแสดงให้เห็นวิธีหนึ่งที่ตรวจสอบความถูกต้องของ OPs ว่านี่เป็นข้อบกพร่อง / ข้อบกพร่องด้านความปลอดภัย ผลการดำเนินงานในอนาคตและการตรวจสอบ datetimes กันเชื่อมโยงใน SQL เช่นไม่ได้เป็นเพียงกลิ่นหรือเทคโนโลยีหนี้ @RezaAghaei คำถามที่ไม่เคยกล่าวถึงฝั่งเซิร์ฟเวอร์อาจเป็นแอป Windows ที่มี SQLExpress - ไม่ว่าจะเป็นวิธีใดก็ตามที่ไม่ใช่เกณฑ์สำหรับคำถาม ทุกคนสามารถพูดได้ แต่ผู้ที่สามารถเข้าถึงการตั้งค่าเซิร์ฟเวอร์เพื่อตอบกลับคำตอบที่ยอดเยี่ยมนี้เช่นเดียวกับที่ใคร ๆ ก็พูดได้ว่าเกี่ยวกับการโฮสต์เซิร์ฟเวอร์ที่ใช้ร่วมกันหรือข้อบกพร่องของ Y2K ฉันเห็นด้วยกับคุณว่าเซิร์ฟเวอร์ถูกล็อค - ไม่ใช่แค่เงื่อนไขเบื้องต้น
Jeremy Thompson

2
คุณช่วยยกตัวอย่างสิ่งที่คุณพูดเกี่ยวกับintประเภทนี้ได้ไหม
johnnyRose

1
ฉันรู้ว่าคุณตอบคำถามนี้มา 2-3 สัปดาห์แล้ว แต่คุณจะแก้ไขโพสต์และเพิ่มตัวอย่างวิธีกำหนดเครื่องหมายลบที่กำหนดเองได้หรือไม่
johnnyRose

23

"SELECT * FROM Table1 WHERE Id =" + intVariable ToString ()


ความปลอดภัย
ใช้ได้
ผู้โจมตีไม่สามารถฉีดอะไรเข้าไปในตัวแปร int ที่คุณพิมพ์ได้

ผลการดำเนินงาน
ไม่ได้ตกลง

การใช้พารามิเตอร์จะดีกว่าดังนั้นแบบสอบถามจะถูกรวบรวมหนึ่งครั้งและแคชไว้สำหรับการใช้งานครั้งต่อไป ครั้งต่อไปแม้จะมีค่าพารามิเตอร์ต่างกันคิวรีก็จะถูกแคชและไม่จำเป็นต้องคอมไพล์ในเซิร์ฟเวอร์ฐานข้อมูล

รูปแบบการเข้ารหัส
การปฏิบัติที่ไม่ดี

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


"SELECT * FROM Product WHERE Id =" + TextBox1.Text


แม้ว่าจะไม่ใช่คำถามของคุณ แต่อาจมีประโยชน์สำหรับผู้อ่านในอนาคต:


หายนะความปลอดภัย !
แม้ว่าIdฟิลด์จะเป็นจำนวนเต็ม แต่แบบสอบถามของคุณอาจอยู่ภายใต้การฉีด SQL สมมติว่าคุณมีข้อความค้นหาในแอปพลิเคชันของคุณ"SELECT * FROM Table1 WHERE Id=" + TextBox1.Textผู้โจมตีสามารถแทรกลงในกล่องข้อความ1; DELETE Table1และแบบสอบถามจะเป็น:

"SELECT * FROM Table1 WHERE Id=1; DELETE Table1"

หากคุณไม่ต้องการใช้การสืบค้นแบบพารามิเตอร์ที่นี่คุณควรใช้ค่าที่พิมพ์:

string.Format("SELECT * FROM Table1 WHERE Id={0}", int.Parse(TextBox1.Text))


คำถามของคุณ


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

ฉันคิดว่าการเปลี่ยนรหัสเหล่านั้นไม่เสียเวลา แนะนำให้เปลี่ยนแน่นอน!

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


แม้แต่ตัวเลือกแรกก็ไม่เป็นไรสำหรับความปลอดภัย ลักษณะการทำงานของ.ToString()ถูกกำหนดโดยรายการคอนฟิกูเรชันระบบปฏิบัติการที่ง่ายต่อการเปลี่ยนแปลงเพื่อรวมข้อความที่กำหนด
Joel Coehoorn

18

มีสองคำถามในหนึ่งเดียว และคำถามจากชื่อเรื่องมีส่วนเกี่ยวข้องน้อยมากกับข้อกังวลของ OP ในความคิดเห็นหลังจากนั้น

แม้ว่าฉันจะตระหนักดีว่าสำหรับ OP นั้นเป็นกรณีเฉพาะของพวกเขาที่มีความสำคัญสำหรับผู้อ่านที่มาจาก Google สิ่งสำคัญคือต้องตอบคำถามทั่วไปที่สามารถใช้วลีได้ว่า "เป็นการเรียงต่อกันที่ปลอดภัยเท่ากับข้อความที่เตรียมไว้หากฉันแน่ใจ ว่าตัวอักษรทุกตัวที่ฉันเชื่อมต่อกันนั้นปลอดภัยหรือไม่ ". ดังนั้นฉันขอให้ความสำคัญกับเรื่องหลังนี้ และคำตอบคือ

ไม่แน่นอน

คำอธิบายไม่ตรงตามที่ผู้อ่านส่วนใหญ่ต้องการ แต่ฉันจะพยายามอย่างเต็มที่

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

คำถามของการแทรก SQL ไม่สามารถทำให้เข้าใจง่ายขึ้นสำหรับปัญหาทางไวยากรณ์บางประการ มันกว้างกว่าที่นักพัฒนาทั่วไปเคยคิด เป็นคำถามเกี่ยวกับระเบียบวิธีเช่นกัน มันไม่ได้เป็นเพียง "ซึ่งโดยเฉพาะอย่างยิ่งการจัดรูปแบบที่เราต้องใช้" แต่ " วิธีการก็จะต้องมีการทำ" ได้เป็นอย่างดี

(จากมุมมองนี้บทความของ Jon Skeet ที่อ้างถึงในคำตอบอื่น ๆ นั้นทำได้ค่อนข้างแย่มากกว่าดีเนื่องจากเป็นการพิจารณาอีกครั้งในกรณีขอบโดยมุ่งเน้นไปที่ปัญหาทางไวยากรณ์โดยเฉพาะและไม่สามารถแก้ไขปัญหาได้ทั้งหมด)

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

  • รายการตัวเลือกการจัดรูปแบบที่เป็นไปได้นั้นใหญ่มาก หมายถึงเราสามารถมองข้ามบางอย่างได้อย่างง่ายดาย หรือทำให้สับสน (โดยใช้การหลีกเลี่ยงสตริงสำหรับตัวระบุเป็นต้น)
  • การเชื่อมต่อกันหมายความว่ามาตรการป้องกันทั้งหมดต้องทำโดยโปรแกรมเมอร์ไม่ใช่โปรแกรม ปัญหานี้เพียงอย่างเดียวนำไปสู่ผลลัพธ์หลายประการ:
    • การจัดรูปแบบดังกล่าวเป็นแบบแมนนวล คู่มือหมายถึงเกิดข้อผิดพลาดอย่างมาก อาจลืมสมัครไปก็ได้
    • ยิ่งไปกว่านั้นยังมีความอยากที่จะย้ายขั้นตอนการจัดรูปแบบไปยังฟังก์ชันส่วนกลางบางอย่างทำให้ยุ่งเหยิงมากยิ่งขึ้นและทำให้ข้อมูลเสียที่จะไม่ไปที่ฐานข้อมูล
  • เมื่อมีนักพัฒนามากกว่าหนึ่งคนที่เกี่ยวข้องปัญหาจะทวีคูณด้วยปัจจัยสิบ
  • เมื่อใช้การเรียงต่อกันเราไม่สามารถบอกคำค้นหาที่อาจเป็นอันตรายได้ในทันที: ทั้งหมดนี้อาจเป็นอันตราย!

ซึ่งแตกต่างจากระเบียบนั้นข้อความที่เตรียมไว้คือจอกศักดิ์สิทธิ์:

  • สามารถแสดงออกในรูปแบบของกฎง่ายๆข้อเดียวที่ปฏิบัติตามได้ง่าย
  • โดยพื้นฐานแล้วมันเป็นมาตรการที่ไม่สามารถแก้ไขได้หมายความว่านักพัฒนาไม่สามารถแทรกแซงและทำให้เสียกระบวนการโดยเต็มใจหรือไม่เต็มใจ
  • การป้องกันจากการฉีดยาเป็นเพียงผลข้างเคียงของข้อความที่เตรียมไว้เท่านั้นซึ่งจุดประสงค์ที่แท้จริงคือการสร้างข้อความที่ถูกต้องตามหลักไวยากรณ์ และคำสั่งที่ถูกต้องตามหลักไวยากรณ์คือหลักฐานการฉีด 100% แต่เราต้องการให้ไวยากรณ์ของเราถูกต้องแม้ว่าจะมีความเป็นไปได้ในการฉีดก็ตาม
  • หากใช้อย่างรอบด้านจะช่วยปกป้องแอปพลิเคชันโดยไม่คำนึงถึงประสบการณ์ของผู้พัฒนา กล่าวว่ามีสิ่งที่เรียกว่าการฉีดลำดับที่สอง และความเข้าใจผิดที่รุนแรงมากที่อ่านว่า "เพื่อป้องกัน, หลีกเลี่ยงอินพุตที่ผู้ใช้ให้มาทั้งหมด " เมื่อรวมเข้าด้วยกันจะนำไปสู่การฉีดหากนักพัฒนาใช้เสรีภาพในการตัดสินใจสิ่งที่ต้องได้รับการปกป้องและสิ่งที่ไม่ควรทำ

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

ที่น่าสนใจคือการโต้เถียงของคำถามนี้เกิดจากลักษณะที่ขัดแย้งกันมากของ Stack Overflow ความคิดของเว็บไซต์ที่จะทำให้การใช้งานของคำถามโดยเฉพาะอย่างยิ่งจากผู้ใช้ที่ถามโดยตรงเพื่อให้บรรลุเป้าหมายของการมีฐานข้อมูลของการใช้งานทั่วไปคำตอบที่เหมาะสมสำหรับผู้ใช้ที่มาจากการค้นหา ความคิดที่ไม่เลวต่อ seแต่ก็ล้มเหลวในสถานการณ์เช่นนี้เมื่อผู้ใช้ถามคำถามที่แคบมากโดยเฉพาะอย่างยิ่งที่จะได้รับการโต้แย้งในความขัดแย้งกับเพื่อนร่วมงาน (หรือที่จะตัดสินใจว่ามันคุ้มค่าที่จะ refactor รหัส) ในขณะที่ผู้เข้าร่วมที่มีประสบการณ์ส่วนใหญ่พยายามเขียนคำตอบโดยคำนึงถึงภารกิจ ของ Stack Overflow ทั้งหมดทำให้คำตอบของพวกเขาดีสำหรับผู้อ่านให้มากที่สุดไม่ใช่ OP เท่านั้น


10
ไม่ตอบคำถาม
edc65

ฐานข้อมูลส่วนใหญ่ตรวจพบเคียวรีที่กำหนดพารามิเตอร์ที่ใช้แล้วโดยความเท่าเทียมกันของสตริง SQL ดังนั้นวิธีการจัดการการเตรียมและใช้งานแบบเก่าจึงดูล้าสมัยสำหรับฉัน ที่จับเหล่านี้สามารถใช้ได้ภายในขอบเขตที่กำหนดเท่านั้นและต้องมีการเข้ารหัสเพื่อติดตามแฮนเดิล ในความคิดของฉันควรใช้คิวรีแบบกำหนดพารามิเตอร์โดยตรงเพื่อให้สามารถใช้แผนการสืบค้นซ้ำได้โดยไม่ต้องจัดการการติดตามและแม้แต่ในแอปพลิเคชันต่างๆ
Erik Hart

15

อย่าเพิ่งคิดถึงความปลอดภัยหรือการพิจารณาประเภทความปลอดภัย

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

เว้นแต่

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

ตอนนี้

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

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


8

หากต้องการเพิ่มข้อมูลให้กับคำตอบ Maciek:

มันง่ายที่จะแก้ไขข้อมูลวัฒนธรรมของแอพ. NET ของบุคคลที่สามโดยเรียกใช้ฟังก์ชันหลักของแอสเซมบลีโดยการสะท้อน:

using System;
using System.Globalization;
using System.Reflection;
using System.Threading;

namespace ConsoleApplication2
{
  class Program
  {
    static void Main(string[] args)
    {
      Assembly asm = Assembly.LoadFile(@"C:\BobbysApp.exe");
      MethodInfo mi = asm.GetType("Test").GetMethod("Main");
      mi.Invoke(null, null);
      Console.ReadLine();
    }

    static Program()
    {
      InstallBobbyTablesCulture();
    }

    static void InstallBobbyTablesCulture()
    {
      CultureInfo bobby = (CultureInfo)CultureInfo.InvariantCulture.Clone();
      bobby.DateTimeFormat.ShortDatePattern = @"yyyy-MM-dd'' OR ' '=''";
      bobby.DateTimeFormat.LongTimePattern = "";
      bobby.NumberFormat.NegativeSign = "1 OR 1=1 OR 1=";
      Thread.CurrentThread.CurrentCulture = bobby;
    }
  }
}

สิ่งนี้ใช้ได้เฉพาะในกรณีที่ฟังก์ชันหลักของ BobbysApp เป็นแบบสาธารณะ หาก Main ไม่ใช่สาธารณะอาจมีฟังก์ชันสาธารณะอื่น ๆ ที่คุณอาจเรียกใช้


1
คุณไม่จำเป็นต้องทำด้วยรหัส คุณสามารถเพิ่มการฉีดได้โดยตรงในการตั้งค่าภูมิภาคของ Windows ดูคำตอบของฉัน
Kaspars Ozols

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

7

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


1
ไม่ใช่ว่าฉันไม่ต้องการใช้พารามิเตอร์ฉันใช้พารามิเตอร์ เพื่อนร่วมงานคนหนึ่งเขียนโค้ดแบบนี้ซึ่งฉันได้แก้ไขให้เป็นพารามิเตอร์ในวันนี้ซึ่งทำให้ฉันนึกถึงคำถามนั้น
johnnyRose

ตกลง. ยิ่งใหญ่ เป็นแนวทางปฏิบัติที่ดีที่สุดในการใช้พารามิเตอร์ วิธีนี้ทำให้คุณไม่ต้องกังวลกับสิ่งต่างๆเช่นการฉีด sql นอกจากนี้หากคุณกำลังสร้างแบบสอบถามแบบไดนามิกคุณยังสามารถใช้พารามิเตอร์ได้ไม่ว่าข้อความค้นหาของคุณจะซับซ้อนเพียงใด เพียงใช้สไตล์ @ 1 ... @ n ในการสร้าง และผนวกเข้ากับคอลเล็กชันพารามิเตอร์ด้วยค่าที่ต้องการ
สูงสุด

@johnyRose มีอีกหนึ่งประเด็นในการใช้พารามิเตอร์นั่นคือโปรแกรมกำลังพัฒนาและเปลี่ยนแปลง คุณสามารถใช้การเรียงต่อกันสำหรับสตริงเท่านั้น แต่ไม่ได้รับประกันว่ามีคนใช้การปรับโครงสร้างที่เปลี่ยนประเภทพารามิเตอร์บางอย่างและการเปลี่ยนแปลงนั้นอาจทำให้เกิดช่องโหว่ของ SQL Injection
lerthe61

3

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


นั่นเป็นสตริงแม้ว่า ฉันกำลังพูดถึงประเภทข้อมูลอื่น ๆ เช่นจำนวนเต็มบูลีนหรือวันที่
johnnyRose

@johnnyRose Yup ฉันเคยเห็นตัวอย่างที่ดีมากด้านบนที่คุณทำเครื่องหมายว่าตอบโดย Kaspards .. และคำตอบที่ดีในขณะที่เขาใช้ประเภทข้อมูลวันที่และเวลาเป็นตัวอย่างซึ่งผิดปกติ :) ฉันหวังว่าคุณจะมั่นใจแล้วว่ามันไม่ปลอดภัยและดีกว่าที่จะใช้พารามิเตอร์ในประเภทข้อมูลประเภทใด ๆ
japzdivino

ฉันไม่เคยสงสัยเลยว่าการใช้พารามิเตอร์นั้นปลอดภัยกว่า คำถามของฉันอ้างถึงการใช้งานที่พิมพ์ผิด
johnnyRose

ใช่.. ฉันเห็นด้วย และนั่นก็เป็นคำถามที่ดีที่จะช่วยผู้อ่านในอนาคต :)
japzdivino

-2

คุณไม่สามารถรับการโจมตีด้วยการฉีด SQL ได้ด้วยวิธีนี้ ฉันได้เขียนบทความเก่าเป็นภาษาตุรกีซึ่งแสดงให้เห็นว่าที่นี่เป็นอย่างไร ตัวอย่างบทความใน PHP และ MySQL แต่แนวคิดทำงานเหมือนกันใน C # และ SQL Server

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

http://localhost/sqlEnjeksiyon//instructors.aspx?id=24

โอเคฉันคิดว่าคุณกำลังใช้ MySQL และฉันโจมตีด้วยวิธีต่อไปนี้

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII((SELECT%20DATABASE()))

โปรดทราบว่าค่าที่ฉีดที่นี่ไม่ใช่สตริง เรากำลังเปลี่ยนค่าถ่านเป็น int โดยใช้ฟังก์ชัน ASCII คุณสามารถทำสิ่งเดียวกันใน SQL Server โดยใช้ "CAST (YourVarcharCol AS INT)"

หลังจากนั้นฉันใช้ความยาวและฟังก์ชันสตริงย่อยเพื่อค้นหาชื่อฐานข้อมูลของคุณ

http://localhost/sqlEnjeksiyon//instructors.aspx?id=LEN((SELECT%20DATABASE()))

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII(SUBSTR(SELECT%20DATABASE(),1,1))

จากนั้นใช้ชื่อฐานข้อมูลคุณจะเริ่มได้รับชื่อตารางในฐานข้อมูล

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII(SUBSTR((SELECT table_name FROM INFORMATION_SCHEMA.TABLES LIMIT 1),1,1))

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


2
คำถามของฉันหมายถึงภาษาที่พิมพ์ผิด แม้ว่าคำอธิบายของคุณจะเหมาะสำหรับภาษาที่มีการพิมพ์ที่ยืดหยุ่น แต่ค่าที่ฉีดยังคงเป็นสตริง
johnnyRose

ไม่มีค่าที่ฉีดเป็นจำนวนเต็ม คุณได้รับถ่านและเปลี่ยนเป็นจำนวนเต็มโดยใช้ฟังก์ชัน ASCII MySQL คุณทำสิ่งเดียวกันใน SQL Server โดยใช้ CAST (YourCharValue AS INT)
Atilla Ozgur

ฉันหมายถึงสิ่งนี้:public void QuerySomething(int id) { // this will only accept an integer }
johnnyRose

-3

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

สิ่งที่สำคัญใน SQL Injection คือชนิดข้อมูลของตัวแปรที่ใช้รับค่าจากผู้ใช้

สมมติว่าเรามีจำนวนเต็มใน where clause และ:

  1. ตัวแปรผู้ใช้คือสตริง ตกลงมันไม่ใช่เรื่องง่ายที่จะฉีด (โดยใช้ UNION) แต่มันง่ายมากที่จะข้ามโดยใช้ 'หรือ 1 = 1' - เหมือนการโจมตี ...

  2. หากตัวแปรผู้ใช้เป็นจำนวนเต็ม จากนั้นอีกครั้งเราสามารถ 'ทดสอบ' ความแข็งแกร่งของระบบโดยผ่านการทดสอบตัวเลขขนาดใหญ่ที่ผิดปกติสำหรับระบบล่มหรือแม้กระทั่งการล้นบัฟเฟอร์ที่ซ่อนอยู่ (ในสตริงสุดท้าย) ... ;)

บางทีพารามิเตอร์ในการสืบค้นหรือ (ดีกว่า - imo) ไปยัง Stored Procedures อาจไม่ใช่ภัยคุกคาม 100% แต่เป็นมาตรการที่จำเป็นน้อยที่สุด (หรือขั้นพื้นฐานหากคุณต้องการ) เพื่อย่อให้เล็กที่สุด


ฉันคิดว่าคุณอาจเข้าใจคำถามผิด ฉันกำลังพูดถึงประเภทที่ไม่ใช่สตริง
johnnyRose

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