در یکی از پرونده‌های پاکسازی که چند سال پیش روی آن کار می‌کردم، یک فروشگاه اینترنتی کوچک قربانی حمله SQL Injection شده بود. مهاجم از یک پارامتر فیلتر دسته‌بندی محصولات وارد شده بود، ساختار جدول کاربران را استخراج کرده بود، هش‌های رمز عبور را بیرون کشیده بود و در نهایت، دیتابیس کل فروشگاه را پاک کرده بود. آن سایت نه بکاپ داشت و نه کنترل دسترسی سطح منبع. هزینه بازسازی، ده‌ها برابر هزینه‌ای بود که اگر از همان ابتدا Prepared Statements استفاده می‌شد، پرداخت نمی‌کردند. این تجربه به من آموخت که SQL Injection (SQLi یا SQL Injection) یکی از قدیمی‌ترین اما همچنان پرخطرترین آسیب‌پذیری‌های وب است. در این مقاله، این حمله و راه‌های مقابله با آن را بر اساس تجربه‌های مهندسی بررسی می‌کنم.

طبق گزارش OWASP Top 10 نسخه ۲۰۲۱، SQL Injection در دسته A03 (Injection) قرار دارد و همچنان یکی از ده آسیب‌پذیری بحرانی وب است. طبق گزارش Verizon Data Breach Investigations Report 2024، حدود ۱۵ درصد از نقض‌های داده شامل نوعی از Injection هستند که SQL Injection سهم بزرگی از آن را دارد. طبق آمار HackerOne، SQL Injection یکی از پرگزارش‌ترین آسیب‌پذیری‌ها در برنامه‌های Bug Bounty است. این آمارها نشان می‌دهد که با وجود در دسترس بودن راه‌حل‌های استاندارد، SQL Injection همچنان یک تهدید جدی است. اگر با مفاهیم پایه‌ای امنیت وب آشنا نیستید، پیشنهاد می‌کنم ابتدا امنیت وب چیست و استانداردهای امنیت وب را مطالعه کنید.

SQL Injection در یک تعریف دقیق

SQL Injection یا SQLi، یک نوع آسیب‌پذیری امنیتی است که به مهاجم اجازه می‌دهد دستورات SQL دلخواه را در کوئری‌های دیتابیس اپلیکیشن تزریق کند. این حمله، زمانی رخ می‌دهد که اپلیکیشن ورودی کاربر را بدون پاک‌سازی، در کوئری SQL قرار می‌دهد. مهاجم با استفاده از کاراکترهای خاص SQL مثل کوتیشن و عملگرهای منطقی، می‌تواند ساختار کوئری را تغییر دهد و دستورات دلخواه را اجرا کند. مفهوم SQL Injection در طول دو دهه گذشته تکامل یافته، اما اصل تهدید همچنان پابرجاست: اعتماد به ورودی کاربر در لایه دیتابیس.

نکته مهم این است که SQL Injection صرفاً یک آسیب‌پذیری در سطح کد نیست؛ یک آسیب‌پذیری معماری است. اگر طراحی اپلیکیشن به گونه‌ای باشد که ورودی کاربر همیشه به عنوان داده (Data) تلقی شود و هرگز به عنوان کد (Code)، SQL Injection رخ نمی‌دهد. تفاوت بین Data و Code، هسته اصلی دفاع در برابر این حمله است.

SQL Injection را نباید با XSS (Cross-Site Scripting) اشتباه گرفت. در XSS، مهاجم کد JavaScript را در مرورگر قربانی اجرا می‌کند. در SQLi، مهاجم دستورات SQL را در دیتابیس سرور اجرا می‌کند. اگر با XSS آشنا نیستید، حملات XSS و راه‌های پیشگیری و XSS چیست را مطالعه کنید.

SQL Injection، حمله به مرز بین داده و کد است. اگر این مرز شفاف نباشد، مهاجم از آن عبور می‌کند. اگر شفاف باشد، مرز نفوذناپذیر می‌شود.

مکانیزم حمله به زبان ساده

