การหลีกเลี่ยงการฉีด SQL โดยไม่มีพารามิเตอร์


109

เรากำลังมีการอภิปรายอีกครั้งในที่ทำงานเกี่ยวกับการใช้การสืบค้น sql ที่เป็นพารามิเตอร์ในโค้ดของเรา เรามีสองด้านในการสนทนา: ฉันและคนอื่น ๆ บางคนที่บอกว่าเราควรใช้พารามิเตอร์เพื่อป้องกันการฉีด sql และคนอื่น ๆ ที่ไม่คิดว่าจำเป็น แต่พวกเขาต้องการแทนที่เครื่องหมายวรรคตอนเดี่ยวด้วยเครื่องหมายอะพอสทรอฟีสองตัวในทุกสตริงเพื่อหลีกเลี่ยงการฉีด sql ฐานข้อมูลของเรากำลังรัน Sql Server 2005 หรือ 2008 และฐานรหัสของเราทำงานบน. NET framework 2.0

ขอยกตัวอย่างง่ายๆใน C #:

ฉันต้องการให้เราใช้สิ่งนี้:

string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe

ในขณะที่คนอื่น ๆ ต้องการทำสิ่งนี้:

string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?

โดยที่ฟังก์ชัน SafeDBString ถูกกำหนดดังนี้:

string SafeDBString(string inputValue) 
{
    return "'" + inputValue.Replace("'", "''") + "'";
}

ตอนนี้ตราบใดที่เราใช้ SafeDBString กับค่าสตริงทั้งหมดในแบบสอบถามของเราเราก็ควรจะปลอดภัย ขวา?

มีสองเหตุผลในการใช้ฟังก์ชัน SafeDBString ประการแรกมันเป็นวิธีที่ทำมาตั้งแต่ยุคหินและประการที่สองการดีบักคำสั่ง sql นั้นง่ายกว่าเนื่องจากคุณเห็นคิวรี excact ที่รันบนฐานข้อมูล

ถ้าอย่างนั้น คำถามของฉันคือการใช้ฟังก์ชัน SafeDBString เพียงพอหรือไม่เพื่อหลีกเลี่ยงการโจมตี sql injection ฉันพยายามค้นหาตัวอย่างของโค้ดที่ละเมิดมาตรการความปลอดภัยนี้ แต่ไม่พบตัวอย่างใด ๆ

มีใครที่สามารถทำลายสิ่งนี้ได้บ้าง? คุณจะทำอย่างไร?

แก้ไข: เพื่อสรุปคำตอบจนถึงตอนนี้:

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

ดังนั้นในขณะที่ไม่มีใครสามารถทำลายความปลอดภัยอย่างง่ายของฟังก์ชัน SafeDBString ได้ฉันก็มีข้อโต้แย้งดีๆมากมาย ขอบคุณ!


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

1
อย่างน้อยเพื่อนร่วมงานของคุณก็ป้องกันการแฮ็กรูปแบบต่างๆได้อย่างใดอย่างหนึ่ง ... พวกเขาแน่ใจหรือไม่ว่าแบบสอบถามที่กำหนดพารามิเตอร์ทั้งหมดทำ (ฉันไม่ใช่ ... )
Arjan Einbu

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

3
แม้จะไม่มีเครื่องหมายคำพูดเดียวคุณยังสามารถทำลายรหัสของคุณด้วยตรรกะได้ ลองใช้ชื่อผู้ใช้ "test OR 1 = 1" - คุณจะได้รับแถวทั้งหมดที่ส่งคืนไม่ใช่แค่แถวเดียวกับการทดสอบชื่อผู้ใช้!
สะพาน

1
เฮ้อ. ฉันไม่เข้าใจว่าเราในฐานะอุตสาหกรรมจัดการอย่างไรเพื่อทนต่อพฤติกรรมที่ไม่เป็นมืออาชีพแบบนี้
jeroenh

คำตอบ:


83

ฉันคิดว่าคำตอบที่ถูกต้องคือ:

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

ใช้พารามิเตอร์


