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


ก่อนอื่น อธิบายเล็กน้อยว่าทำไมโดยทั่วไปจึงต้องใช้เครื่องหมายทับเหล่านี้
หากเราแทนที่ข้อมูลใดๆ ลงในแบบสอบถาม ดังนั้นเพื่อที่จะแยกแยะข้อมูลนี้จากคำสั่ง SQL จะต้องใส่เครื่องหมายคำพูดเพื่อแยกความแตกต่างระหว่างข้อมูลนี้
เช่น ถ้าคุณเขียน
SELECT * จากตาราง โดยที่ชื่อ = บิล
จากนั้นฐานข้อมูลจะตัดสินว่า Bill เป็นชื่อของเขตข้อมูลอื่น ไม่พบ และจะส่งข้อผิดพลาด ดังนั้นข้อมูลที่ถูกแทนที่ (ในกรณีนี้คือชื่อ Bill) จะต้องอยู่ในเครื่องหมายคำพูด - จากนั้นฐานข้อมูลจะพิจารณาว่าเป็นสตริงซึ่งจะต้องกำหนดค่าให้กับฟิลด์ชื่อ:
SELECT * จากตาราง WHERE name = "Bill"
อย่างไรก็ตาม ราคาอาจปรากฏในข้อมูลด้วย เช่น,
SELECT * จากตารางโดยที่ name = "D"Artagnan"
ที่นี่ฐานข้อมูลจะตัดสินว่า "D" คือข้อมูล และ Artagnan เป็นคำสั่งที่ไม่ทราบ และจะทำให้เกิดข้อผิดพลาดด้วย ดังนั้นจึงจำเป็นต้องติดตามข้อมูลทั้งหมดเพื่ออธิบายให้ฐานข้อมูลทราบว่าเครื่องหมายคำพูด (และอักขระพิเศษอื่น ๆ) ที่พบในนั้นอ้างอิงถึงข้อมูล
ด้วยเหตุนี้ เราจะได้รับคำขอที่ถูกต้องซึ่งจะไม่ทำให้เกิดข้อผิดพลาด:
SELECT * จากตาราง โดยที่ name = "D\"Artagnan"

ดังนั้นเราจึงพบว่าเมื่อแทนที่ข้อมูลสตริงลงในแบบสอบถาม ควรปฏิบัติตามกฎสองข้อ:
- ข้อมูลสตริงที่แทรกทั้งหมดจะต้องอยู่ในเครื่องหมายคำพูด (เดี่ยวหรือคู่ แต่ข้อมูลเดี่ยวจะสะดวกกว่าและใช้บ่อยกว่า)
- อักขระพิเศษจะต้องหลีกด้วยเครื่องหมายทับ

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

ทั้งหมดข้างต้นใช้กับข้อมูลสตริงและวันที่ สามารถแทรกตัวเลขได้โดยไม่ต้องต่อท้ายหรือล้อมรอบด้วยเครื่องหมายคำพูด ถ้าคุณทำเช่นนี้แล้ว อย่างจำเป็น!บังคับข้อมูลให้เป็นประเภทที่ต้องการก่อนที่จะแทรกลงในแบบสอบถาม เช่น
$id = intval ($id);
อย่างไรก็ตาม เพื่อความเรียบง่าย (และความน่าเชื่อถือ) คุณสามารถทำงานกับตัวเลขได้เช่นเดียวกับสตริง (เนื่องจาก mysql ยังคงแปลงเป็นประเภทที่ต้องการ) ดังนั้น เราจะติดตามข้อมูลใด ๆ ที่แทรกลงในคำขอและใส่ไว้ในเครื่องหมายคำพูด

นอกจากนี้ยังมีกฎอีกข้อหนึ่ง - เป็นทางเลือก แต่ควรปฏิบัติตามเพื่อหลีกเลี่ยงข้อผิดพลาด:
ชื่อของฟิลด์และตารางควรอยู่ในเครื่องหมายคำพูดเดี่ยวด้านหลัง - "`" (คีย์ที่มีสัญลักษณ์นี้จะอยู่บนแป้นพิมพ์มาตรฐานทางด้านซ้ายของคีย์ "1") ท้ายที่สุดชื่อฟิลด์อาจตรงกับ mysql คำหลัก แต่ถ้าเราใช้เครื่องหมายคำพูดย้อนกลับ MySQL จะเข้าใจทุกอย่างถูกต้อง:
SELECT * จาก `ตาราง` โดยที่ `date` = "2006-04-04"
คุณควรแยกความแตกต่างระหว่างเครื่องหมายคำพูดเหล่านี้ และไม่สับสนระหว่างเครื่องหมายคำพูดกับเครื่องหมายอื่นๆ คุณควรจำไว้ว่า backticks จะไม่ถูกหลบเลี่ยงด้วยเครื่องหมายทับ

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

SQL Injection เป็นวิธีการโจมตีของแฮ็กเกอร์เมื่อข้อมูลที่ถ่ายโอนไปยังสคริปต์ได้รับการแก้ไขในลักษณะที่แบบสอบถามที่สร้างในสคริปต์นี้เริ่มดำเนินการบางอย่างที่แตกต่างไปจากที่ตั้งใจไว้โดยสิ้นเชิง
กฎสำหรับการป้องกันการโจมตีดังกล่าวสามารถแบ่งออกเป็นสองจุด:
1. การทำงานกับข้อมูล
2. การทำงานกับการควบคุมแบบสอบถาม

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

จุดที่สองนั้นยากกว่ามากเนื่องจากไม่มีกฎสากลเดียวสำหรับข้อมูล - backticks จะไม่ปกป้องชื่อฟิลด์จากการถูกแฮ็กเกอร์แก้ไข ไม่สามารถใช้เครื่องหมายคำพูดเพื่อป้องกันชื่อตาราง คำสั่ง SQL พารามิเตอร์คำสั่ง LIMIT และคำสั่งอื่น ๆ
ดังนั้น กฎพื้นฐานเมื่อแทนที่องค์ประกอบควบคุมลงในแบบสอบถามคือ:
หากคุณต้องการแทรกคำสั่ง SQL หรือชื่อของเขตข้อมูล ฐานข้อมูล ตารางลงในแบบสอบถามแบบไดนามิก คุณไม่ควรแทรกคำสั่งเหล่านั้นลงในแบบสอบถามโดยตรงไม่ว่าในกรณีใด
ตัวเลือกทั้งหมดสำหรับการเพิ่มเติมดังกล่าวจะต้องเขียนเป็น ADVANCE ในสคริปต์ของคุณ และเลือกตามสิ่งที่ผู้ใช้ป้อน
ตัวอย่างเช่น หากคุณต้องการส่งชื่อฟิลด์ไปยังคำสั่งซื้อตามตัวดำเนินการ คุณไม่ควรแทนที่โดยตรงไม่ว่าในกรณีใดก็ตาม เราต้องตรวจสอบก่อน ตัวอย่างเช่น สร้างอาร์เรย์ของค่าที่ถูกต้อง และแทนที่ลงในคำขอเฉพาะเมื่อมีพารามิเตอร์ที่ส่งผ่านอยู่ในอาร์เรย์นี้:
$orders =array("name" , "price" , "qty" );
$key = array_search($_GET["sort"], $orders));
$orderby = $คำสั่งซื้อ [ $key ];
$แบบสอบถาม= "เลือก * จาก `ตาราง` เรียงลำดับตาม$สั่งโดย " ;