برای درک SQL Injection، باید مکانیزم یک حمله ساده را ببینیم. فرض کنید یک اپلیکیشن، ورودی کاربر را در یک کوئری SQL وارد می‌کند:

-- کوئری آسیب‌پذیر در PHP
$email = $_POST["email"];
$query = "SELECT * FROM users WHERE email = " . $email . "";
$result = mysqli_query($conn, $query);

اگر کاربر یک ایمیل معمولی مثل user@example.com وارد کند، کوئری به این شکل است:

SELECT * FROM users WHERE email = "user@example.com"

اما اگر مهاجم ورودی زیر را وارد کند:

" OR "1"="1

کوئری نهایی به این شکل تبدیل می‌شود:

SELECT * FROM users WHERE email = "" OR "1"="1"

این کوئری، شرط "1"="1" را که همیشه درست است به WHERE اضافه می‌کند و بنابراین همه رکوردهای جدول را برمی‌گرداند. مهاجم با همین تکنیک، می‌تواند به داده‌های حساس دسترسی پیدا کند. مکانیزم پیشرفته‌تر، استفاده از UNION برای ترکیب نتایج دو کوئری و DROP برای حذف جداول است.

مثال پیشرفته‌تر با UNION:

" UNION SELECT username, password, NULL FROM admin_users --

این ورودی، به کوئری اصلی UNION SELECT اضافه می‌کند و داده‌های جدول admin_users را نیز برمی‌گرداند. دو خط تیره (--) در انتها، بقیه کوئری را به کامنت تبدیل می‌کند تا خطای سینتکس رخ ندهد. اگر با ساختار کوئری‌های SQL آشنا نیستید، آموزش MySQL از صفر و دستورات پرکاربرد MySQL را مطالعه کنید.

سه نوع اصلی SQLi

SQL Injection بر اساس نحوه انتقال نتایج به مهاجم، به سه دسته اصلی تقسیم می‌شود: In-band، Blind و Out-of-band. هر دسته، مکانیزم متفاوتی دارد و برای شناسایی و مقابله با آن، رویکرد متفاوتی نیاز است.

نوعنحوه انتقال دادهسطح دشواری شناسایی
In-bandپاسخ مستقیم در پاسخ HTTPآسان
Blindبر اساس رفتار اپلیکیشنمتوسط تا سخت
Out-of-bandکانال جانبی (DNS/HTTP)سخت

در In-band SQLi، مهاجم داده استخراج‌شده را مستقیماً در پاسخ HTTP می‌بیند. این نوع شامل Error-based و Union-based است. در Error-based، مهاجم از پیام‌های خطای دیتابیس برای استخراج داده استفاده می‌کند. در Union-based، مهاجم از عملگر UNION برای ترکیب نتایج دو کوئری استفاده می‌کند.

در Blind SQLi، مهاجم نمی‌تواند داده استخراج‌شده را در پاسخ ببیند. به جای آن، از رفتار اپلیکیشن برای استخراج داده استفاده می‌کند. دو زیرشاخه مهم: Boolean-based (مبتنی بر درست یا غلط بودن شرط) و Time-based (مبتنی بر تأخیر عمدی در پاسخ). در Time-based، مهاجم از تابع SLEEP() استفاده می‌کند و بر اساس تأخیر پاسخ، اطلاعات را استخراج می‌کند.

در Out-of-band SQLi، مهاجم از کانال جانبی مثل DNS یا HTTP برای انتقال داده استفاده می‌کند. این نوع، در سناریوهایی کاربرد دارد که پاسخ HTTP مهاجم را نمی‌بیند یا Blind SQLi خیلی کند است. مثلاً مهاجم از تابع LOAD_FILE در MySQL برای خواندن فایل‌های سرور و ارسال از طریق DNS استفاده می‌کند.

تأثیر SQLi بر کسب‌وکار

SQL Injection فراتر از یک آسیب‌پذیری فنی است؛ یک ریسک کسب‌وکاری با اثرات ملموس. سه سطح تأثیر اصلی: فنی، اعتباری و قانونی.

