آموزش Prepared Statement در PHP: امنسازی دیتابیس در برابر SQL Injection
حمله SQL Injection چیست و چرا دیتابیس ما را تهدید میکند؟
یکی از رایجترین و خطرناکترین آسیبپذیریهای امنیتی در وب، حمله تزریق دیتابیس یا SQL Injection (SQLi) است. این حمله زمانی رخ میدهد که ورودی کاربر مستقیماً و بدون فیلترسازی در ساختار کوئریهای SQL قرار گیرد. در این حالت، نفوذگر با وارد کردن دستورات مخرب SQL میتواند منطق کوئری شما را تغییر داده، به اطلاعات حساس (مانند رمزهای عبور) دسترسی پیدا کرده و یا حتی کل دیتابیس را حذف کند.
گوگل و نهادهای امنیتی معتبری مانند OWASP، استفاده از عبارات آماده یا Prepared Statements را به عنوان مطمئنترین و استانداردترین راهکار برای مقابله با این آسیبپذیری معرفی کردهاند.
Prepared Statement چیست و چگونه کار میکند؟
در روشهای سنتی، ساختار کوئری و دادههای ورودی با هم ترکیب شده و یکجا به دیتابیس ارسال میشوند. اما در Prepared Statement، این فرآیند به دو مرحله مجزا تقسیم میشود:
- مرحله آمادهسازی (Prepare): قالب کوئری SQL (بدون دادههای واقعی کاربر) به دیتابیس فرستاده میشود. دیتابیس ساختار کوئری را آنالیز و بهینهسازی میکند.
- مرحله ارسال داده و اجرا (Execute): دادههای کاربر به عنوان پارامترهای مجزا به سمت دیتابیس ارسال شده و به قالب متصل میشوند. در این حالت، دیتابیس به دادهها صرفاً به عنوان یک «مقدار متنی خام» نگاه میکند و هیچ دستور SQLی را درون ورودی کاربر اجرا نخواهد کرد.
آموزش استفاده از Prepared Statement با استفاده از PDO (توصیه شده)
کتابخانه PDO (PHP Data Objects) بهترین و استانداردترین روش برای اتصال به دیتابیس در PHP است چرا که از دیتابیسهای مختلف پشتیبانی میکند و امنیت بالایی دارد.
در ادامه یک نمونه کد کامل برای اجرای کوئری امن با PDO آورده شده است:
try {
// 1. اتصال امن به دیتابیس به وسیله PDO
$dsn = "mysql:host=localhost;dbname=my_secure_db;charset=utf8mb4";
$username = "root";
$password = "";
$options = [
PDO::ATTR_ERRMODE =--> PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // خاموش کردن شبیه سازی برای امنیت واقعی
];
$pdo = new PDO($dsn, $username, $password, $options);
// ۲. تعریف کوئری با استفاده از Placeholders (:username)
$sql = "SELECT id, email, password FROM users WHERE username = :username_param LIMIT 1";
// ۳. آماده سازی کوئری در دیتابیس
$stmt = $pdo->prepare($sql);
// ۴. بایند کردن داده کاربر به صورت کاملا امن
$userInput = "admin' OR '1'='1"; // یک ورودی مخرب نمونه
$stmt->bindParam(':username_param', $userInput, PDO::PARAM_STR);
// ۵. اجرا
$stmt->execute();
$user = $stmt->fetch();
if ($user) {
echo "کاربر با موفقیت یافت شد!";
} else {
echo "کاربری یافت نشد.";
}
} catch (PDOException $e) {
// در محیط پروڈکشن هرگز جزییات خطا را به کاربر نمایش ندهید
error_log($e->getMessage());
die("خطایی در سیستم رخ داده است.");
}
آموزش استفاده از Prepared Statement در MySQLi
اگر پروژه شما از اکستنشن قدیمیتر MySQLi استفاده میکند، باز هم باید از عبارات آماده به شکل زیر بهره ببرید:
// ۱. ایجاد اتصال به دیتابیس
$mysqli = new mysqli("localhost", "root", "", "my_secure_db");
if ($mysqli--->connect_error) {
die("اتصال ناموفق: " . $mysqli->connect_error);
}
// ۲. کوئری با استفاده از علامت سوال ?
$sql = "SELECT id, email FROM users WHERE email = ?";
// ۳. آماده سازی در دیتابیس
$stmt = $mysqli->prepare($sql);
if ($stmt) {
$emailInput = "test@example.com";
// ۴. بایند کردن پارامتر (s مخفف string است)
$stmt->bind_param("s", $emailInput);
// ۵. اجرا
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo "شناسه کاربر: " . $row['id'];
}
$stmt->close();
}
$mysqli->close();
تفاوت اجرای امن و غیرامن در دیتابیس
هنگامی که از Prepared Statement استفاده میکنید، موتور دیتابیس کوئری را کامپایل کرده و جایگاه دادهها را مشخص میکند. اگر کاربر متنی مانند admin' OR '1'='1 وارد کند، دیتابیس فیلد نام کاربری را دقیقا برابر با کل این متن جستجو میکند و هرگز بخش OR '1'='1 را به عنوان دستور شرطی منطقی اجرا نخواهد کرد.
این تفاوت بنیادین، راه نفوذ و دور زدن اهراز هویت در دیتابیس را برای همیشه مسدود خواهد کرد.
پرسش و پاسخ
نظری یافت نشد
برای ارسال نظر ابتدا وارد شوید