Re "ใช้ไลบรารีมาตรฐานอุตสาหกรรมที่เชื่อถือได้" - คุณช่วยแนะนำสำหรับ. NET ได้ไหม อาจมากกว่าหนึ่งขึ้นอยู่กับ DB: SQLServer, MySQL, PostgreSQL? ฉันมองหา SQL-sanitizer แต่ไม่มีโชคดังนั้นจึงถูกบังคับให้ติดตั้งด้วยตัวเองให้ดีที่สุดเท่าที่จะทำได้ (ซึ่งไม่ต้องสงสัยเลยว่าจะไม่สามารถเข้าใจผิดได้)
PSU

72

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

นอกจากนี้ยังหลีกเลี่ยงปัญหา i18n จำนวนมากเกี่ยวกับวันที่ / ตัวเลข วันที่ 01/02/03 คือวันใด 123,456 เป็นเท่าไหร่? เซิร์ฟเวอร์ของคุณ (app-server และ db-server) ตกลงกันเองหรือไม่?

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


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

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

1
@tnyfst: สามารถใช้แผนการดำเนินการซ้ำได้เมื่อสตริงการสืบค้นเปลี่ยนแปลงสำหรับค่าพารามิเตอร์ทุกชุด? ฉันไม่คิดว่าจะเป็นไปได้
John Saunders

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

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

27

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

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

หากปัญหาคือการเขียนรหัสเดิมทั้งหมดใหม่การประนีประนอมอย่างง่ายคือการใช้การสืบค้นแบบพารามิเตอร์ในรหัสใหม่ทั้งหมดและทำการ refactor รหัสเก่าเพื่อใช้เมื่อทำงานกับรหัสนั้น

ฉันเดาว่าปัญหาที่เกิดขึ้นจริงคือความภาคภูมิใจและความดื้อรั้นและไม่มีอะไรให้คุณทำได้มากกว่านี้


19

ก่อนอื่นตัวอย่างของคุณสำหรับเวอร์ชัน "แทนที่" ไม่ถูกต้อง คุณต้องใส่เครื่องหมายอะพอสทรอฟีรอบข้อความ:

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);

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

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

นอกจากนี้การหลีกเลี่ยงเครื่องหมายคำพูดเดี่ยวยังไม่ดีพอ ผลิตภัณฑ์ DB จำนวนมากอนุญาตให้ใช้วิธีอื่นในการหลบหนีอักขระที่ผู้โจมตีสามารถใช้ประโยชน์ได้ ตัวอย่างเช่นใน MySQL คุณสามารถหลีกเลี่ยงเครื่องหมายคำพูดเดียวด้วยแบ็กสแลช ดังนั้นค่า "ชื่อ" ต่อไปนี้จะระเบิด MySQL ด้วยSafeDBString()ฟังก์ชันเพราะเมื่อคุณเพิ่มเครื่องหมายคำพูดเดี่ยวเป็นสองเท่าค่าแรกจะยังคงถูกหลีกเลี่ยงโดยแบ็กสแลชโดยปล่อยให้ค่าที่ 2 เป็น "active":

x \ 'หรือ 1 = 1; -


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

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


5
ฟังก์ชันแทนที่จะเพิ่มเครื่องหมายอะพอสทรอฟี
Rune Grimstad

5
จากนั้นก็เป็นเพียงแหล่งที่มาของจุดบกพร่องอีกแห่งหนึ่ง มันรู้ความแตกต่างระหว่าง NULL เป็นค่า null และ NULL เป็นสตริงข้อความได้อย่างไร? หรือระหว่างอินพุตตัวเลขกับสตริงที่มีตัวเลขอยู่?
Joel Coehoorn

จุดดี. คุณควรใช้ฟังก์ชันสำหรับสตริงเท่านั้นและอาจเป็นวันที่ดังนั้นคุณต้องระวัง นั่นเป็นอีกเหตุผลหนึ่งที่ต้องใช้พารามิเตอร์! เย้!
Rune Grimstad

10

ฉันจะพูดว่า:

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

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

3) คุณแน่ใจเพียงใดว่าคุณได้ครอบคลุมเวกเตอร์การโจมตีทั้งหมดที่ Microsoft (ผู้เขียนฐานข้อมูลและไลบรารีการเข้าถึง) รู้ในการใช้งาน SafeDBString ของคุณ ...

4) การอ่านโครงสร้างของ sql นั้นง่ายแค่ไหน? ตัวอย่างใช้ + การเรียงต่อกันพารามิเตอร์เหมือนสตริงมากรูปแบบซึ่งอ่านได้ง่ายกว่า