در سطح فنی، SQLi می‌تواند منجر به افشای کامل دیتابیس (شامل اطلاعات کاربران، رمزهای هش‌شده، داده‌های مالی)، تغییر یا حذف داده‌ها، دور زدن مکانیزم احراز هویت، اجرای دستورات سیستمی (در صورت دسترسی بالای کاربر دیتابیس)، و در موارد پیشرفته، دسترسی کامل به سرور. در یکی از پرونده‌های پاکسازی، SQLi از یک افزونه وردپرسی منجر به استخراج ۵۰ هزار حساب کاربری شده بود.

در سطح اعتباری، افشای SQLi در یک سایت می‌تواند به شدت به برند آسیب بزند. اگر سایت شما داده کاربران را از دست بدهد، اعتماد از دست می‌رود و بازگشت آن زمان‌بر است. طبق گزارش IBM Cost of Data Breach 2024، میانگین هزینه یک نقض داده حدود ۴.۸۸ میلیون دلار است. این هزینه، شامل شناسایی نقض، اطلاع‌رسانی، پاسخ به حادثه، جریمه‌های قانونی و از دست رفتن مشتری است.

در سطح قانونی، در کشورهای با قوانین سختگیرانه حفاظت از داده مثل GDPR، افشای داده از طریق SQLi می‌تواند به جریمه‌های سنگین منجر شود. طبق قوانین GDPR، جریمه می‌تواند تا ۲۰ میلیون یورو یا ۴ درصد از درآمد سالانه جهانی شرکت باشد. اگر با مفاهیم پایه‌ای امنیت آشنا نیستید، استانداردهای امنیت وب و انواع آسیب‌پذیری‌های رایج وب را مطالعه کنید.

Prepared Statements؛ راه‌حل بنیادین

Prepared Statements (دستورات آماده) مؤثرترین و بنیادین‌ترین راه مقابله با SQL Injection است. در این رویکرد، ساختار کوئری SQL از داده جدا می‌شود و دیتابیس، داده را به عنوان پارامتر دریافت می‌کند، نه به عنوان بخشی از سینتکس کوئری. این یعنی حتی اگر داده شامل کاراکترهای خطرناک مثل کوتیشن باشد، به عنوان متن تلقی می‌شود، نه کد SQL.

نمونه Prepared Statements در PHP با PDO:

// آماده‌سازی کوئری با پارامترهای نام‌دار
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND status = :status");

// اجرای کوئری با مقادیر
$stmt->execute([
    "email" => $email,
    "status" => "active"
]);

// دریافت نتیجه
$user = $stmt->fetch();

نمونه در PHP با MySQLi:

$stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ? AND status = ?");
$stmt->bind_param("ss", $email, $status);
$stmt->execute();
$result = $stmt->get_result();

نمونه در Python با psycopg2:

cursor.execute(
    "SELECT * FROM users WHERE email = %s AND status = %s",
    (email, status)
)

نکات مهم درباره Prepared Statements: اول، ساختار کوئری (کد) از داده جدا می‌شود. دوم، پارامترها به عنوان داده (Data) به دیتابیس ارسال می‌شوند، نه به عنوان بخشی از کوئری. سوم، حتی اگر داده شامل کوتیشن، کاما یا سایر کاراکترهای خاص SQL باشد، به عنوان متن تلقی می‌شود. چهارم، Prepared Statements در همه دیتابیس‌های مدرن مثل MySQL، PostgreSQL، SQLite و SQL Server پشتیبانی می‌شود.

یک اشتباه رایج در Prepared Statements: استفاده از متغیرهای رشته‌ای به جای پارامترها. مثلاً در PDO، اگر کوئری با $pdo->query() اجرا شود و ورودی در رشته کوئری درج شود، همچنان آسیب‌پذیر است. همچنین اگر نام جدول یا نام ستون از ورودی کاربر گرفته شود (که معمولاً نباید)، Prepared Statements کمکی نمی‌کند و باید Allowlist استفاده کرد. اگر با PHP و وردپرس کار می‌کنید، نوشتن کد PHP امن برای وردپرس و پاک‌سازی داده‌ها در وردپرس را بخوانید.