เราค้นหาอาร์เรย์ของตัวเลือกที่อธิบายไว้ล่วงหน้าสำหรับคำที่ผู้ใช้ป้อน และหากพบ เราจะเลือกองค์ประกอบที่เกี่ยวข้องของอาร์เรย์ หากไม่พบรายการที่ตรงกัน องค์ประกอบแรกของอาร์เรย์จะถูกเลือก
ดังนั้นสิ่งที่แทนที่ในคำขอจึงไม่ใช่สิ่งที่ผู้ใช้ป้อน แต่เป็นสิ่งที่เขียนไว้ในสคริปต์ของเรา
ต้องทำเช่นเดียวกันในกรณีอื่น ๆ ทั้งหมด
ตัวอย่างเช่น ถ้าส่วนคำสั่ง WHERE ถูกสร้างขึ้นแบบไดนามิก:
ถ้า (!empty($_GET [ "price" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "price" ]). """ ;
$query = "SELECT * จาก `ตาราง` โดยที่ $where " ;

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


ก่อนอื่น อธิบายเล็กน้อยว่าทำไมโดยทั่วไปจึงต้องใช้เครื่องหมายทับเหล่านี้
หากเราแทนที่ข้อมูลใดๆ ลงในแบบสอบถาม ดังนั้นเพื่อที่จะแยกแยะข้อมูลนี้จากคำสั่ง SQL จะต้องใส่เครื่องหมายคำพูดเพื่อแยกความแตกต่างระหว่างข้อมูลนี้
เช่น ถ้าคุณเขียน
SELECT * จากตาราง โดยที่ชื่อ = บิล
จากนั้นฐานข้อมูลจะตัดสินว่า Bill เป็นชื่อของฟิลด์อื่น ไม่พบ และจะส่งข้อผิดพลาด ดังนั้นข้อมูลที่ถูกแทนที่ (ในกรณีนี้คือชื่อ Bill) จะต้องอยู่ในเครื่องหมายคำพูด - จากนั้นฐานข้อมูลจะพิจารณาว่าเป็นสตริงซึ่งจะต้องกำหนดค่าให้กับฟิลด์ชื่อ:
SELECT * จากตาราง WHERE name = "Bill"
อย่างไรก็ตาม ราคาอาจปรากฏในข้อมูลด้วย เช่น,
SELECT * จากตารางโดยที่ name = "D"Artagnan"
ที่นี่ฐานข้อมูลจะตัดสินว่า "D" คือข้อมูล และ Artagnan เป็นคำสั่งที่ไม่ทราบ และจะทำให้เกิดข้อผิดพลาดด้วย ดังนั้นจึงจำเป็นต้องติดตามข้อมูลทั้งหมดเพื่ออธิบายให้ฐานข้อมูลทราบว่าเครื่องหมายคำพูด (และอักขระพิเศษอื่นๆ) ที่พบในนั้นอ้างอิงถึงข้อมูล
ด้วยเหตุนี้ เราจะได้รับคำขอที่ถูกต้องซึ่งจะไม่ทำให้เกิดข้อผิดพลาด:
SELECT * จากตาราง โดยที่ name = "D\"Artagnan"

ดังนั้นเราจึงพบว่าเมื่อทำการแทนที่ข้อมูลลงในแบบสอบถาม ควรปฏิบัติตามกฎสองข้อ:
- ข้อมูลทั้งหมดที่แทรกลงในคำขอจะต้องอยู่ในเครื่องหมายคำพูด (เดี่ยวหรือคู่ แต่ข้อมูลเดี่ยวจะสะดวกกว่าและใช้บ่อยกว่า)
- ในตัวแปรสตริงทั้งหมด จะต้องหลีกอักขระพิเศษด้วยเครื่องหมายทับ

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

ที่จริงแล้ว สิ่งที่กล่าวมาทั้งหมดใช้กับข้อมูลสตริงและวันที่ สามารถแทรกตัวเลขได้โดยไม่ต้องต่อท้ายหรือล้อมรอบด้วยเครื่องหมายคำพูด ถ้าคุณทำเช่นนี้แล้ว อย่างจำเป็น!บังคับข้อมูลให้เป็นประเภทที่ต้องการก่อนที่จะแทรกลงในแบบสอบถาม เช่น
$id = intval ($id);
อย่างไรก็ตาม เพื่อความเรียบง่าย (และความน่าเชื่อถือ) คุณสามารถทำงานกับตัวเลขได้เช่นเดียวกับสตริง (เนื่องจาก mysql ยังคงแปลงเป็นประเภทที่ต้องการ) ดังนั้น เราจะติดตามข้อมูลใด ๆ ที่แทรกลงในคำขอและใส่ไว้ในเครื่องหมายคำพูด

นอกจากนี้ยังมีกฎอีกข้อหนึ่ง - เป็นทางเลือก แต่ควรปฏิบัติตามเพื่อหลีกเลี่ยงข้อผิดพลาด:
ชื่อของฟิลด์และตารางควรอยู่ในเครื่องหมายคำพูดเดี่ยวด้านหลัง - "`" (คีย์ที่มีสัญลักษณ์นี้จะอยู่บนแป้นพิมพ์มาตรฐานทางด้านซ้ายของคีย์ "1") ท้ายที่สุดชื่อฟิลด์อาจตรงกับ mysql คำหลัก แต่ถ้าเราใช้เครื่องหมายคำพูดย้อนกลับ MySQL จะเข้าใจทุกอย่างถูกต้อง:
SELECT * จาก `ตาราง` โดยที่ `date` = "2006-04-04"
คุณควรแยกความแตกต่างระหว่างเครื่องหมายคำพูดเหล่านี้ และไม่สับสนระหว่างเครื่องหมายคำพูดกับเครื่องหมายอื่นๆ คุณควรจำไว้ว่า backticks จะไม่ถูกหลบเลี่ยงด้วยเครื่องหมายทับ

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

SQL Injection เป็นวิธีการโจมตีของแฮ็กเกอร์เมื่อข้อมูลที่ถ่ายโอนไปยังสคริปต์ได้รับการแก้ไขในลักษณะที่แบบสอบถามที่สร้างในสคริปต์นี้เริ่มดำเนินการบางอย่างที่แตกต่างไปจากที่ตั้งใจไว้โดยสิ้นเชิง
กฎสำหรับการป้องกันการโจมตีดังกล่าวสามารถแบ่งออกเป็นสองจุด:
1. การทำงานกับข้อมูล
2. การทำงานกับการควบคุมแบบสอบถาม

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

จุดที่สองนั้นยากกว่ามากเนื่องจากไม่มีกฎสากลเดียวสำหรับข้อมูล - backticks จะไม่ปกป้องชื่อฟิลด์จากการถูกแฮ็กเกอร์แก้ไข ไม่สามารถใช้เครื่องหมายคำพูดเพื่อป้องกันชื่อตาราง คำสั่ง SQL พารามิเตอร์คำสั่ง LIMIT และคำสั่งอื่น ๆ
ดังนั้นกฎพื้นฐานเมื่อแทนที่องค์ประกอบควบคุมลงในแบบสอบถามคือ:
หากคุณต้องการแทรกคำสั่ง SQL หรือชื่อของเขตข้อมูล ฐานข้อมูล ตารางลงในแบบสอบถามแบบไดนามิก คุณไม่ควรแทรกคำสั่งเหล่านั้นลงในแบบสอบถามโดยตรงไม่ว่าในกรณีใด
ตัวเลือกทั้งหมดสำหรับการเพิ่มเติมดังกล่าวจะต้องเขียนเป็น ADVANCE ในสคริปต์ของคุณ และเลือกตามสิ่งที่ผู้ใช้ป้อน
ตัวอย่างเช่น หากคุณต้องการส่งชื่อฟิลด์ไปยังคำสั่งซื้อตามตัวดำเนินการ คุณไม่ควรแทนที่โดยตรงไม่ว่าในกรณีใดก็ตาม เราต้องตรวจสอบก่อน ตัวอย่างเช่น สร้างอาร์เรย์ของค่าที่ถูกต้อง และแทนที่ลงในคำขอเฉพาะเมื่อมีพารามิเตอร์ที่ส่งผ่านอยู่ในอาร์เรย์นี้:
$orders =array("name" , "price" , "qty" );
$key = array_search($_GET["sort"], $orders));
$orderby = $คำสั่งซื้อ [ $key ];
$แบบสอบถาม= "เลือก * จาก `ตาราง` เรียงตาม $orderby";
เราค้นหาอาร์เรย์ของตัวเลือกที่อธิบายไว้ล่วงหน้าสำหรับคำที่ผู้ใช้ป้อน และหากพบ เราจะเลือกองค์ประกอบที่เกี่ยวข้องของอาร์เรย์ หากไม่พบรายการที่ตรงกัน องค์ประกอบแรกของอาร์เรย์จะถูกเลือก
ดังนั้นสิ่งที่แทนที่ในคำขอจึงไม่ใช่สิ่งที่ผู้ใช้ป้อน แต่เป็นสิ่งที่เขียนไว้ในสคริปต์ของเรา
ต้องทำเช่นเดียวกันในกรณีอื่น ๆ ทั้งหมด
ตัวอย่างเช่น ถ้าส่วนคำสั่ง WHERE ถูกสร้างขึ้นแบบไดนามิก:
ถ้า (!empty($_GET [ "price" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "price" ]). """ ;
$แบบสอบถาม= "เลือก * จาก `ตาราง` โดยที่ $ ที่ไหน";
เป็นเรื่องยากสำหรับฉันที่จะจินตนาการถึงกรณีที่ชื่อตารางสามารถแทรกลงในคิวรีแบบไดนามิกได้ แต่หากเกิดเหตุการณ์เช่นนี้ จะต้องแทรกชื่อจากชุดที่กำหนดไว้ล่วงหน้าในสคริปต์เท่านั้น
พารามิเตอร์ของตัวดำเนินการ LIMIT ควรถูกบังคับให้เป็นประเภทจำนวนเต็มโดยใช้การดำเนินการทางคณิตศาสตร์หรือฟังก์ชัน intval()
อย่าคิดว่าตัวอย่างที่แสดงไว้ที่นี่ใช้ตัวเลือกทั้งหมดสำหรับการสร้างคิวรีแบบไดนามิก คุณเพียงแค่ต้องเข้าใจหลักการและนำไปใช้ในกรณีดังกล่าวทั้งหมด

คุณสมบัติของการทำงานร่วมกับตัวดำเนินการ LIKE
กรณีที่แยกจากกันโดยสิ้นเชิงคือตัวดำเนินการ LIKE
ประการแรก นอกเหนือจากการติดตามตามปกติแล้ว เครื่องหมายทับจะต้องเพิ่มเป็นสองเท่าในตัวแปรที่แทนที่ใน LIKE นั่นคือหากตัวแปรมีอักขระ \ จะต้องเพิ่มเป็นสองเท่า จากนั้นจะต้องดำเนินการ Escape ตามปกติผ่าน mysql_real_escape_string
เช่น ถ้าเรากำลังมองหาสตริง
อักขระ \ เรียกว่า "แบ็กสแลช" และเราต้องการการจับคู่แบบตรงทั้งหมด จากนั้นเราก็ใช้ mysql_real_escape_string และการสืบค้นเป็นแบบมาตรฐาน:
SELECT * FROM test WHERE field = "สัญลักษณ์ \\ เรียกว่า \"แบ็กสแลช\""หากเราต้องการแทนที่สตริงนี้ใน LIKE ก่อนอื่นเราต้องแทนที่แต่ละเครื่องหมายทับด้วยสอง จากนั้นจึงใช้ mysql_real_escape_string ผลลัพธ์ที่ได้ก็จะเป็น
SELECT * จากตาราง โดยที่ฟิลด์ LIKE "% character \\\\ เรียกว่า \"backslash\"%"
สิ่งที่สองที่ควรทราบก็คือ ไม่มีฟังก์ชันใดที่เพิ่มเครื่องหมายทับเพิ่มลงในเมตาอักขระการค้นหา "%" และ "_" ที่ใช้ในตัวดำเนินการ LIKE ดังนั้น หากคุณใช้โอเปอเรเตอร์นี้และไม่ต้องการให้อักขระ _ และ % ใช้เป็นไวด์การ์ด ให้เพิ่มเครื่องหมายทับด้วยตนเอง ซึ่งสามารถทำได้ด้วยคำสั่ง
$data = addCslashes($data, "%_"); คำเตือน - นี่ไม่ใช่การติดขนตา! ชื่อฟังก์ชันนี้มี "c" พิเศษอยู่ในชื่อ

ดังนั้นปรากฎว่าเราต้องประมวลผลตัวแปรที่ใช้ในตัวดำเนินการ LIKE แยกกัน
ขั้นแรกให้แทนที่หนึ่งสแลชด้วยสอง โดยใช้ตัวอย่างโค้ดดังนี้:
$var = str_replace ("\\" , "\\\\" , $var ); จากนั้น (คุณสามารถติดตามข้อมูลอื่น ๆ ทั้งหมดในคำขอ):
$var = mysql_real_escape_string ($var);แล้วถ้าเราต้องการให้ _ และ % ตรงกันทุกประการ เราก็ทำ
$var = addCslashes($var, "_%");
ดังนั้น ถ้าเราค้นหา เช่น สำหรับสตริงต่อไปนี้
อักขระ \ เรียกว่า "แบ็กสแลช" และอักขระ _ เรียกว่า "ขีดล่าง"หลังจากประมวลผลแล้ว ในคำขอควรมีลักษณะดังนี้:
"%สัญลักษณ์ \\\\ เรียกว่า \"แบ็กสแลช\" และสัญลักษณ์ \_ เรียกว่า \"ขีดเส้นใต้\"นั่นคือเครื่องหมายสแลชซึ่งเดิมอยู่ในบรรทัดนั้นเพิ่มขึ้นสี่เท่า ตัวละครที่เหลือก็ติดตามตามปกติ บวก - มองเห็นอักขระขีดล่างได้

เกี่ยวกับเครื่องหมายทับ วิธีกำจัดพวกเขา
เครื่องหมายทับหรือแบ็กสแลชจากแบ็กสแลชภาษาอังกฤษคือแบ็กสแลช (“\”) ที่ปรากฏขึ้นในตัวแปรของคุณอย่างอธิบายไม่ถูก มันถูกเพิ่มเข้าไปในอักขระพิเศษบางตัว แต่ส่วนใหญ่จะสังเกตเห็นได้เนื่องจากเครื่องหมายคำพูด
สิ่งนี้เกิดขึ้นเนื่องจากการตั้งค่า PHP พิเศษ ซึ่งโดยปกติจะเปิดใช้งานตามค่าเริ่มต้นบนโฮสติ้ง ตามทฤษฎีแล้ว การตั้งค่าเหล่านี้สามารถเพิ่มความปลอดภัยของสคริปต์ที่ทำงานกับฐานข้อมูลได้ ในทางปฏิบัติ การเพิ่มเครื่องหมายทับโดยอัตโนมัติมักส่งผลให้เกิดความสับสนและความไม่สะดวก ทั้งเมื่อทำงานกับฐานข้อมูลและในกรณีที่ไม่มีฐานข้อมูล
ด้านล่างนี้เราจะตรวจสอบทั้งสองกรณีโดยละเอียด

คำสั่ง php.ini ซึ่งเรียกรวมกันว่า "ราคาวิเศษ" มีหน้าที่เพิ่มเครื่องหมายทับโดยอัตโนมัติ:
magic_quotes_gpc และ magic_quotes_runtime หากเปิดใช้งานอันแรก PHP จะเพิ่มเครื่องหมายทับให้กับข้อมูลที่มาจากผู้ใช้โดยอัตโนมัติ - จากคำขอ POST, GET และคุกกี้ (รวมถึงการเข้าสู่ระบบและรหัสผ่านที่ได้รับผ่านการอนุญาต HTTP)
หากเป็นอย่างที่สอง เครื่องหมายทับจะถูกเพิ่มลงในข้อมูลที่ได้รับระหว่างการเรียกใช้สคริปต์ - ตัวอย่างเช่นจากไฟล์หรือฐานข้อมูล

หากคุณทำงานโดยไม่มีฐานข้อมูลหรือทำงานกับฐานข้อมูลอย่างถูกต้อง (ซึ่งจะกล่าวถึงด้านล่าง) เครื่องหมายทับพิเศษจะรบกวนคุณเท่านั้น และคุณต้องกำจัดมันทิ้ง วิธีที่ง่ายและถูกต้องที่สุดคือปิดการใช้งานการเพิ่มอัตโนมัติในการตั้งค่า PHP
ซึ่งสามารถทำได้โดยการแก้ไขคำสั่งที่เกี่ยวข้องใน php.ini หากคุณสามารถเข้าถึงได้ หรือโดยการสร้างไฟล์ .htaccess ในไดเร็กทอรีสุดท้ายของไซต์และเพิ่มบรรทัดลงไป
php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0

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

วิธีที่ง่ายที่สุดในการทำความเข้าใจข้อมูลที่ได้รับระหว่างการดำเนินการคือการเขียนที่จุดเริ่มต้นของสคริปต์:
set_magic_quotes_runtime(0); สำหรับข้อมูลที่ได้รับจากผู้ใช้ทุกอย่างจะซับซ้อนกว่ามาก สำหรับโค้ดนี้เราต้องการสองฟังก์ชัน:

  • คุณสามารถตรวจสอบว่า PHP ได้เพิ่มเข้าไปหรือไม่โดยใช้ฟังก์ชัน get_magic_quotes_gpc
  • ฟังก์ชั่นแถบสแลชจะลบเครื่องหมายสแลชออก
    ดังนั้น คุณต้องตรวจสอบโดยใช้อันแรก และหากเพิ่ม PHP แล้ว ให้ตรวจสอบตัวแปรขาเข้าทั้งหมดและล้างมันโดยใช้อันที่สอง
    หากคุณทำงานอย่างถูกต้องด้วย register_globals = off ก็เพียงพอที่จะใช้แถบสแลชกับอาร์เรย์ทั้งหมดที่มีข้อมูลที่มาจากเบราว์เซอร์
    ตัวอย่างเช่น คุณสามารถรวมโค้ดต่อไปนี้ในสคริปต์ไซต์ทั้งหมด:
    แถบฟังก์ชั่น (& $el ) (
    ถ้า (is_array($el))
    foreach($el เป็น $k => $v )
    แถบ($el[$k]);
    อื่น $el = แถบสแลช ($el );
    }
    ถ้า (get_magic_quotes_gpc()) (
    แถบ($_GET);
    แถบ($_POST);
    แถบ ($_COOKIE);
    แถบ($_REQUEST);
    ถ้า (isset($_SERVER [ "PHP_AUTH_USER" ])) แถบ ($_SERVER [ "PHP_AUTH_USER" ]);
    ถ้า (isset($_SERVER [ "PHP_AUTH_PW" ])) แถบ ($_SERVER [ "PHP_AUTH_PW" ]);
    }
    ในกรณีที่การตั้งค่า register_globals ไม่ถูกต้องการค้นหาวิธีแก้ปัญหาที่ยอมรับได้จะเป็นเรื่องยากดังนั้นจึงเป็นการดีกว่า - ฉันขอย้ำ - ทำงานกับการตั้งค่าที่ถูกต้องทันที

    หมายเหตุ

    • สาเหตุหนึ่งที่คุณไม่ควรเชื่อถือ "คำพูดวิเศษ" เป็นอีกสาเหตุหนึ่ง ไม่น่าเป็นไปได้มาก แต่ก็ยังอยู่ "คำพูดวิเศษ" จริงๆ แล้วไม่ได้หมายถึงสองคำสั่ง แต่หมายถึงสามคำสั่ง ที่สามคือ magic_quotes_sybase ไม่เพียงแต่จะเพิ่มเครื่องหมายคำพูดแทนเครื่องหมายสแลชเท่านั้น แต่ยังยกเลิกเอฟเฟกต์ของ magic_quotes_gpc ด้วย หากโดยปาฏิหาริย์ทั้งสองคำสั่งนี้มีสถานะ "เปิด" แสดงว่าคำสั่งสุดท้าย จะไม่ทำงาน!นั่นคือโดยการใช้ "เครื่องหมายคำพูดวิเศษ" ในกรณีนี้ เราจะได้รับความพึงพอใจจากข้อความค้นหาที่เขียนไม่ถูกต้อง โดยทั่วไป ตามทฤษฎีแล้ว เราต้องคำนึงถึงการมีอยู่ของคำสั่งนี้ เนื่องจากมันยังสร้างความประหลาดใจ เช่น... การเปลี่ยนแปลงพฤติกรรมของฟังก์ชัน addslashes และ stripslashes! หาก magic_quotes_sybase = on ฟังก์ชันเหล่านี้จะเริ่มเพิ่มและลบเครื่องหมายคำพูดเดี่ยวแทนที่จะเป็นเครื่องหมายทับ ตามลำดับ
    • ตัวอย่างทั้งหมดที่เกี่ยวข้องกับฐานข้อมูล Mysql เท่านั้น กฎเฉพาะสำหรับการเขียนคำสั่งอาจแตกต่างกันสำหรับ DBMS อื่นๆ แต่หลักการทั่วไปยังคงเหมือนเดิม:
      • หาก API สำหรับการทำงานกับฐานข้อมูลหรือไลบรารีบุคคลที่สามมีให้ ฟังก์ชั่นพิเศษสำหรับการเขียนแบบสอบถามและมีความเป็นไปได้ที่จะใช้มันคุณต้องใช้มันก่อน
      • หากไม่มีฟังก์ชันดังกล่าว คุณควรดูในเอกสารประกอบสำหรับฟังก์ชันสำหรับการ Escape อักขระพิเศษสำหรับ DBMS นี้
    หมายเหตุ: แบบฟอร์ม
    เมื่อแสดงค่าในแท็กอินพุตของแบบฟอร์ม เครื่องหมายทับไม่ได้ช่วยอะไร
    หากต้องการแสดงข้อความทั้งหมดในช่องดังกล่าว ค่าจะต้องอยู่ในเครื่องหมายคำพูดและไปยังข้อมูลที่ส่งออก ใช้ฟังก์ชัน htmlspecialchars()
    ตัวอย่าง:

    นอกจากนี้ ควรสังเกตด้วย (แม้ว่าจะไม่เกี่ยวข้องกับเครื่องหมายคำพูดและเครื่องหมายทับก็ตาม) ว่าควรใช้ฟังก์ชัน htmlspecialchars เมื่อส่งออกไปยังเบราว์เซอร์โดยทั่วไปกับข้อมูลทั้งหมดที่ได้รับจากผู้ใช้ที่ไม่ได้รับการยืนยัน เหตุใดจึงควรทำเช่นนี้ คุณสามารถอ่านบน Google ได้ตามคำขอว่าช่องโหว่ XSS คืออะไร
    โดย phpfaq.ru
  • เนื่องจากลักษณะงานของฉัน ฉันจึงต้องดำเนินการตรวจสอบความปลอดภัยของซอร์สโค้ดของเว็บแอปพลิเคชัน
    เว็บแอปพลิเคชั่นมากมายและโค้ดมากมาย...

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

    เอาล่ะ มาเริ่มกันเลย

    ตัวละครที่ไร้ประโยชน์หลบหนี
    พบใน 83% ของเว็บแอปพลิเคชัน PHP ที่เสี่ยงต่อการถูกแทรก SQL
    การใช้ฟังก์ชัน Escape สำหรับอักขระเช่น
    mysql_escape_string
    mysql_real_escape_string
    เพิ่มสแลช
    โดยไม่มีเครื่องหมายคำพูด ส่วนใหญ่มักปรากฏในพารามิเตอร์ตัวเลข (*_id ทุกชนิด)
    ตัวอย่าง
    $sql = "เลือกผู้ใช้จากรายการผู้ใช้ WHERE userid=".mysql_real_escape_string($_GET["uid"]);

    นี่ดูเหมือนจะเป็นรหัสที่ปลอดภัย แต่อยู่เพียงผิวเผินเท่านั้น รูปแบบการฉีด SQL ที่พบบ่อยที่สุดใน PHP ในทางปฏิบัติของฉันพุ่งเข้ามาที่นี่ ในการโจมตีช่องโหว่นี้ ผู้โจมตีจำเป็นต้องหลีกเลี่ยงการใช้อักขระ " " \x00 \r \n \x1a ในเวกเตอร์การโจมตี
    ตัวอย่างเช่น:
    /index.php?uid=-777 UNION เลือกรหัสผ่านจากรายการผู้ใช้

    ค้นหาในรหัส
    ซับซ้อนด้วยความหมายของภาษา สำหรับการค้นหาแบบง่ายคุณสามารถใช้ egrep:
    egrep -Rin "(select|update|insert|delete|replace).*(from|set|into).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . - grep -v "[\""]["\"]"

    ตรรกะของนิพจน์การค้นหามีดังนี้: ค้นหาบรรทัดทั้งหมดที่ไม่มีลำดับอักขระเครื่องหมายคำพูด ("", "", "", "") ทางด้านซ้ายของฟังก์ชันการกรอง แน่นอนว่าวิธีนี้ยังห่างไกลจาก 100% แต่เป็นไปไม่ได้ที่จะต้องใช้นิพจน์ทั่วไปเพื่อทำการวิเคราะห์เชิงความหมาย
    เพื่อให้ง่ายต่อการแสดงข้อมูล คุณสามารถเน้นฟังก์ชั่นเป็นสีในคอนโซลได้:
    egrep -Rin "(select|update|insert|delete|replace).*(from|set|into).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . - grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

    เพื่อป้องกันช่องโหว่ไวด์การ์ด ควรใช้การคัดเลือกประเภท
    ซึ่งทำงานได้เร็วกว่าและเชื่อถือได้มากกว่าการกรองและการคัดกรองทุกประเภท
    สำหรับตัวอย่างข้างต้น แพตช์อาจเป็นดังนี้:
    $sql = "เลือกผู้ใช้จากรายชื่อผู้ใช้ WHERE userid=".intval($_GET["uid"]);

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

    (PHP 4 >= 4.3.0, PHP 5)

    mysql_real_escape_string — หลีกอักขระพิเศษในสตริงเพื่อใช้ในคำสั่ง SQL

    คำอธิบาย

    mysql_real_escape_string (สตริง $unescaped_string [, ทรัพยากร $link_identifier = NULL]): สตริง

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

    mysql_real_escape_string() เรียกฟังก์ชันไลบรารีของ MySQL mysql_real_escape_string ซึ่งใส่แบ็กสแลชไว้หน้าอักขระต่อไปนี้: \x00, \n, \r, \ , " , " และ \x1ก.

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

    คำเตือน

    ความปลอดภัย: ชุดอักขระเริ่มต้น

    ชุดอักขระจะต้องตั้งค่าที่ระดับเซิร์ฟเวอร์หรือด้วยฟังก์ชัน API mysql_set_charset()เพื่อให้มันมีผลกระทบ mysql_real_escape_string() - ดูส่วนแนวคิดเกี่ยวกับชุดอักขระสำหรับข้อมูลเพิ่มเติม

    พารามิเตอร์

    unscaped_string

    สตริงที่จะหลบหนี

    Link_identifier

    การเชื่อมต่อ MySQL หากไม่ได้ระบุตัวระบุลิงก์ ลิงก์สุดท้ายจะเปิดโดย mysql_connect()ถือว่า หากไม่พบลิงก์ดังกล่าว ระบบจะพยายามสร้างลิงก์ดังกล่าวขึ้นมา mysql_connect()ถูกเรียกมาโดยไม่มีข้อโต้แย้ง หากไม่พบหรือสร้างการเชื่อมต่อ a E_คำเตือนเกิดข้อผิดพลาดระดับ

    ส่งกลับค่า

    ส่งกลับสตริงที่หลบหนีหรือ เท็จเมื่อเกิดข้อผิดพลาด

    ข้อผิดพลาด/ข้อยกเว้น

    การเรียกใช้ฟังก์ชันนี้โดยไม่มีการเชื่อมต่อ MySQL จะส่งเสียงออกมาเช่นกัน E_คำเตือนข้อผิดพลาด PHP ระดับ รันฟังก์ชันนี้เมื่อมีการเชื่อมต่อ MySQL ที่ถูกต้องเท่านั้น

    ตัวอย่าง

    ตัวอย่าง #1 ง่าย mysql_real_escape_string() ตัวอย่าง

    //เชื่อมต่อ
    $link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password" )
    หรือตาย(mysql_error());

    //สอบถาม
    $query = วิ่ง ( "เลือก * จากผู้ใช้โดยที่ผู้ใช้ = "%s" และรหัสผ่าน = "%s"",
    mysql_real_escape_string($ผู้ใช้)
    mysql_real_escape_string($รหัสผ่าน));
    ?>

    ตัวอย่าง #2 mysql_real_escape_string() ต้องมีตัวอย่างการเชื่อมต่อ

    ตัวอย่างนี้แสดงให้เห็นว่าจะเกิดอะไรขึ้นหากไม่มีการเชื่อมต่อ MySQL เมื่อเรียกใช้ฟังก์ชันนี้

    ตัวอย่างข้างต้นจะแสดงผลลัพธ์ที่คล้ายกับ:

    คำเตือน: mysql_real_escape_string(): ไม่มีไฟล์หรือไดเร็กทอรีดังกล่าวใน /this/test/script.php ออนไลน์ 5 คำเตือน: mysql_real_escape_string(): ไม่สามารถสร้างลิงก์ไปยังเซิร์ฟเวอร์ใน /this/test/script.php ออนไลน์ 5 bool(false) string(41) "SELECT * FROM Actors WHERE Last_name = """

    ตัวอย่างที่ 3 ตัวอย่างการโจมตีแบบฉีด SQL

    // เราไม่ได้ตรวจสอบ $_POST["password"] ซึ่งอาจเป็นสิ่งที่ผู้ใช้ต้องการก็ได้ เช่น:
    $_POST [ "ชื่อผู้ใช้" ] = "ไอดาน" ;
    $_POST [ "รหัสผ่าน" ] = "" หรือ ""="" ;

    // สืบค้นฐานข้อมูลเพื่อตรวจสอบว่ามีผู้ใช้ที่ตรงกันหรือไม่
    $query = ( $_POST [ "ชื่อผู้ใช้" ]) " และ รหัสผ่าน=" ( $_POST [ "รหัสผ่าน" ]) "" ;
    mysql_query($แบบสอบถาม);

    // ซึ่งหมายความว่าแบบสอบถามที่ส่งไปยัง MySQL จะเป็น:
    สะท้อน $ แบบสอบถาม;
    ?>

    แบบสอบถามที่ส่งไปยัง MySQL:

    ซึ่งจะทำให้ทุกคนสามารถเข้าสู่ระบบได้โดยไม่ต้องใช้รหัสผ่านที่ถูกต้อง

    หมายเหตุ

    จำเป็นต้องมีการเชื่อมต่อ MySQL ก่อนใช้งาน mysql_real_escape_string() มิฉะนั้นจะเป็นข้อผิดพลาดของระดับ E_คำเตือนถูกสร้างขึ้นและ เท็จจะถูกส่งกลับ หากไม่ได้กำหนด link_identifier การเชื่อมต่อ MySQL สุดท้ายจะถูกนำมาใช้

    บันทึก: mysql_real_escape_string() ไม่หนี % และ _ - สิ่งเหล่านี้คือไวด์การ์ดใน MySQL หากรวมกับ ชอบ, ยินยอม, หรือ ถอน.

    8 ปีที่แล้ว

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

    ฟังก์ชั่น mysql_escape_mimic ($inp) (
    ถ้า(is_array($inp))
    กลับ array_map (__วิธีการ__ , $inp );

    ถ้า(!empty($inp ) && is_string ($inp )) (
    กลับ str_replace (array("\\" , "\0" , "\n" , "\r" , """ , """ , "\x1a" ), array("\\\\" , "\ \0" , "\\n" , "\\r" , "\\"" , "\\"" , "\\Z" ), $inp );
    }

    กลับ $inp ;
    }
    ?>

    13 ปีที่แล้ว

    โปรดทราบว่า mysql_real_escape_string ไม่ได้เติมแบ็กสแลชไว้ข้างหน้า \x00, \n, \r และ และ \x1a ตามที่กล่าวไว้ในเอกสารประกอบ แต่จริงๆ แล้วแทนที่อักขระด้วยการแสดง MySQL ที่ยอมรับได้สำหรับการสืบค้น (เช่น \n ถูกแทนที่ด้วย "\ n" litteral) (\, ", และ " ถูกหลีกตามที่บันทึกไว้) นี่ไม่ได้เปลี่ยนวิธีใช้ฟังก์ชันนี้ แต่ฉันคิดว่ามันดีที่ได้รู้

    6 ปีที่แล้ว

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

    ดังนั้น แทนที่จะใช้ฟังก์ชันที่เสียหายอย่างมากนี้ ให้ใช้คำสั่งที่เตรียมไว้แบบพาราเมตริกแทน

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

    นั่นหมายความว่าอย่างไร?

    หมายความว่าแทนที่จะสร้างคำสั่ง SQL เช่นนี้:

    "ใส่ค่า X (A)(".$_POST["a"].)"

    คุณควรใช้ฟังก์ชันเตรียม () ของ mysqli () เพื่อดำเนินการคำสั่งที่มีลักษณะดังนี้:

    "แทรกลงในค่า X (A) (?)"

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

    ตัวอย่างเช่น หากคุณกำลังสร้างเฟรมเวิร์กเล็กๆ และต้องการแทรกลงในตารางตาม URI คำขอ คุณประโยชน์สูงสุดที่จะไม่รับค่า $_SERVER["REQUEST_URI"] (หรือค่าใดๆ ก็ตาม บางส่วน) และเชื่อมโยงสิ่งนั้นเข้ากับแบบสอบถามของคุณโดยตรง คุณควรแยกวิเคราะห์ส่วนของค่า $_SERVER["REQUEST_URI"] ที่คุณต้องการและแมปสิ่งนั้นผ่านฟังก์ชันหรืออาเรย์ที่เชื่อมโยงบางประเภทกับผู้ที่ไม่ใช่ผู้ใช้ ค่าที่ระบุ หากการแมปไม่มีค่า คุณจะรู้ว่ามีบางอย่างผิดปกติกับข้อมูลที่ผู้ใช้ให้ไว้

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

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

    9 ปีที่แล้ว

    มีนิสัยแปลกๆ ที่น่าสนใจในตัวอย่าง #2 เกี่ยวกับการแทรก SQL: AND มีลำดับความสำคัญมากกว่า OR ดังนั้นคิวรีที่ฉีดจะดำเนินการจริงในฐานะ WHERE (user="aidan" และรหัสผ่าน="") OR ""="" ดังนั้นแทน ในการส่งคืนบันทึกฐานข้อมูลที่สอดคล้องกับชื่อผู้ใช้ที่กำหนดเอง (ในกรณีนี้คือ "aidan") จริงๆ แล้วมันจะส่งคืนบันทึกฐานข้อมูลทั้งหมดโดยไม่มีลำดับใดเป็นพิเศษ ดังนั้นผู้โจมตีอาจสามารถเข้าสู่ระบบด้วยบัญชีใดๆ ได้ แต่ไม่จำเป็นต้องเข้าสู่ระบบด้วยบัญชีใดๆ . ควบคุมว่าเป็นบัญชีใด

    แน่นอนว่าผู้ที่มีศักยภาพในการโจมตีสามารถปรับเปลี่ยนพารามิเตอร์เพื่อกำหนดเป้าหมายผู้ใช้ที่สนใจ:

    //เช่น. ค่านิยมของผู้โจมตี
    $_POST [ "ชื่อผู้ใช้" ] = "" ;
    $_POST["รหัสผ่าน"] = "" หรือผู้ใช้ = "ผู้ดูแลระบบ" และ "" = "";

    // แบบสอบถามมีรูปแบบไม่ถูกต้อง
    $แบบสอบถาม= "เลือก * จากผู้ใช้โดยที่ผู้ใช้ ="$_POST [ ชื่อผู้ใช้ ] " และ รหัสผ่าน = $_POST [ รหัสผ่าน ] "" ;

    สะท้อน $ แบบสอบถาม;

    // แบบสอบถามที่ส่งไปยัง MySQL จะอ่าน:
    // SELECT * จากผู้ใช้ WHERE user="" ANDpassword="" OR user="administrator" AND ""="";
    // ซึ่งจะอนุญาตให้ใครก็ตามสามารถเข้าถึงบัญชีชื่อ "ผู้ดูแลระบบ"

    ?>

    1 ปีที่ผ่านมา

    @feedr
    ฉันอธิบายบันทึกของเขาอย่างละเอียดดังต่อไปนี้:
    $string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
    $array1 = array("\\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
    $array2 = array("\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\"", "\\\Z");
    เสียงสะท้อน($สตริง);
    เสียงสะท้อน (PHP_EOL);
    สำหรับ($i=0; $i ถ้า ($i==0)
    $p = "/(?อื่น
    $p = "/(?เสียงสะท้อน($i);
    เสียงสะท้อน($p);
    เสียงสะท้อน($array2[$i]);
    $string = preg_replace($p, $array2[$i], $string);
    เสียงก้อง("\t");
    เสียงสะท้อน($สตริง);
    เสียงสะท้อน (PHP_EOL);
    }
    เสียงสะท้อน (PHP_EOL);
    เสียงสะท้อน($สตริง);

    2 ปีที่แล้ว

    อ้างคำพูดของแซมที่ Numb Safari

    [ "การอภิปรายเรื่องการ Escape จะไม่สมบูรณ์หากไม่ได้บอกทุกคนว่าโดยพื้นฐานแล้วคุณไม่ควรใช้อินพุตภายนอกเพื่อสร้างโค้ดที่ตีความ สิ่งนี้ใช้ได้กับคำสั่ง SQL หรืออะไรก็ตามที่คุณจะเรียกใช้ฟังก์ชัน "eval" ทุกประเภท

    ดังนั้น แทนที่จะใช้ฟังก์ชันที่เสียหายอย่างมากนี้ ให้ใช้คำสั่งที่เตรียมไว้แบบพาราเมตริกแทน

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

    แซมพูดถูก........

    อย่างไรก็ตาม ฉันไม่คิดว่ามันสมเหตุสมผลที่จะหยุดการฆ่าเชื้อทั้งหมดและส่งต่องานไปยังข้อความที่เตรียมด้วยพารามิเตอร์

    นักพัฒนาเฉพาะที่ทำงานในสถานการณ์เฉพาะจะรู้มากขึ้นเกี่ยวกับข้อมูลที่ถูกต้อง (เฉพาะบริบทนั้น)

    หากคุณขอให้ผู้ใช้ส่งผ่านค่าที่คุณให้ไว้แล้วและคุณรู้ว่าค่าดังกล่าวทั้งหมดเริ่มต้น AB****** และสตริงควรมีความยาว 7 หรือ 11 แต่ไม่เคยมีความยาวอื่นใดเลย พื้นฐานของการฆ่าเชื้อล่วงหน้าที่ดี - ความยาวที่แตกต่างกันที่อนุญาตของสตริงอาจบ่งบอกถึงข้อมูลเดิม

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

    ด้วยวิธีนี้ DB ของฉันจะไม่อุดตันด้วยข้อความที่ไม่ปลอดภัยที่ปลอดภัย - มันก็ไม่อุดตันซึ่งจะดีกว่า

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

    นอกจากนี้เท่าที่ฉันสามารถอ่านในเอกสารอย่างเป็นทางการได้
    ==============================================

    "การหลบหนีและการฉีด SQL

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

    นั่นบอกผมว่าอันตรายภายในนั้นถูกหลีกเลี่ยงโดยการจัดการแบบอื่น ไม่ใช่การทำให้เป็นโมฆะ

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

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

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

    1. คำชี้แจงที่เตรียมไว้
    2. ใช้ _real_escape_string()
    3. ไม่ใช้เครื่องหมายอัญประกาศวิเศษเพราะมันสร้างความสับสนให้กับฐานข้อมูลและจบลงด้วยการให้ข้อมูลเช่น "คุณไม่ได้เรียกมันว่า..."

    ทั้งหมดนี้ยอดเยี่ยมมากและฉันก็คอยจับตาดูมันอยู่ อย่างไรก็ตาม ฉันสงสัยว่าตัวละครอย่างเครื่องหมายดอลลาร์ [$] เครื่องหมายเปอร์เซ็นต์ [%] และบางทีตัวละครอื่นๆ ควรหลบหนีออกไปหรือไม่ แบบสอบถามอาจตีความเครื่องหมายดอลลาร์ว่าเป็นตัวแปร PHP ได้หรือไม่ แล้วไวยากรณ์ LIKE ที่ฉันได้ยินมาใช้สัญลักษณ์ % หรือแม้แต่ไวด์การ์ดล่ะ ข้อความที่เตรียมไว้ควรดูแลเรื่องทั้งหมดนี้ในทางเทคนิค แต่ฉันแค่อยากจะรักษาความปลอดภัยและให้แน่ใจว่าฉันได้ทำทุกอย่างอย่างเหมาะสม ในกรณีที่ฉันลืมใช้ข้อความที่เตรียมไว้หรือละเลยฉันหวังว่าแนวป้องกันที่สองนี้จะบอกฉันว่าฉันสามารถหายวิงเวียนศีรษะได้

    นี่คือสิ่งที่ฉันใช้ในการหลบหนี:

    ฟังก์ชั่น Escape($connection, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecialchars ($new_data, ENT_NOQUOTES); ส่งคืน $new_data;

    แล้วนี่ถูกต้องไหม? ฉันกำลังทำอะไรผิดมหันต์หรือเปล่า? โปรดทราบว่าเมื่อส่งคืนข้อมูลฐานข้อมูล ฉันจะต้องลบแบ็กสแลชที่อยู่หน้าอักขระ $,% และ _

    ฉันกำลังทำอะไรผิดมหันต์หรือเปล่า?

    ก่อนอื่นเกี่ยวกับการวิจัยของคุณ

    แถลงการณ์ที่เตรียมไว้ – เพียงผู้เดียว, เพียงคนเดียวสิ่งมหัศจรรย์ที่คุณพบ

    แม้ว่าการใช้ mysqli_real_escape_string (สมมติว่าคุณกำลังใช้คำสั่งที่เตรียมไว้) ก็จะเป็นเช่นนั้น ไร้ประโยชน์และเป็นอันตราย(สร้างผลลัพธ์ที่คุณสังเกตตัวเอง: "คุณเรียกว่าไม่ใช่\t...")

    และ Magic Quotes ก็ถูกลบออกจากภาษาไปนานแล้ว - ดังนั้นจึงไม่คุ้มค่าอะไรเลย

    ดังนั้นแม้แต่สถานที่เริ่มแรกของคุณส่วนใหญ่ยังผิดอย่างชัดเจน

    ตอนนี้ถึงคำถามของคุณ

    แบบสอบถามอาจตีความเครื่องหมายดอลลาร์ว่าเป็นตัวแปร PHP ได้หรือไม่

    แล้วไวยากรณ์ LIKE ที่ฉันได้ยินมาใช้สัญลักษณ์ % หรือแม้แต่ไวด์การ์ดล่ะ

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

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

    ข้อความที่เตรียมไว้ควรดูแลเรื่องทั้งหมดนี้ในทางเทคนิค

    ข้อความที่เตรียมไว้ไม่เกี่ยวข้องกับเครื่องหมาย $ หรือ % คำสั่งที่เตรียมไว้อ้างถึงการแทรก SQL แต่ไม่มีอักขระใดที่สามารถทำให้เกิดสิ่งนี้ได้ (คุณไม่สามารถเรียกว่า "การฉีด" เป็นตัวดำเนินการ LIKE ที่ใช้อย่างถูกต้องใช่ไหม)

    สุดท้ายก็ถึงส่วนที่แย่ที่สุด

    ในกรณีที่คุณลืมใช้ข้อความที่เตรียมไว้หรือละเลยที่จะปฏิบัติตาม

    ไม่มีอะไรจะช่วยคุณได้

    และความช่วยเหลือน้อยที่สุดก็จะมาจากฟังก์ชันที่คุณพัฒนาขึ้น

    สรุป.

    1. กำจัดคุณสมบัตินี้
    2. ใช้ ฟิลเลอร์ *เพื่อแสดงตัวแปรแต่ละตัวในแบบสอบถาม
    3. ยกเว้นอักขระ % และ _ ในอินพุตเฉพาะในกรณีที่จะใช้ในตัวดำเนินการ LIKE และคุณไม่ต้องการให้ตีความอักขระเหล่านั้น
    4. ใช้ htmlspecialchars() สำหรับเอาต์พุต ไม่ใช่อินพุต mysql

    *อ่านข้อความที่เตรียมไว้หากคำนี้ไม่คุ้นเคยกับคุณ

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

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

    คุณไม่จำเป็นต้องใช้ htmlspecialchars เมื่อเข้าถึงฐานข้อมูล ควรใช้เมื่อแสดงข้อมูลต่อผู้ใช้ในหน้า HTML เท่านั้น เพื่อป้องกันการแทรก XSS

    ขึ้นอยู่กับว่าข้อมูลอะไรและใช้เพื่ออะไร

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

    ตัวอย่างการแทรกคำสั่งในคลาสนี้

    $data = Array ("login" => "admin", "active" => true, "firstName" => "John", "lastName" => "Doe", "password" => $db->func( "SHA1(?)",Array ("รหัสผ่านลับ+เกลือ")), // รหัสผ่าน = SHA1("รหัสผ่านลับ+เกลือ") "createdAt" => $db->now(), // createAt = NOW() " หมดอายุ" => $db->ตอนนี้("+1Y") // หมดอายุ = NOW() + ช่วงเวลา 1 ปี // ช่วงเวลาที่สนับสนุน [s]econd, [m]inute, [h]hour, [d]day, [เดือนปี); $id = $db->insert("ผู้ใช้", $data); if ($id) echo "สร้างผู้ใช้แล้ว Id=" . $รหัส; อย่างอื่นสะท้อน "การแทรกล้มเหลว: " $db->getLastError();