นอกจากนี้ยังมี 2 วิธีในการหาสิ่งที่เรียกใช้จริง - ม้วนฟังก์ชัน LogCommand ของคุณเองฟังก์ชันง่ายๆที่ไม่มีปัญหาด้านความปลอดภัยหรือแม้แต่ดูการติดตาม sql เพื่อหาสิ่งที่ฐานข้อมูลคิดว่าเกิดขึ้นจริง

ฟังก์ชัน LogCommand ของเราเป็นเพียง:

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }

ถูกหรือผิดก็ให้ข้อมูลที่เราต้องการโดยไม่มีปัญหาด้านความปลอดภัย


1
เขาอาจต้องจัดการกับโปรแกรมเมอร์ VBSCRIPT เก่า ๆ ที่คุ้นเคยกับการทำทุกอย่างรวมถึง XML และ SQL ผ่านการต่อสายอักขระ คนเหล่านี้จะกลัวการใช้ API ไม่มีอะไรที่สามารถทำได้กับพวกเขาอย่างน้อยก็ไม่มีมนุษยธรรม
John Saunders

1
+1 สำหรับรายการ # 2 ยกเว้นว่าจะไม่มีวิธีบังคับใช้พารามิเตอร์จริงเช่นกัน
Joel Coehoorn

7

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


MySQL ยังบันทึกการสืบค้นที่กำหนดพารามิเตอร์ด้วยค่าพารามิเตอร์ที่แทรกเข้าไป
Bill Karwin

5

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


2
+1 เนื่องจาก mysql_real_escape_string () หนี \ x00, \ x1a, \ n \ r 'และ "นอกจากนี้ยังจัดการปัญหาชุดอักขระอีกด้วยฟังก์ชันไร้เดียงสาของ OP ไม่ได้ทำสิ่งนั้น!
Bill Karwin

4

คุณไม่สามารถทำการตรวจสอบอินพุตของผู้ใช้ได้ทุกประเภทโดยไม่ต้องใช้พารามิเตอร์

หากคุณใช้คลาส SQLCommand และ SQLParameter เพื่อทำการเรียก DB คุณจะยังคงเห็นคิวรี SQL ที่กำลังดำเนินการอยู่ ดูคุณสมบัติ CommandText ของ SQLCommand

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


3

สิ่งนี้จะปลอดภัยก็ต่อเมื่อคุณรับประกันว่าคุณจะผ่านสตริง

จะเกิดอะไรขึ้นถ้าคุณไม่ผ่านสตริงในบางจุด? จะเกิดอะไรขึ้นถ้าคุณผ่านเพียงตัวเลข?

http://www.mywebsite.com/profile/?id=7;DROP DATABASE DB

ในที่สุดจะกลายเป็น:

SELECT * FROM DB WHERE Id = 7;DROP DATABASE DB

เป็นสตริงหรือตัวเลขก็ได้ สตริงจะถูกหลีกเลี่ยงด้วย SafeDbString ตัวเลขคือ Int32 และไม่สามารถวางฐานข้อมูลได้
Andomar

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

Andomar - ถ้าคุณแค่สร้างคำสั่ง SQL ด้วยมือมันก็คือ "type" ก็ไม่สำคัญคุณสามารถฉีด SQL ด้วยตัวเลขได้อย่างง่ายดายมาก รูน - ฉันคิดว่านี่เป็นการพึ่งพาผู้พัฒนาแต่ละรายมากเกินไปในการจดจำความแตกต่างทั้งหมดของการแก้ SQL injection ด้วยตนเอง ถ้าคุณแค่พูดว่า "ใช้พารามิเตอร์" มันง่ายมากและไม่ผิดพลาด
joshcomley

@Andomar: แล้ว NULL ล่ะ? หรือสตริงที่ดูเหมือนตัวเลข?
Joel Coehoorn

2

ฉันจะใช้กระบวนงานหรือฟังก์ชันที่จัดเก็บไว้สำหรับทุกสิ่งดังนั้นคำถามจะไม่เกิดขึ้น

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


ตกลงวิธีการฉีด SQL โดยใช้พารามิเตอร์?
John Saunders