ORM و لایه‌های انتزاعی

ORM (Object-Relational Mapping) ابزارهایی مثل Doctrine، Eloquent، Hibernate، SQLAlchemy و Sequelize هستند که تعامل با دیتابیس را از طریق اشیاء و متدها ممکن می‌کنند، نه کوئری‌های خام. استفاده از ORM، به دلیل استفاده پیش‌فرض از Prepared Statements، یکی از مؤثرترین راه‌های کاهش ریسک SQL Injection است.

نمونه استفاده از Eloquent در Laravel:

$users = User::where("email", $email)
             ->where("status", "active")
             ->get();

نمونه در Django:

users = User.objects.filter(email=email, status="active")

ORM به صورت خودکار از Prepared Statements استفاده می‌کند و ورودی کاربر را به عنوان پارامتر ارسال می‌کند. اما این محافظت مطلق نیست و در سه سناریو از بین می‌رود: اول، استفاده از کوئری‌های خام در ORM (مثل DB::raw() یا connection.execute()). دوم، استفاده از نام جدول یا ستون از ورودی کاربر. سوم، استفاده از عملگرهای منطقی مثل whereRaw() بدون پاک‌سازی.

نکات مهم درباره ORM: اول، ORM نه‌فقط امنیت، بلکه خوانایی و نگهداری کد را بهبود می‌دهد. دوم، در پروژه‌های بزرگ، ORM همراه با Query Builder کاربرد دارد. سوم، در صورت نیاز به کوئری‌های پیچیده، از Query Builder استفاده کنید که همچنان از Prepared Statements بهره می‌برد. چهارم، در PostgreSQL، از cursor.execute() با پارامترها استفاده کنید، نه فرمت‌بندی رشته.

ORM، لایه انتزاعی امن است، تا زمانی که از آن به عنوان ابزار امن استفاده کنید. کوئری خام در ORM، مثل حفر تونل از زیر دیوار امنیتی است.

Input Validation و Type Casting

Input Validation و Type Casting، دو لایه دفاعی مکمل در کنار Prepared Statements هستند. Input Validation، بررسی می‌کند که ورودی با فرمت مورد انتظار مطابقت دارد یا نه. Type Casting، ورودی را به نوع داده مورد انتظار تبدیل می‌کند.

نمونه Type Casting در PHP:

// ورودی کاربر
$userId = $_GET["user_id"];

// Type Casting
$userId = (int) $userId;

// حالا امن است
$query = "SELECT * FROM users WHERE id = " . $userId;

نمونه Input Validation در PHP:

// بررسی ایمیل
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    throw new InvalidArgumentException("ایمیل نامعتبر است");
}

// بررسی عدد صحیح
if (!filter_var($userId, FILTER_VALIDATE_INT)) {
    throw new InvalidArgumentException("شناسه کاربر باید عدد باشد");
}

// بررسی الگوی سفارشی
if (!preg_match("/^[a-z0-9_]{3,20}$/", $username)) {
    throw new InvalidArgumentException("نام کاربری نامعتبر است");
}

نکات مهم درباره Input Validation: اول، Validation باید بر اساس Allowlist باشد، نه Blacklist. یعنی الگوهای مجاز را تعریف کنید، نه الگوهای غیرمجاز. دوم، Validation باید در زمان Input انجام شود تا داده نامعتبر از ورود به سیستم جلوگیری شود. سوم، Validation باید در سمت سرور انجام شود، نه فقط سمت کلاینت. چهارم، Validation تکمیلی است، نه جایگزین Prepared Statements. اگر با اعتبارسنجی داده آشنا نیستید، اعتبارسنجی داده‌ها در کدنویسی وردپرس و پاک‌سازی داده‌ها در وردپرس را مطالعه کنید.

اصل حداقل دسترسی در دیتابیس

