راهنمای جامع جلوگیری از SQL Injection در PHP (طبق استانداردهای گوگل)
حمله SQL Injection چیست و چرا رخ میدهد؟
حمله تزریق کد اسکیوال یا SQL Injection (SQLi) یکی از قدیمیترین و در عین حال خطرناکترین آسیبپذیریهای وب است. این آسیبپذیری زمانی رخ میدهد که دادههای وارد شده توسط کاربر، بدون اعتبارسنجی یا فیلترسازی مناسب، مستقیماً با دستورات SQL ترکیب شده و توسط پایگاه داده اجرا شوند. در این حالت، مهاجم میتواند دستورات دلخواه خود را به پایگاه داده تزریق کرده و به اطلاعات حساس دسترسی پیدا کند، آنها را تغییر دهد یا حتی کل دیتابیس را حذف کند.
کد ناامن: ریشه اصلی مشکل
بسیاری از برنامهنویسان تازهکار (و حتی گاهی باسابقه) اقدام به الحاق مستقیم متغیرها به رشتههای SQL میکنند. کد زیر نمونه بارز یک فاجعه امنیتی است:
// یک مثال کاملاً ناامن و آسیبپذیر
$username = $_POST["username"];
$password = $_POST["password"];
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $query);
اگر نفوذگر در فیلد نام کاربری عبارت admin' OR '1'='1 را وارد کند، کوئری نهایی به شکل زیر در میآید که همواره صحیح بوده و بدون نیاز به رمز عبور، دسترسی ادمین را به هکر میدهد!
راهکار استاندارد گوگل: استفاده از Prepared Statements
گوگل در راهنماهای امنیتی خود تاکید میکند که هرگز نباید دادههای کاربر را به طور مستقیم با دستورات SQL ادغام کرد. بهترین، استانداردترین و امنترین راهکار برای جلوگیری از SQL Injection در PHP، استفاده از Prepared Statements (دستورات آماده) از طریق کتابخانه PDO یا MySQLi است.
چرا Prepared Statements امن است؟
در این روش، ساختار کوئری ابتدا به پایگاه داده فرستاده و کامپایل میشود. سپس دادههای کاربر به صورت جداگانه (پارامترهای بایند شده) ارسال میگردند. دیتابیس متغیرهای ورودی را صرفاً به عنوان دیتای خام پردازش میکند، نه بخشی از دستور پایگاه داده؛ بنابراین امکان اجرای کدهای مخرب کاملاً از بین میرود.
پیادهسازی امن با PDO (توصیه شده)
کلاس PDO به دلیل پشتیبانی از پایگاه دادههای مختلف و امنیت بالا، بهترین انتخاب است. نمونه پیادهسازی استاندارد آن به شکل زیر است:
// اتصال امن با PDO و تنظیم حالت خطا
try {
$pdo = new PDO("mysql:host=localhost;dbname=mydb;charset=utf8mb4", "user", "pass");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// تعریف کوئری با Placeholder
$stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE username = :username");
// بایند کردن و اجرا
$stmt->execute([
'username' => $_POST['username']
]);
$user = $stmt->fetch();
} catch (PDOException $e) {
// نمایش ندادن خطای واقعی به کاربر جهت حفظ امنیت
error_log($e->getMessage());
die("خطایی در سیستم رخ داده است.");
}
چند نکته طلایی از استانداردهای امنیتی گوگل
- غیرفعال کردن Emulate Prepares: در تنظیمات PDO، شبیهساز Prepared Statements را غیرفعال کنید (
PDO::ATTR_EMULATE_PREPARES => false) تا مطمئن شوید پایگاه داده به صورت واقعی کوئریها را تفکیک میکند. - اصل حداقل دسترسی (Least Privilege): کاربری که اپلیکیشن شما با آن به دیتابیس وصل میشود، باید فقط دسترسیهای مورد نیاز خود (مانند SELECT و INSERT) را داشته باشد، نه دسترسیهای مدیریتی نظیر DROP TABLE.
- اعتبارسنجی و فیلترینگ ورودیها: علاوه بر لایه پایگاه داده، همیشه ورودیهای کاربر را با توابعی نظیر
filter_var()در PHP اعتبارسنجی کنید.
پرسش و پاسخ
نظری یافت نشد
برای ارسال نظر ابتدا وارد شوید