@Saunders: ขั้นตอนที่ 1 คือการค้นหาบัฟเฟอร์ล้นบั๊กในฟังก์ชันการจัดการพารามิเตอร์ของฐานข้อมูลของคุณ
Brian

2
เจอหรือยัง ในฐานข้อมูลเชิงพาณิชย์ที่ถูกแฮกเกอร์นับแสนรายวัน? หนึ่งที่สร้างโดย บริษัท ซอฟต์แวร์ที่ทราบว่ามีกระเป๋าที่ลึกมาก คุณสามารถอ้างชื่อคดีตามชื่อได้หากเป็นไปได้
John Saunders

1
แน่นอนว่าถ้า SPROC ใช้การเรียงต่อกันและ EXEC (แทนที่จะเป็น sp_ExecuteSQL) คุณจะกลับมามีปัญหาอีกครั้ง ... (ฉันเคยเห็นมันทำผิดหลายครั้งเกินไปที่จะลดราคา ... )
Marc Gravell

2

เห็นด้วยอย่างมากเกี่ยวกับปัญหาด้านความปลอดภัย
อีกเหตุผลหนึ่งที่ต้องใช้พารามิเตอร์เพื่อประสิทธิภาพ

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

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


2

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

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

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

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

ชั้นมีลักษณะดังนี้ ...

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

namespace myNamespace
{
    /// <summary>
    /// Pour le confort et le bonheur, cette classe remplace StringBuilder pour la construction
    /// des requêtes SQL, avec l'avantage qu'elle gère la création des paramètres via la méthode
    /// Value().
    /// </summary>
    public class SqlBuilder
    {
        private StringBuilder _rq;
        private SqlCommand _cmd;
        private int _seq;
        public SqlBuilder(SqlCommand cmd)
        {
            _rq = new StringBuilder();
            _cmd = cmd;
            _seq = 0;
        }
        //Les autres surcharges de StringBuilder peuvent être implémenté ici de la même façon, au besoin.
        public SqlBuilder Append(String str)
        {
            _rq.Append(str);
            return this;
        }
        /// <summary>
        /// Ajoute une valeur runtime à la requête, via un paramètre.
        /// </summary>
        /// <param name="value">La valeur à renseigner dans la requête</param>
        /// <param name="type">Le DBType à utiliser pour la création du paramètre. Se référer au type de la colonne cible.</param>
        public SqlBuilder Value(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append(paramName);
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this;
        }
        public SqlBuilder FuzzyValue(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append("'%' + " + paramName + " + '%'");
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this; 
        }

        public override string ToString()
        {
            return _rq.ToString();
        }
    }
}

1

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

ซึ่งจะทิ้งพารามิเตอร์และกระบวนงานที่จัดเก็บไว้

และใช่คุณควรพยายามติดตั้งโค้ดด้วยวิธีที่ดีที่สุดที่คุณรู้ในตอนนี้ไม่ใช่แค่การทำโค้ดเสมอไป


เครื่องหมายวรรคตอนคู่จะถูกแปลโดยเซิร์ฟเวอร์ sql เป็นเครื่องหมายวรรคตอนเดียวดังนั้น O'Reilly จะถูกแปลเป็น Name = 'O''Reilly'
Rune Grimstad

ดังนั้นจึงมีฟังก์ชันที่เกี่ยวข้องในการลบเครื่องหมายอะพอสทรอฟีเมื่อผู้ใช้ต้องการดูข้อมูลหรือไม่?
quamrana

ไม่จำเป็น. ลำดับการหลีกเลี่ยงอนุญาตให้ตัวแยกวิเคราะห์เห็นเครื่องหมายคำพูดเดียวแทนที่จะเป็นจุดสิ้นสุดของสตริง มันก็เห็น''เป็นตัวอักษรดังนั้นสายของคุณจะเห็นภายในเป็นลำดับตัวอักษร' O'Reillyนั่นคือสิ่งที่ DB จะจัดเก็บดึงข้อมูลเปรียบเทียบและอื่น ๆ หากคุณต้องการแสดงข้อมูลของผู้ใช้หลังจากที่คุณได้หลีกเลี่ยงแล้วให้เก็บสำเนาของสตริงที่ไม่ใช้ค่า Escape
cHao

1

ต่อไปนี้เป็นบทความสองสามข้อที่คุณอาจพบว่ามีประโยชน์ในการโน้มน้าวเพื่อนร่วมงานของคุณ