اصل حداقل دسترسی (Principle of Least Privilege) یکی از اصول بنیادین امنیت است که در حوزه SQL Injection بسیار مهم است. این اصل می‌گوید کاربر دیتابیس که اپلیکیشن با آن کار می‌کند، فقط باید به عملیات مورد نیاز دسترسی داشته باشد، نه بیشتر.

سه سطح دسترسی در MySQL: کاربر با دسترسی خواندن (SELECT)، کاربر با دسترسی خواندن و نوشتن (SELECT، INSERT، UPDATE، DELETE)، و کاربر با دسترسی کامل (شامل DROP، CREATE، ALTER). برای اپلیکیشن، حداقل دسترسی مورد نیاز، سطح دوم است. کاربر اپلیکیشن هرگز نباید به DROP یا CREATE دسترسی داشته باشد.

-- ایجاد کاربر دیتابیس با حداقل دسترسی
CREATE USER "app_user"@"localhost" IDENTIFIED BY "strong_password";

-- دسترسی فقط برای SELECT، INSERT، UPDATE، DELETE روی دیتابیس خاص
GRANT SELECT, INSERT, UPDATE, DELETE ON app_database.* TO "app_user"@"localhost";

-- عدم دسترسی به DROP، CREATE، ALTER، GRANT
-- عدم دسترسی به سایر دیتابیس‌ها
FLUSH PRIVILEGES;

نکات مهم درباره حداقل دسترسی: اول، کاربر اپلیکیشن نباید کاربر root باشد. دوم، کاربر اپلیکیشن نباید به دیتابیس system (مثل mysql یا information_schema) دسترسی داشته باشد. سوم، در معماری میکروسرویس، هر سرویس باید کاربر اختصاصی داشته باشد. چهارم، برای عملیات ادمین (مثل مهاجرت)، از کاربر جداگانه استفاده کنید، نه کاربر اپلیکیشن. اگر به امنیت دیتابیس علاقه‌مندید، امنیت دیتابیس چیست و بهترین روش‌های امنیت MySQL را بخوانید.

WAF و لایه‌های دفاعی تکمیلی

WAF (Web Application Firewall) یک لایه دفاعی تکمیلی در برابر SQL Injection و سایر حملات وب است. WAF، درخواست‌های HTTP را قبل از رسیدن به اپلیکیشن بررسی می‌کند و در صورت شناسایی الگوهای حمله، آن‌ها را مسدود می‌کند. سه نوع WAF وجود دارد: Network-based (سخت‌افزاری)، Host-based (نرم‌افزاری مثل ModSecurity)، و Cloud-based (سرویس‌های ابری مثل Cloudflare WAF و Sucuri).

نقش WAF در دفاع در برابر SQLi: اول، شناسایی و مسدود کردن payload‌های شناخته‌شده SQLi. دوم، محدودسازی نرخ درخواست‌های مشکوک. سوم، ثبت لاگ درخواست‌های مسدودشده برای تحلیل بعدی. چهارم، محافظت در برابر حملات جدید که هنوز در کد اپلیکیشن پچ نشده‌اند.

نکات مهم درباره WAF: اول، WAF جایگزین کد امن نیست. اگر کد شما آسیب‌پذیر باشد، WAF فقط بخشی از حملات را مسدود می‌کند. دوم، WAF باید به طور مداوم به‌روزرسانی شود تا الگوهای جدید را بشناسد. سوم، در حالت Learning شروع کنید تا از False Positive جلوگیری کنید. چهارم، لاگ WAF باید پایش شود تا حملات واقعی و False Positive تشخیص داده شوند. اگر با Cloudflare و WAF آشنا نیستید، مقایسه Cloudflare و Sucuri و فایروال ابری در برابر سنتی را مطالعه کنید.

تست و شناسایی SQLi

تست و شناسایی SQL Injection بخشی جدانشدنی از استراتژی مقابله است. سه رویکرد اصلی: تست دستی، تست خودکار و Static Analysis.

