PHP MySQL งบที่เตรียมไว้
คำสั่งที่เตรียมไว้มีประโยชน์มากในการต่อต้านการฉีด SQL
งบที่เตรียมไว้และพารามิเตอร์ที่ถูกผูกไว้
คำสั่งที่เตรียมไว้เป็นคุณลักษณะที่ใช้ในการดำเนินการคำสั่ง SQL เดียวกัน (หรือคล้ายกัน) ซ้ำๆ อย่างมีประสิทธิภาพ
คำสั่งที่เตรียมไว้โดยทั่วไปทำงานดังนี้:
- การจัดเตรียม: เทมเพลตคำสั่ง SQL จะถูกสร้างขึ้นและส่งไปยังฐานข้อมูล ค่าบางค่าไม่ระบุ เรียกว่าพารามิเตอร์ (มีป้ายกำกับ "?") ตัวอย่าง: INSERT INTO MyGuests VALUES(?, ?, ?)
- ฐานข้อมูลจะแยกวิเคราะห์ คอมไพล์ และดำเนินการปรับคิวรีให้เหมาะสมบนเท็มเพลตคำสั่ง SQL และเก็บผลลัพธ์โดยไม่ต้องดำเนินการ
- ดำเนินการ: ในภายหลัง แอปพลิเคชันจะผูกค่ากับพารามิเตอร์ และฐานข้อมูลจะดำเนินการคำสั่ง แอปพลิเคชันอาจดำเนินการคำสั่งได้หลายครั้งตามที่ต้องการด้วยค่าต่างๆ
เมื่อเทียบกับการดำเนินการคำสั่ง SQL โดยตรง คำสั่งที่เตรียมไว้มีข้อดีหลักสามประการ:
- คำสั่งที่เตรียมไว้ช่วยลดเวลาในการแยกวิเคราะห์เนื่องจากการเตรียมการในแบบสอบถามทำได้เพียงครั้งเดียว (แม้ว่าคำสั่งจะดำเนินการหลายครั้ง)
- พารามิเตอร์ที่ถูกผูกไว้จะลดแบนด์วิดท์ไปยังเซิร์ฟเวอร์ให้เหลือน้อยที่สุด เนื่องจากคุณต้องการส่งเฉพาะพารามิเตอร์ในแต่ละครั้ง ไม่ใช่การสืบค้นทั้งหมด
- คำสั่งที่เตรียมไว้มีประโยชน์มากในการต่อต้านการฉีด SQL เนื่องจากค่าพารามิเตอร์ซึ่งถูกส่งในภายหลังโดยใช้โปรโตคอลอื่น ไม่จำเป็นต้องหลีกเลี่ยงอย่างถูกต้อง ถ้าแม่แบบคำสั่งเดิมไม่ได้มาจากอินพุตภายนอก การฉีด SQL จะไม่เกิดขึ้น
คำสั่งที่เตรียมไว้ใน MySQLi
ตัวอย่างต่อไปนี้ใช้คำสั่งที่เตรียมไว้และพารามิเตอร์ที่ถูกผูกไว้ใน MySQLi:
ตัวอย่าง (MySQLi พร้อมคำสั่งที่เตรียมไว้)
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// prepare and bind
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);
// set parameters and execute
$firstname = "John";
$lastname = "Doe";
$email = "[email protected]";
$stmt->execute();
$firstname = "Mary";
$lastname = "Moe";
$email = "[email protected]";
$stmt->execute();
$firstname = "Julie";
$lastname = "Dooley";
$email = "[email protected]";
$stmt->execute();
echo "New records created successfully";
$stmt->close();
$conn->close();
?>
บรรทัดโค้ดเพื่ออธิบายจากตัวอย่างด้านบน:
"INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)"
ใน SQL ของเรา เราแทรกเครื่องหมายคำถาม (?) โดยเราต้องการแทนที่ด้วยค่าจำนวนเต็ม สตริง ค่าสองเท่าหรือค่าหยด
จากนั้น ให้ดูที่ฟังก์ชัน bind_param() :
$stmt->bind_param("sss", $firstname, $lastname, $email);
ฟังก์ชันนี้ผูกพารามิเตอร์กับแบบสอบถาม SQL และบอกฐานข้อมูลว่าพารามิเตอร์คืออะไร อาร์กิวเมนต์ "sss" แสดงรายการประเภทของข้อมูลที่เป็นพารามิเตอร์ อักขระ s บอก mysql ว่าพารามิเตอร์เป็นสตริง
อาร์กิวเมนต์อาจเป็นหนึ่งในสี่ประเภท:
- ฉัน - จำนวนเต็ม
- d - สองเท่า
- เอส - สตริง
- b - BLOB
เราต้องมีอย่างใดอย่างหนึ่งสำหรับแต่ละพารามิเตอร์
ด้วยการบอก mysql ว่าคาดหวังข้อมูลประเภทใด เราลดความเสี่ยงของการฉีด SQL
หมายเหตุ:หากเราต้องการแทรกข้อมูลใดๆ จากแหล่งภายนอก (เช่น การป้อนข้อมูลของผู้ใช้) สิ่งสำคัญคือต้องล้างข้อมูลและตรวจสอบความถูกต้อง
งบที่เตรียมไว้ใน PDO
ตัวอย่างต่อไปนี้ใช้คำสั่งที่เตรียมไว้และพารามิเตอร์ที่ถูกผูกไว้ใน PDO:
ตัวอย่าง (PDO พร้อมงบที่เตรียมไว้)
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// prepare sql and bind parameters
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email)
VALUES (:firstname, :lastname, :email)");
$stmt->bindParam(':firstname', $firstname);
$stmt->bindParam(':lastname', $lastname);
$stmt->bindParam(':email', $email);
// insert a row
$firstname = "John";
$lastname = "Doe";
$email = "[email protected]";
$stmt->execute();
// insert another row
$firstname = "Mary";
$lastname = "Moe";
$email = "[email protected]";
$stmt->execute();
// insert another row
$firstname = "Julie";
$lastname = "Dooley";
$email = "[email protected]";
$stmt->execute();
echo "New records created successfully";
} catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
$conn = null;
?>