http://www.sommarskog.se/dynamic_sql.html

http://unixwiz.net/techtips/sql-injection.html

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


1

อาจเสียได้อย่างไรก็ตามวิธีการขึ้นอยู่กับเวอร์ชัน / แพตช์ที่แน่นอนเป็นต้น

สิ่งหนึ่งที่นำมาแล้วคือข้อผิดพลาดล้น / การตัดทอนที่สามารถใช้ประโยชน์ได้

อีกวิธีหนึ่งในอนาคตคือการค้นหาจุดบกพร่องที่คล้ายกับฐานข้อมูลอื่นตัวอย่างเช่นสแต็ก MySQL / PHP ประสบปัญหาในการหลบหนีเนื่องจากลำดับ UTF8 บางตัวสามารถใช้เพื่อจัดการกับฟังก์ชันการแทนที่ฟังก์ชันแทนที่จะถูกหลอกให้แนะนำอักขระการฉีด

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

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


1

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

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


1

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

นอกจากนี้ยังมีQUOTENAMEฟังก์ชัน T-SQL ที่มีประโยชน์หากคุณไม่สามารถโน้มน้าวให้พวกเขาใช้พารามิเตอร์ได้ มันดึงดูดความกังวลเกี่ยวกับ qoute ที่หลบหนีได้มาก (ทั้งหมด?)


1

2 ปีต่อมาผม recidivated ... ทุกคนที่พบว่าพารามิเตอร์เจ็บปวดคือยินดีที่จะลองขยาย VS ฉัน QueryFirstคุณแก้ไขคำขอของคุณในไฟล์. sql จริง (Validation, Intellisense) ในการเพิ่มพารามิเตอร์คุณเพียงแค่พิมพ์ลงใน SQL ของคุณโดยตรงโดยเริ่มต้นด้วย "@" เมื่อคุณบันทึกไฟล์ QueryFirst จะสร้างคลาส Wrapper เพื่อให้คุณเรียกใช้แบบสอบถามและเข้าถึงผลลัพธ์ มันจะค้นหาประเภท DB ของพารามิเตอร์ของคุณและแมปกับประเภท. net ซึ่งคุณจะพบว่าเป็นอินพุตของเมธอด Execute () ที่สร้างขึ้น อาจไม่ง่ายกว่านี้. การทำอย่างถูกวิธีนั้นรวดเร็วและง่ายกว่าการทำวิธีอื่น ๆ อย่างมากและการสร้างช่องโหว่ในการฉีด sql จะเป็นไปไม่ได้หรืออย่างน้อยก็ยากในทางตรงกันข้าม มีข้อดีอื่น ๆ เช่นความสามารถในการลบคอลัมน์ในฐานข้อมูลของคุณและเห็นข้อผิดพลาดในการคอมไพล์ในแอปพลิเคชันของคุณทันที

ข้อจำกัดความรับผิดชอบทางกฎหมาย: ฉันเขียน QueryFirst


0

ต่อไปนี้เป็นเหตุผลบางประการในการใช้การสืบค้นแบบกำหนดพารามิเตอร์:

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

0

มีช่องโหว่เล็กน้อย (ฉันจำไม่ได้ว่าเป็นฐานข้อมูลใด) ที่เกี่ยวข้องกับการบัฟเฟอร์ล้นของคำสั่ง SQL

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


0

การพิจารณาที่สำคัญอีกประการหนึ่งคือการติดตามข้อมูลที่หลบหนีและไม่ใช้ Escape มีแอปพลิเคชันมากมายเว็บและอื่น ๆ ที่ดูเหมือนจะไม่ได้รับการติดตามอย่างถูกต้องเมื่อข้อมูลเป็น Raw-Unicode, & -encoded, จัดรูปแบบ HTML และอื่น ๆ เห็นได้ชัดว่าจะกลายเป็นเรื่องยากที่จะติดตามว่าสตริงใดที่''เข้ารหัสและไม่ได้เข้ารหัส

นอกจากนี้ยังเป็นปัญหาเมื่อคุณเปลี่ยนประเภทของตัวแปรบางตัว - บางทีอาจเคยเป็นจำนวนเต็ม แต่ตอนนี้เป็นสตริง ตอนนี้คุณมีปัญหา

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