تست دستی: ابتدا، در همه فیلدهای ورودی payload‌های ساده مثل ' و " را امتحان کنید و رفتار اپلیکیشن را مشاهده کنید. سپس با payload‌های پیشرفته‌تر مثل ' OR '1'='1 و ' UNION SELECT NULL-- تست کنید. مکان‌های تست: فرم‌های لاگین، فیلترهای جستجو، پارامترهای URL، API endpoints، و هر ورودی که به دیتابیس می‌رود.

تست خودکار: ابزارهایی مثل sqlmap، OWASP ZAP، Burp Suite Scanner و Acunetix، SQLi را به طور خودکار شناسایی می‌کنند. sqlmap یکی از قدرتمندترین ابزارها در این حوزه است و می‌تواند انواع SQLi را در دیتابیس‌های مختلف شناسایی و اکسپلویت کند.

# نمونه استفاده از sqlmap
sqlmap -u "https://example.com/page?id=1" --dbs
sqlmap -u "https://example.com/page?id=1" -D app_database --tables
sqlmap -u "https://example.com/page?id=1" -D app_database -T users --dump

Static Analysis: ابزارهایی مثل SonarQube، Snyk Code و Semgrep، کد را تحلیل می‌کنند و نقاط آسیب‌پذیر SQLi را شناسایی می‌کنند. این ابزارها در CI/CD pipeline قابل ادغام هستند و می‌توانند قبل از انتشار کد، مشکلات را شناسایی کنند. اگر با CI/CD آشنا نیستید، مقایسه ابزارهای CI/CD و گیت در وردپرس را بخوانید.

SQL Injection در وردپرس

وردپرس، به دلیل استفاده گسترده از دیتابیس MySQL، از SQL Injection مصون نیست. اما هسته وردپرس از توابع اختصاصی و Prepared Statements استفاده می‌کند که ریسک را به شدت کاهش می‌دهد. مشکل اصلی، در قالب‌ها و افزونه‌های شخص ثالث است که اغلب از کوئری‌های خام استفاده می‌کنند.

توابع امن در وردپرس: $wpdb->prepare() برای Prepared Statements، absint() برای تبدیل به عدد مثبت، esc_sql() برای Escape در موارد ضروری، و sanitize_text_field() برای پاک‌سازی متن.

// روش ناامن
$wpdb->query("SELECT * FROM {$wpdb->prefix}users WHERE id = $user_id");

// روش امن
$wpdb->query(
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}users WHERE id = %d",
        $user_id
    )
);

نکات مهم درباره SQL Injection در وردپرس: اول، هرگز از کوئری‌های خام با ورودی کاربر استفاده نکنید. دوم، از $wpdb->prepare() برای همه کوئری‌هایی که ورودی کاربر دارند استفاده کنید. سوم، در افزونه‌های حرفه‌ای، از ORM یا Query Builder استفاده کنید. چهارم، در بررسی افزونه‌های شخص ثالث، به کدهای SQL دقت کنید. اگر با امنیت وردپرس کار می‌کنید، راهنمای امنیت وردپرس و اشتباهات رایج امنیت وردپرس را مطالعه کنید.

پرسش‌های پرتکرار درباره SQL Injection

آیا ORM جلوی همه SQL Injection را می‌گیرد؟ تا حد زیادی، بله. اما اگر از کوئری‌های خام در ORM استفاده کنید یا نام جدول و ستون را از ورودی کاربر بگیرید، همچنان آسیب‌پذیر است. ORM یک لایه دفاعی است، نه جایگزین درست‌نویسی.

آیا WAF جایگزین Prepared Statements است؟ خیر. WAF یک لایه دفاعی تکمیلی است. اگر کد شما آسیب‌پذیر باشد، WAF فقط بخشی از حملات را مسدود می‌کند. Prepared Statements راه‌حل بنیادین است و باید همیشه پیاده‌سازی شود.

آیا Escape کردن ورودی کافی است؟ در برخی موارد، بله، اما نه به عنوان راه‌حل اصلی. Escape با mysqli_real_escape_string() در صورتی که با charset درست استفاده شود، امن است. اما Prepared Statements راه‌حل مطمئن‌تری است که به charset وابسته نیست.

چگونه بفهمم سایتم آسیب‌پذیر SQLi است؟ از ابزارهای تست خودکار مثل sqlmap و OWASP ZAP استفاده کنید. اما این ابزارها نه همه SQLi را شناسایی می‌کنند و نه همه هشدارها را درست تشخیص می‌دهند. بهترین رویکرد، ترکیب ابزار خودکار با Static Analysis و Code Review است.

آیا Static Analysis می‌تواند همه SQLi را شناسایی کند؟ خیر. Static Analysis معمولاً آسیب‌پذیری‌های واضح را شناسایی می‌کند، اما کوئری‌های پیچیده یا پویا را ممکن است از دست بدهد. برای شناسایی کامل، نیاز به ترکیب Static Analysis، Dynamic Analysis (مانند تست خودکار) و Code Review دستی است.

آیا وردپرس در برابر SQLi محافظت می‌کند؟ هسته وردپرس از توابع امن مثل $wpdb->prepare() استفاده می‌کند. اما این محافظت به قالب‌ها و افزونه‌ها منتقل نمی‌شود. قالب‌ها و افزونه‌های ضعیف می‌توانند SQLi را وارد کنند. در بررسی افزونه‌های شخص ثالث، به کدهای SQL دقت کنید.

آیا باید IP کاربر را برای دفاع در برابر SQLi محدود کنم؟ محدودسازی IP یکی از لایه‌های دفاعی است، اما نه راه‌حل اصلی. اگر IP کاربر را محدود کنید، مهاجم می‌تواند از IP دیگری حمله کند. رویکرد درست، ترکیب چند لایه دفاعی است: Prepared Statements، WAF، Rate Limiting و پایش مداوم.

آنچه باید با خود ببرید

SQL Injection یکی از قدیمی‌ترین اما همچنان پرخطرترین آسیب‌پذیری‌های وب است. سه نوع اصلی این حمله In-band، Blind و Out-of-band هستند و هر کدام مکانیزم و رویکرد مقابله متفاوتی دارند.

هفت اصل کلیدی برای مقابله با SQL Injection:

  1. Prepared Statements، راه‌حل بنیادین و بدون جایگزین است.
  2. ORM و Query Builder، با استفاده درست، ریسک SQLi را به شدت کاهش می‌دهند.
  3. Input Validation و Type Casting، لایه دفاعی تکمیلی هستند.
  4. اصل حداقل دسترسی در دیتابیس، خسارت را در صورت نفوذ محدود می‌کند.
  5. WAF یک لایه دفاعی تکمیلی است، نه جایگزین کد امن.
  6. Static Analysis و تست خودکار، بخشی از استراتژی کامل تست هستند.
  7. در وردپرس، $wpdb->prepare() و توابع امن، پایه دفاع هستند.

قدم عملی امروز: در کد پروژه خود، همه کوئری‌های SQL که ورودی کاربر دارند را پیدا کنید. اگر از رشته‌های متصل استفاده می‌کنند، به Prepared Statements تبدیل کنید. اگر از ORM استفاده می‌کنید، کوئری‌های خام را پیدا کنید و به Query Builder تبدیل کنید. اگر با وردپرس کار می‌کنید، همه کوئری‌های $wpdb->query() را بررسی کنید. این سه بررسی، بخش بزرگی از ریسک SQLi در پروژه شما را حذف می‌کند. اگر می‌خواهید عمیق‌تر شوید، امنیت دیتابیس و بهترین روش‌های امنیت MySQL را مطالعه کنید.

اگر تجربه‌ای در شناسایی یا مقابله با SQL Injection در پروژه‌های واقعی داشتید — به‌خصوص اگر با یک حمله پیچیده مواجه شده‌اید یا یک تکنیک دور زدن WAF را کشف کرده‌اید — در دیدگاه‌ها بنویسید. این تجربه‌ها برای خواننده‌های بعدی از هر مقاله تئوریک ارزشمندتر است. 🔐