حملات SQL Injection
حملات SQL Injection زمانی رخ میدهند که ورودی کاربر بهصورت ناامن وارد Query دیتابیس شود و ساختار دستور SQL را تغییر دهد. این آسیبپذیری میتواند به افشای اطلاعات، تغییر یا حذف دادهها و دسترسی غیرمجاز منجر شود. در این مقاله با انواع SQL Injection، روشهای شناسایی، Prepared Statement، Parameterized Query و مهمترین راهکارهای جلوگیری از تزریق SQL آشنا میشوید.
حملات SQL Injection یا تزریق SQL یکی از شناختهشدهترین آسیبپذیریهای امنیتی در وب هستند. در این حملات، مهاجم تلاش میکند دادهای کنترلنشده را وارد ورودیهای یک برنامه کند تا ساختار Query دیتابیس تغییر کرده و رفتاری خارج از هدف اصلی برنامه ایجاد شود. نتیجه این ضعف میتواند از مشاهده غیرمجاز اطلاعات تا تغییر دادهها، دور زدن بعضی کنترلهای دسترسی یا حتی در شرایط خاص آسیب گستردهتر به سامانه متغیر باشد.
نکته مهم این است که SQL Injection مشکل خود پایگاه داده نیست؛ مشکل زمانی ایجاد میشود که برنامه، داده دریافتی از کاربر را بدون جداسازی صحیح از دستور SQL وارد Query کند.
به همین دلیل داشتن رمز عبور قوی برای دیتابیس، SSL روی سایت یا حتی نصب یک افزونه امنیتی بهتنهایی نمیتواند یک کد آسیبپذیر در برابر SQL Injection را اصلاح کند.
راهکار اصلی، نوشتن Queryهای امن، استفاده از Prepared Statement و Parameterized Query، محدود کردن سطح دسترسی دیتابیس، مدیریت صحیح خطاها، بهروزرسانی نرمافزار و مانیتورینگ رفتارهای مشکوک است.
در این مقاله بهصورت کامل بررسی میکنیم SQL Injection چیست، چگونه شکل میگیرد، چه قسمتهایی از سایت ممکن است آسیبپذیر باشند، چه نشانههایی وجود دارد، چگونه ریسک آن را کاهش دهیم و اگر احتمال تزریق SQL در سایت وجود داشت چه اقداماتی باید انجام شوند.

SQL Injection چیست؟
SQL Injection که معمولاً بهصورت SQLi نوشته میشود، یک ضعف امنیتی در برنامههایی است که با پایگاه داده SQL ارتباط دارند.
برای درک ساده موضوع، فرض کنید وبسایت از کاربر یک شناسه محصول دریافت میکند و براساس آن اطلاعات محصول را از دیتابیس میخواند.
برنامه باید دادهای مانند شناسه محصول را صرفاً بهعنوان داده در نظر بگیرد.
مشکل زمانی شروع میشود که مقدار دریافتی مستقیماً وارد ساختار دستور SQL شود. در این شرایط ممکن است بخشی از ورودی کاربر توسط Database Engine بهعنوان قسمت دیگری از دستور تفسیر شود.
بنابراین اصل مشکل این است:
داده کنترلشده توسط کاربر و دستور SQL نباید با یکدیگر ترکیب شوند.
Parameterized Query دقیقاً برای ایجاد همین جداسازی استفاده میشود.
SQL چیست؟
SQL مخفف:
Structured Query Language
است.
این زبان برای تعامل با پایگاههای داده رابطهای استفاده میشود.
سیستمهایی مانند:
MySQL، MariaDB، PostgreSQL، Microsoft SQL Server و برخی دیگر از SQL یا Dialectهای مرتبط با آن استفاده میکنند.
یک برنامه ممکن است با SQL عملیات مختلفی انجام دهد؛ مانند خواندن اطلاعات، ثبت داده، ویرایش رکورد یا حذف اطلاعات.
برای مثال فروشگاه اینترنتی از Database برای نگهداری:
محصولات، کاربران، سفارشها، موجودی کالا، تنظیمات و اطلاعات مرتبط با پرداخت
استفاده میکند.
به همین دلیل هر ضعفی که اجازه دسترسی غیرمجاز به Database را ایجاد کند میتواند بسیار جدی باشد.

حمله SQL Injection چگونه ایجاد میشود؟
برای اینکه SQL Injection شکل بگیرد، معمولاً چند شرط وجود دارد.
برنامه باید یک ورودی قابل کنترل توسط کاربر داشته باشد.
این ورودی میتواند از:
فرم، URL، پارامتر Query، Cookie، HTTP Header، API یا سایر بخشهای Request
دریافت شود.
سپس برنامه مقدار ورودی را بدون استفاده از روش ایمن وارد یک Query میکند.
در چنین شرایطی ساختار Query ممکن است تحت تأثیر داده ورودی قرار بگیرد.
بنابراین SQL Injection همیشه محدود به یک Text Box در صفحه Login نیست.
هر نقطهای که اطلاعات خارجی وارد Query شود باید بهعنوان مرز امنیتی در نظر گرفته شود.
یک مثال ساده از کدنویسی ناامن
فرض کنید برنامهای اطلاعات محصول را براساس شناسه دریافت میکند و برنامهنویس Query را با چسباندن مستقیم ورودی به رشته SQL میسازد.
نمونه مفهومی ناامن:
$sql = "SELECT * FROM products WHERE id = " . $user_input;
مشکل اصلی این کد این است که:
$user_input
مستقیماً بخشی از Query شده است.
در نتیجه Database Engine نمیتواند بهصورت مطمئن تشخیص دهد کدام قسمت «دستور» و کدام قسمت «داده» است.
راه درست استفاده از Parameterized Query است.
برای مثال با PDO:
$stmt = $pdo->prepare(
"SELECT * FROM products WHERE id = :id"
);
$stmt->execute([
'id' => $product_id
]);
در این روش ساختار Query از مقدار ورودی جداست.
این تفاوت یکی از پایهایترین اصول جلوگیری از SQL Injection است.
چرا Escape کردن ورودی همیشه بهترین راه نیست؟
در گذشته بسیاری از برنامهها تلاش میکردند با اضافه کردن Escape Characterها جلوی SQL Injection را بگیرند.
اما این روش میتواند پیچیده و مستعد خطا باشد.
مشکلات ممکن است به:
Encoding، نوع Database، Context Query و روش تبدیل داده
وابسته باشند.
به همین دلیل راهکار اصلی برای مقادیر دادهای معمولاً:
Parameterized Queries / Prepared Statements
است.
بهجای اینکه برنامهنویس تلاش کند ورودی خطرناک را تشخیص دهد، ساختار Query از ابتدا از داده جدا میشود.
Prepared Statement چیست؟
Prepared Statement روشی است که Query ابتدا با Placeholderهای مشخص تعریف میشود.
سپس مقدار واقعی پارامترها جداگانه به Query داده میشود.
برای مثال:
$stmt = $pdo->prepare(
"SELECT name, price FROM products WHERE id = ?"
);
$stmt->execute([$product_id]);
اینجا:
?
نماینده مقدار است.
Database آن مقدار را بهعنوان داده پردازش میکند، نه بخشی از Syntax اصلی Query.
این رویکرد یکی از مؤثرترین روشهای پیشگیری از SQL Injection است.

SQL Injection چه خسارتی میتواند ایجاد کند؟
شدت آسیب کاملاً به نوع Query، سطح دسترسی Database User و معماری Application وابسته است.
در یک سامانه ضعیف، SQL Injection ممکن است باعث افشای اطلاعاتی شود که کاربر عادی نباید آنها را مشاهده کند.
در سناریوهای جدیتر ممکن است مهاجم بتواند دادههای موجود را تغییر دهد یا حذف کند.
در بعضی معماریها نیز آسیب Database میتواند روی سایر بخشهای برنامه اثر بگذارد.
اطلاعات حساس احتمالی شامل:
رمزهای Hashشده، اطلاعات کاربران، ایمیلها، سفارشها، اطلاعات داخلی برنامه، Tokenها و Configurationهای ذخیرهشده
هستند.
بنابراین حتی اگر SQL Injection مستقیماً به Server Access منجر نشود، Data Breach حاصل از آن میتواند خسارت جدی ایجاد کند.
انواع SQL Injection
SQL Injection یک تکنیک واحد نیست و بسته به رفتار برنامه و Database میتواند شکلهای متفاوتی داشته باشد.
شناخت این دستهها برای دفاع و تحلیل Incident اهمیت دارد، اما در یک راهنمای دفاعی نیازی به استفاده از Payloadهای واقعی برای سوءاستفاده نیست.
In-band SQL Injection
در این مدل، کانالی که درخواست از طریق آن ارسال میشود همان کانالی است که نتیجه نیز از آن بازمیگردد.
بهعنوان مثال Application یک ورودی دریافت میکند و نتیجه Query را مستقیماً در همان صفحه نمایش میدهد.
این نوع ضعف معمولاً برای تیم امنیتی سادهتر قابل تشخیص است؛ زیرا رفتار غیرعادی ممکن است مستقیماً در Response مشاهده شود.
Error-Based SQL Injection چیست؟
در Error-Based SQL Injection، پیام خطای Database اطلاعاتی درباره ساختار داخلی Application در اختیار فرد مهاجم قرار میدهد.
برای مثال ممکن است یک Error شامل:
نام جدول، نام ستون، مسیر فایل، نوع Database یا بخشی از Query
باشد.
به همین دلیل نمایش Error خام دیتابیس به کاربران Production تصمیم مناسبی نیست.
در Development وجود جزئیات Error برای عیبیابی مفید است، اما در Production بهتر است خطای عمومی به کاربر نمایش داده شده و جزئیات کامل فقط در Logهای محافظتشده ثبت شوند.
Union-Based SQL Injection چیست؟
این اصطلاح به سوءاستفاده از قابلیت ترکیب Result Setها در SQL اشاره دارد.
از دید دفاعی نکته مهم این است که اگر Input بتواند ساختار Query را تغییر دهد، مهاجم ممکن است بتواند دادههای خارج از محدوده مورد انتظار برنامه را وارد Result کند.
راه جلوگیری همچنان همان اصول پایه است:
Parameterized Query، محدود کردن Privilegeهای دیتابیس و جلوگیری از نمایش اطلاعات اضافی.
Blind SQL Injection چیست؟
گاهی Application اطلاعات Database یا خطای SQL را مستقیماً نمایش نمیدهد.
اما ممکن است رفتار صفحه براساس نتیجه Query متفاوت باشد.
در چنین شرایطی یک مهاجم میتواند با مقایسه رفتار برنامه اطلاعاتی درباره وضعیت Query به دست آورد.
به این خانواده:
Blind SQL Injection
گفته میشود.
Blind بودن به معنی کمخطر بودن آسیبپذیری نیست.
فقط روش دریافت نتیجه مستقیم نیست.
Boolean-Based Blind SQL Injection
در این نوع، رفتار Application بسته به True یا False شدن یک Condition تغییر میکند.
برای مثال ممکن است:
یک صفحه نمایش داده شود یا نشود، تعداد Results تغییر کند یا پیام متفاوتی ظاهر شود.
برای دفاع، تفاوتی در اصل مسئله وجود ندارد.
Query باید Parameterized باشد.
Time-Based Blind SQL Injection
در برخی شرایط، رفتار زمانی Database میتواند اطلاعاتی درباره نتیجه Query ایجاد کند.
برای تیمهای امنیتی، تعداد زیاد Requestهایی با الگوی زمانی غیرمعمول میتواند یکی از Signalهای قابل بررسی باشد.
اما کندی سایت بهتنهایی اثبات SQL Injection نیست.
Database Query سنگین، کمبود منابع، Plugin نامناسب یا Bot Traffic نیز میتوانند باعث افزایش Response Time شوند.
Second-Order SQL Injection چیست؟
این مورد برای بسیاری از توسعهدهندگان کمتر شناختهشده است.
در SQL Injection مستقیم، ورودی کاربر همان لحظه وارد Query ناامن میشود.
اما در Second-Order SQL Injection داده ابتدا ذخیره میشود و بعداً در بخش دیگری از Application به شکل ناامن وارد Query میشود.
یعنی ممکن است ورودی هنگام ثبت کاملاً عادی به نظر برسد، اما چند ساعت یا چند روز بعد در Backend یا Report Generator استفاده شود.
بنابراین فقط Validation در صفحه ورودی کافی نیست.
هر نقطهای که داده وارد Query میشود باید Query امن داشته باشد؛ حتی اگر داده از Database خود برنامه خوانده شده باشد.
SQL Injection در صفحه Login
Login Form یکی از قسمتهایی است که معمولاً هنگام صحبت درباره SQL Injection مثال زده میشود.
اما طراحی صحیح Login باید علاوه بر Query امن، شامل:
Password Hashing مناسب، Rate Limiting، 2FA و Session Management
نیز باشد.
در مورد SQL Injection، Username یا Email نباید با String Concatenation وارد Query شود.
برای مثال:
$stmt = $pdo->prepare(
"SELECT id, password_hash FROM users WHERE email = :email"
);
$stmt->execute([
'email' => $email
]);
سپس Password باید جداگانه با الگوریتم مناسب Verify شود.
SQL Injection در Search سایت
Search Box یکی دیگر از نقاط رایج ورود داده است.
کاربر Query را وارد میکند و Application ممکن است آن را داخل جستجوی Database استفاده کند.
در این حالت نیز Parameterized Query باید استفاده شود.
توسعهدهنده نباید تصور کند چون Search عمومی است اطلاعات آن خطر امنیتی ندارد.
هر ورودی خارجی باید Untrusted تلقی شود.
SQL Injection در فیلتر محصولات
فروشگاه اینترنتی معمولاً تعداد زیادی Parameter دارد:
قیمت، دستهبندی، برند، مرتبسازی، صفحهبندی و Attributeهای محصول.
اگر هرکدام از این مقادیر بدون کنترل وارد Query شوند، میتوانند سطح حمله ایجاد کنند.
نکته مهم درباره قسمتهایی مانند ORDER BY این است که بعضی ساختارهای SQL را نمیتوان مانند Value عادی Bind کرد.
در چنین شرایطی بهتر است از Allowlist استفاده شود.
برای مثال:
$allowed = ['price', 'created_at', 'name'];
if (!in_array($sort, $allowed, true)) {
$sort = 'created_at';
}
سپس فقط یکی از گزینههای از قبل تعریفشده وارد Query میشود.
Allowlist چیست؟
Allowlist یعنی فقط مجموعه مشخصی از مقادیر مجاز باشند.
برای مثال اگر سیستم Sort فقط میتواند:
name
price
یا:
date
باشد، نیازی نیست هر String دلخواهی پذیرفته شود.
برنامه ابتدا بررسی میکند مقدار ورودی دقیقاً یکی از گزینههای تعریفشده باشد.
این روش برای بخشهایی از SQL که Parameter Binding مستقیم برای ساختار آنها مناسب نیست اهمیت زیادی دارد.
SQL Injection در API
API نیز تفاوت اساسی با سایت معمولی ندارد.
اگر JSON Request دریافت شده و مقدار آن مستقیماً وارد SQL شود، همان خطر ایجاد میشود.
حتی APIهایی که فقط توسط Mobile App استفاده میشوند نیز باید ورودی را Untrusted در نظر بگیرند.
اینکه Client رسمی است تضمین نمیکند Request قابل دستکاری نیست.
Security باید در Server Enforcement شود.
SQL Injection از طریق Cookie
Cookie از سمت Client ارسال میشود و قابل تغییر است.
بنابراین نباید آن را Trusted دانست.
اگر مثلاً Application مقدار Language، User ID یا Preference را از Cookie گرفته و داخل Query قرار دهد، همان اصول امنیتی باید رعایت شوند.
SQL Injection از طریق HTTP Header
مواردی مانند:
User-Agent، Referer، X-Forwarded-For
نیز ورودی خارجی هستند.
اگر Application آنها را داخل Database Log کند و بعد در Query ناامن دیگری استفاده کند، حتی ممکن است Second-Order Injection ایجاد شود.
قاعده اصلی ساده است:
هر چیزی که خارج از Trust Boundary برنامه میآید باید Untrusted فرض شود.
آیا WordPress در برابر SQL Injection امن است؟
هسته WordPress مکانیزمها و APIهایی برای تعامل امنتر با Database ارائه میدهد، اما این به معنی غیرممکن بودن SQL Injection در کل اکوسیستم WordPress نیست.
Plugin یا Theme سفارشی میتواند Query ناامن بنویسد.
بنابراین حتی اگر WordPress Core بهروز باشد، یک افزونه آسیبپذیر میتواند امنیت Database را تحت تأثیر قرار دهد.
در سایتهای وردپرسی باید:
Core، Theme و Pluginها مرتب Update شوند و کدهای سفارشی نیز Review شوند.
$wpdb->prepare() در وردپرس چیست؟
در توسعه WordPress، متد:
$wpdb->prepare()
برای ساخت Queryهای Parameterized استفاده میشود.
نمونه مفهومی:
$query = $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE ID = %d",
$post_id
);
$result = $wpdb->get_row($query);
این روش بسیار بهتر از چسباندن مستقیم:
$post_id
داخل Query است.
توسعهدهنده Plugin یا Theme باید از APIهای استاندارد WordPress استفاده کند و از ساخت رشتههای SQL با Input خام کاربر خودداری کند.
آیا Sanitization بهتنهایی جلوی SQL Injection را میگیرد؟
Sanitization مفید است اما جای Parameterized Query را نمیگیرد.
برای مثال ممکن است ورودی قرار باشد Integer باشد.
برنامه میتواند آن را Validate کند که واقعاً عدد است.
اما Query نیز همچنان باید امن نوشته شود.
بهترین رویکرد:
Validation + Parameterization
است.
Validation مشخص میکند داده از نظر Business Logic قابل قبول است.
Parameterization مشخص میکند داده نمیتواند Syntax Query را تغییر دهد.
Validation و Sanitization چه تفاوتی دارند؟
Validation بررسی میکند داده شرایط مورد انتظار را دارد یا نه.
مثلاً سن باید عددی بین ۱۸ تا ۱۲۰ باشد.
Sanitization معمولاً داده را برای یک Context مشخص پاکسازی یا Normalize میکند.
در امنیت Database، نباید Sanitization را بهعنوان تنها دفاع در نظر گرفت.
Query Architecture مهمتر است.
چرا Blacklist روش خوبی نیست؟
فرض کنید برنامهنویس تعدادی Character یا Keyword را ممنوع کند.
ممکن است تصور شود با Block کردن چند واژه SQL مشکل حل میشود.
این روش بسیار شکننده است.
SQL Syntax، Encoding و Contextهای مختلف باعث میشوند Blacklist بهراحتی ناقص شود.
از نظر مهندسی امنیت، بهتر است داده اصلاً امکان تبدیل شدن به Syntax را نداشته باشد.
یعنی Parameterized Query.
ORMها و SQL Injection
Frameworkهای مدرن معمولاً ORM یا Query Builder دارند.
استفاده صحیح از آنها میتواند احتمال SQL Injection را کاهش دهد.
اما ORM تضمین جادویی امنیت نیست.
اگر برنامهنویس از قابلیت Raw Query استفاده کند و Input کاربر را مستقیماً به رشته SQL اضافه کند، آسیبپذیری میتواند دوباره ایجاد شود.
بنابراین حتی در Laravel، Django، Rails یا سایر Frameworkها نیز:
Raw SQL
باید با احتیاط استفاده شود.
آیا Stored Procedure جلوی SQL Injection را میگیرد؟
نه همیشه.
Stored Procedure میتواند طراحی امن داشته باشد، اما اگر داخل آن Dynamic SQL ناامن ساخته شود، همان مشکل دوباره ایجاد میشود.
امنیت به این بستگی دارد که داده چگونه وارد Query شود.
نام تکنولوژی بهتنهایی تعیینکننده نیست.
Principle of Least Privilege در دیتابیس
حتی اگر Application دچار SQL Injection شود، سطح خسارت میتواند تحت تأثیر Permission دیتابیس قرار بگیرد.
اگر Web Application فقط نیاز به:
SELECT، INSERT و UPDATE
روی چند Table مشخص دارد، نباید بدون دلیل Database User آن دسترسی مدیریتی کامل داشته باشد.
به این اصل:
Least Privilege
گفته میشود.
اگر Account برنامه نتواند ساختار Database را تغییر دهد، بعضی آثار احتمالی حمله نیز محدودتر میشوند.
آیا سایت باید با کاربر Root دیتابیس متصل شود؟
در Application عمومی معمولاً استفاده از Database Root Account برای Connection تصمیم بسیار نامناسبی است.
برنامه باید User اختصاصی با حداقل Permission موردنیاز داشته باشد.
اگر Credential Application افشا یا SQL Injection ایجاد شد، Access مهاجم نیز محدودتر خواهد بود.
آیا Prefix دیتابیس جلوی SQL Injection را میگیرد؟
خیر.
تغییر Prefix جداول ممکن است کمی اطلاعات ساختاری را کمتر قابل حدس کند، اما دفاع واقعی در برابر SQL Injection نیست.
اگر مهاجم قادر باشد Query را کنترل کند، تکیه بر نام غیرقابل پیشبینی جدول کافی نیست.
این نمونهای از:
Security Through Obscurity
است.
میتواند یک Layer جزئی باشد ولی جای Secure Coding را نمیگیرد.
WAF چگونه به مقابله با SQL Injection کمک میکند؟
Web Application Firewall میتواند بعضی Requestهای مشکوک را براساس Pattern یا Rule شناسایی و Block کند.
این موضوع برای:
Botها و Attackهای شناختهشده
مفید است.
اما WAF نباید بهانهای برای نگه داشتن کد آسیبپذیر باشد.
اگر یک Endpoint SQL Injection دارد، راهحل اصلی اصلاح Source Code است.
WAF یک Compensating Control محسوب میشود.
چرا WAF نمیتواند تضمین کامل بدهد؟
Applicationها Contextهای بسیار متفاوتی دارند.
ورودیای که برای یک سایت کاملاً قانونی است ممکن است برای سایت دیگر مشکوک باشد.
مهاجمان نیز میتوانند شکل Requestها را تغییر دهند.
به همین دلیل Signature-Based Detection همیشه کامل نیست.
همچنین اگر Attack از مسیر داخلی یا API Trusted انجام شود، بعضی Ruleها ممکن است اصلاً فعال نشوند.
نشانههای احتمالی SQL Injection در سایت
هیچ علامت واحدی وجود ندارد که بهتنهایی SQL Injection را اثبات کند.
بااینحال برخی رفتارها ارزش بررسی دارند.
برای مثال:
افزایش Errorهای Database، Requestهای غیرعادی به Parameterهای Dynamic، افزایش ناگهانی Query Time، خطاهای Syntax غیرمنتظره، تعداد زیاد Requestهای مشابه از IPهای خاص یا مشاهده الگوهای غیرعادی در WAF.
اما این Signalها باید توسط متخصص تحلیل شوند.
وجود Error SQL ممکن است صرفاً Bug برنامه باشد، نه Attack.
چرا Logها در تشخیص SQL Injection مهم هستند؟
Log میتواند به تیم امنیت نشان دهد:
چه Endpointی هدف قرار گرفته، Request چه زمانی ارسال شده، Response Code چه بوده، IP و User Agent چه بوده و آیا یک الگوی تکراری وجود دارد.
برای این منظور بهتر است:
Web Server Log، Application Log، WAF Log و Database Monitoring
در دسترس باشند.
البته نباید Password، Token یا اطلاعات بسیار حساس بدون نیاز داخل Log ذخیره شوند.
Database Error را به کاربر نمایش ندهید
فرض کنید Query با Error مواجه شود و Application همان Message اصلی Database را در صفحه نمایش دهد.
این پیام ممکن است اطلاعات مفیدی درباره:
Database Engine، Table، Column یا Query
افشا کند.
راه بهتر این است که کاربر پیام عمومی ببیند:
«خطایی رخ داده است.»
و جزئیات فنی در Log امنیتی ذخیره شوند.
Monitoring دیتابیس
برای پروژههای حساس، Database Activity Monitoring میتواند رفتارهای غیرمعمول را شناسایی کند.
مثلاً افزایش شدید Queryهای مشخص یا Access غیرمنتظره به Table حساس.
این ابزارها برای سازمانهای بزرگ اهمیت بیشتری دارند.
اما برای سایت کوچک نیز حداقل Monitoring Error و Resource Usage ارزشمند است.
SQL Injection و Data Breach
اگر SQLi امکان دسترسی غیرمجاز به اطلاعات ایجاد کند، Incident دیگر فقط یک Bug نرمافزاری نیست.
ممکن است Data Breach رخ داده باشد.
در چنین شرایطی باید مشخص شود:
چه اطلاعاتی در دسترس بوده، آیا Evidence برای استخراج داده وجود دارد، Incident چه زمانی شروع شده و چه حسابهایی درگیر بودهاند.
برای کسبوکارهایی که اطلاعات کاربران را نگهداری میکنند، این مسئله ممکن است علاوه بر جنبه فنی، الزامات حقوقی و قراردادی نیز داشته باشد.
اگر احتمال SQL Injection وجود دارد چه کنیم؟
اولین اقدام نباید آزمون تصادفی Payload روی سایت Production باشد.
در عوض تیم فنی باید Endpoint مشکوک را شناسایی و Source Code مربوط به Query را Review کند.
در صورت نیاز Application را در محیط Staging یا Test بررسی کنید.
سپس Queryهای Dynamic به Parameterized Query تبدیل شوند.
Database Permissionها و Logها نیز باید بررسی شوند.
اگر Evidence حمله واقعی وجود دارد، Incident Response باید فعال شود.
آیا باید سایت را خاموش کنیم؟
به شدت آسیبپذیری و احتمال Exploitation بستگی دارد.
اگر یک Endpoint عمومی و حساس SQL Injection تأییدشده دارد و Patch فوری در دسترس نیست، محدود کردن موقت Endpoint میتواند منطقیتر از باز نگه داشتن آن باشد.
روشها ممکن است شامل:
Disable کردن Feature، Require Authentication، محدود کردن Route یا اعمال Rule موقت WAF
باشند.
هدف این است که Exposure تا زمان Fix کاهش یابد.
چگونه SQL Injection را در کد پیدا کنیم؟
Code Review باید به دنبال Queryهایی باشد که با:
String Concatenation، String Interpolation یا ساخت Dynamic SQL
ایجاد میشوند.
بخشهایی که ورودی:
GET، POST، JSON، Cookie، Header یا Database Data
را وارد Query میکنند اولویت بالاتری دارند.
SAST و Security Scanner نیز میتوانند کمک کنند، ولی False Positive و False Negative ممکن است وجود داشته باشد.
تست امنیتی SQL Injection
تست باید فقط روی سامانهای انجام شود که مالک آن هستید یا مجوز صریح برای ارزیابی آن دارید.
در محیط حرفهای بهتر است از:
Staging، Test Database و داده مصنوعی
استفاده شود.
هدف Security Testing تأیید این است که ورودی کاربر نتواند Query Structure را تغییر دهد.
برای بیشتر تیمهای توسعه، Unit Test و Integration Test روی Queryهای حساس نیز ارزشمند است.
Code Review بهتر است یا Scanner؟
این دو مکمل یکدیگر هستند.
Scanner میتواند تعداد زیادی Route را سریع بررسی کند.
Code Review میتواند Context Business Logic و نحوه ساخته شدن Query را بهتر تشخیص دهد.
در سیستم حساس بهتر است:
SAST + DAST + Manual Review
در کنار هم استفاده شوند.
SAST چیست؟
SAST یا Static Application Security Testing کد را بدون اجرای واقعی Application برای الگوهای آسیبپذیر بررسی میکند.
مثلاً ممکن است مسیر ورود User Input به یک Query String را شناسایی کند.
مزیت آن امکان بررسی زودهنگام در CI/CD است.
DAST چیست؟
DAST یا Dynamic Application Security Testing رفتار Application در حال اجرا را بررسی میکند.
این نوع تست میتواند ضعفهایی را پیدا کند که در Runtime ظاهر میشوند.
اما DAST بهتنهایی نمیتواند تمام مسیرهای کد را ببیند.
Dependency Scanning
SQL Injection همیشه از کد نوشتهشده توسط تیم شما ایجاد نمیشود.
یک CMS، Framework یا Plugin قدیمی ممکن است Vulnerability شناختهشده داشته باشد.
بنابراین Dependency Monitoring و Update نیز مهم هستند.
برای سایت WordPress این موضوع مخصوصاً درباره Pluginها اهمیت دارد.
امنیت SQL Injection در WooCommerce
WooCommerce بهطور مستقیم با اطلاعات ارزشمندی مانند:
کاربر، سفارش، محصول و موجودی
درگیر است.
هسته و Extensionهای معتبر معمولاً از APIهای WordPress استفاده میکنند، اما Plugin سفارشی یا Add-on قدیمی همچنان میتواند Query ناامن داشته باشد.
برای فروشگاه بهتر است:
افزونههای ناشناس حذف شوند، Extensionها بهروز باشند و کد سفارشی توسط Developer آشنا با WordPress Security بررسی شود.
SQL Injection و افزونههای وردپرس
یکی از اشتباهات مدیران سایت نصب Pluginهای متعدد از منابع نامعتبر است.
هر Plugin میتواند Endpoint جدید، AJAX Handler یا API Route ایجاد کند.
اگر توسعهدهنده آن Query ناامن نوشته باشد، سطح حمله افزایش پیدا میکند.
به همین دلیل تعداد افزونهها بهتنهایی مشکل نیست؛ کیفیت و نگهداری آنها مهمتر است.
AJAX و SQL Injection در وردپرس
وردپرس از AJAX در بسیاری از Dashboard و Front-end Featureها استفاده میکند.
Endpointهای AJAX نیز باید:
Authorization، Nonce در جای مناسب، Validation و Query Safe
داشته باشند.
استفاده از Nonce بهتنهایی SQL Injection را حل نمیکند.
Nonce برای مسئله دیگری مانند جلوگیری از بعضی Request Forgeryها استفاده میشود.
Query همچنان باید Parameterized باشد.
آیا HTTPS جلوی SQL Injection را میگیرد؟
خیر.
HTTPS ارتباط بین Client و Server را رمزنگاری میکند.
اگر Server ورودی دریافتشده را به شکل ناامن داخل SQL استفاده کند، TLS جلوی آن را نمیگیرد.
این دو مشکل در لایههای متفاوت هستند.
HTTPS ضروری است، اما جای Secure Coding را نمیگیرد.
آیا 2FA جلوی SQL Injection را میگیرد؟
خیر.
2FA برای محافظت از Account Login طراحی شده است.
SQL Injection یک ضعف در Query Handling است.
البته اگر Endpoint آسیبپذیر فقط برای Administrator قابل دسترسی باشد، Authentication قوی Exposure را کاهش میدهد؛ اما Vulnerability همچنان باید Fix شود.
آیا Backup از SQL Injection جلوگیری میکند؟
خیر.
Backup یک Prevention Control برای SQLi نیست.
اما اگر Attack باعث حذف یا تغییر داده شود، Backup سالم میتواند Recovery را بسیار آسانتر کند.
این مثال خوبی از مفهوم دفاع چندلایه است.
Backups دیتابیس
برای Databaseهای مهم باید Backup منظم وجود داشته باشد.
نسخههای Backup بهتر است:
Off-site، چندنسخهای و Testشده
باشند.
اگر Attack باعث Data Modification شود، Restoration ممکن است لازم شود.
اما Restore کورکورانه Database میتواند دادههای جدید و قانونی را نیز حذف کند.
بنابراین قبل از Restore باید Timeline Incident بررسی شود.
Query Logging و اطلاعات حساس
فعال کردن General Query Log ممکن است برای Troubleshooting مفید باشد، اما در Production باید با احتیاط انجام شود.
Queryها ممکن است شامل اطلاعات حساس باشند و Log بسیار بزرگ تولید کنند.
بنابراین Logging باید هدفمند و با Retention مناسب باشد.
Secure Development Lifecycle
بهترین زمان رفع SQL Injection زمانی نیست که سایت هک شده است.
Security باید از زمان توسعه وارد پروژه شود.
یک چرخه مناسب شامل:
Threat Modeling، Secure Coding، Code Review، Security Testing و Patch Management
است.
اگر Parameterized Query از ابتدا Standard تیم باشد، احتمال ایجاد این دسته ضعف بسیار کاهش مییابد.
Secure Code Review Checklist
هنگام Review هر Query باید پرسید:
آیا Input خارجی وارد این Query میشود؟
آیا Query Parameterized است؟
آیا Dynamic Identifier از Allowlist میآید؟
آیا Database User حداقل Permission لازم را دارد؟
آیا Error حساس به کاربر نمایش داده میشود؟
این سؤالات ساده بسیاری از مشکلات را زودتر آشکار میکنند.
جلوگیری از SQL Injection در PHP
در PHP استفاده از:
PDO
یا:
MySQLi Prepared Statements
انتخاب استانداردی است.
برای Queryهای معمولی، ساختار باید ثابت بماند و Valueها Bind شوند.
از قرار دادن مستقیم:
$_GET
$_POST
یا دادههای مشابه
در رشته SQL خودداری کنید.
جلوگیری از SQL Injection در Laravel
در Laravel بهتر است تا حد امکان از:
Eloquent ORM
یا:
Query Builder
به شکل استاندارد استفاده شود.
Raw Expressionها فقط زمانی استفاده شوند که واقعاً لازم هستند.
هر Raw Query باید از نظر ورود User-controlled Data بررسی شود.
جلوگیری از SQL Injection در Python
Frameworkهایی مانند Django ORM سیستم Query امنی فراهم میکنند، مشروط بر اینکه توسعهدهنده بهجای ساخت SQL خام از APIهای استاندارد استفاده کند.
اگر Raw SQL لازم است، Parameter Binding باید استفاده شود.
String Formatting برای SQL تصمیم مناسبی نیست.
جلوگیری از SQL Injection در Node.js
Library دیتابیس باید از Parameterized Query یا Placeholder پشتیبانی کند.
از Template Literal برای ترکیب مستقیم داده User با SQL خودداری شود.
حتی اگر Input قبلاً در Front-end Validate شده است، Backend باید Validation مستقل داشته باشد.
چرا Front-end Validation کافی نیست؟
JavaScript مرورگر در اختیار کاربر است.
فرد میتواند Request را بدون استفاده از فرم اصلی ارسال کند.
بنابراین:
maxlength
یا محدودیت HTML
Control امنیتی Server نیست.
Validation اصلی همیشه باید در Backend اجرا شود.
امنیت Database Credential
Credential اتصال Database معمولاً داخل Environment Variable یا Configuration امن نگهداری میشود.
نباید:
در Repository عمومی، Front-end JavaScript یا فایل قابل دانلود
قرار داشته باشد.
اگر Credential افشا شد، باید Rotate شود.
همچنین User مربوطه فقط Permission موردنیاز Application را داشته باشد.
Secret Management
در زیرساخت حرفهای بهتر است Secretها توسط سیستم مناسب مدیریت شوند.
قرار دادن Password دیتابیس داخل:
Git Repository
یا:
Backup عمومی
ریسک بزرگی است.
حتی اگر SQL Injection نداشته باشید، افشای Database Credential میتواند مستقیماً Data Security را به خطر بیندازد.
امنیت سرور دیتابیس
Database بهتر است بدون نیاز از اینترنت عمومی قابل دسترسی نباشد.
اگر Application و Database روی Network خصوصی هستند، فقط Serverهای موردنیاز باید امکان اتصال داشته باشند.
Firewall و Network Segmentation سطح حمله را کاهش میدهند.
SQL Injection Application را اصلاح نمیکنند، اما در برابر سایر سناریوهای نفوذ مفید هستند.
آیا Database باید Remote Access داشته باشد؟
فقط در صورت نیاز.
اگر Remote Administration لازم است، بهتر است Access به IPها یا Networkهای مشخص محدود شود و Authentication قوی داشته باشد.
باز گذاشتن Database Port برای کل اینترنت بدون دلیل ریسک غیرضروری ایجاد میکند.
SQL Injection و Cloud Database
استفاده از Cloud Database بهخودیخود SQL Injection را حل نمیکند.
اگر Application Query ناامن ایجاد کند، Managed Database نیز همان Query را اجرا میکند.
Cloud Provider بخشی از زیرساخت را امن میکند؛ Secure Coding همچنان مسئولیت Application است.
جلوگیری چندلایه از SQL Injection
بهترین معماری تنها یک Control ندارد.
لایه اول:
Parameterized Queries.
لایه دوم:
Validation و Allowlist.
لایه سوم:
Least Privilege.
لایه چهارم:
WAF.
لایه پنجم:
Logging و Monitoring.
لایه ششم:
Backup و Incident Response.
اگر لایهای شکست بخورد، لایههای دیگر باید میزان خسارت را محدود کنند.
اشتباهات رایج در مقابله با SQL Injection
یکی از رایجترین اشتباهات این است که فقط ورودی فرم Login بررسی شود.
درحالیکه API، Cookie، Header و Backend Job نیز میتوانند Data خارجی دریافت کنند.
اشتباه دیگر اعتماد بیش از حد به WAF است.
برخی توسعهدهندگان نیز تصور میکنند ORM بهصورت خودکار همه Queryهای Raw را امن میکند.
استفاده از Database Root Account برای Application و نمایش SQL Error به کاربران از خطاهای مهم دیگر هستند.
اگر SQL Injection تأیید شد چه کارهایی انجام دهیم؟
ابتدا Endpoint آسیبپذیر را Fix یا موقتاً محدود کنید.
سپس Logs مرتبط با Web Server، Application، WAF و Database بررسی شوند تا مشخص شود آیا Exploitation انجام شده است.
اگر احتمال دسترسی به اطلاعات حساس وجود دارد، Incident باید بهعنوان Data Exposure احتمالی بررسی شود.
Credentialهای در معرض خطر Rotate شوند.
Database Permissionها بازبینی شوند.
Backupها بررسی شوند و پس از اطمینان از سلامت Application، Monitoring شدیدتر برای مدتی ادامه پیدا کند.
آیا بعد از SQL Injection باید رمز کاربران را تغییر داد؟
همیشه نه.
این تصمیم به این بستگی دارد که چه اطلاعاتی قابل دسترسی بودهاند.
اگر جدول User Credential در معرض خطر بوده است، Password Reset میتواند لازم باشد.
اگر Passwordها به شکل صحیح Hash شده باشند نیز ممکن است بسته به Algorithm و Risk Incident نیاز به Reset وجود داشته باشد.
تصمیم باید براساس Scope واقعی Breach گرفته شود.
Passwordهای دیتابیس چطور؟
اگر SQL Injection صرفاً امکان Query محدود ایجاد کرده باشد، الزاماً Credential اتصال دیتابیس افشا نشده است.
اما اگر Configuration File یا Server نیز Compromise شده باشد، Database Credential باید Rotate شود.
در Incident Response نباید بدون Evidence فرض کرد حمله فقط به SQLi محدود مانده است.
چگونه جلوی تکرار آسیبپذیری را بگیریم؟
فقط Fix کردن همان Endpoint کافی نیست.
اگر یک Developer در چند نقطه از پروژه الگوی ناامن مشابهی استفاده کرده باشد، احتمال وجود Vulnerabilityهای بیشتر هست.
پس باید کل Codebase برای همان Pattern بررسی شود.
همچنین Secure Coding Guideline تیم باید اصلاح شود و Static Analysis به CI/CD اضافه شود.
این کار Root Cause را هدف قرار میدهد.
تست Regression امنیتی
بعد از Fix باید Test ایجاد شود تا Vulnerability مشابه دوباره در نسخه آینده ایجاد نشود.
به این کار Security Regression Testing گفته میشود.
هر Bug امنیتی مهم باید به یک درس توسعهای تبدیل شود.
اگر فقط Patch انجام شود و Test اضافه نشود، ممکن است در Refactor بعدی همان مشکل برگردد.
SQL Injection و سئو
SQL Injection مستقیماً یک مسئله SEO نیست، اما نتیجه حمله میتواند SEO سایت را نیز آسیب بزند.
اگر مهاجم بتواند Database را تغییر دهد، ممکن است:
محتوای صفحات، لینکها، Titleها، URLها یا Redirectها
تغییر کنند.
همچنین Downtime یا نمایش Warning امنیتی میتواند اعتماد کاربران را کاهش دهد.
بنابراین امنیت Application بخشی از سلامت کلی سایت است.
SQL Injection و فروشگاه اینترنتی
در فروشگاه، Integrity اطلاعات اهمیت زیادی دارد.
حتی اگر اطلاعات کارت بانکی مستقیماً در سایت ذخیره نشوند، Database ممکن است شامل:
آدرس مشتری، سفارش، شماره تماس، ایمیل و اطلاعات مالی تجاری
باشد.
به همین دلیل Development Pluginهای سفارشی WooCommerce باید Code Review امنیتی داشته باشد.

چکلیست جلوگیری از حملات SQL Injection
- [ ] تمام Queryهای دارای ورودی خارجی Parameterized هستند.
- [ ] از String Concatenation برای ساخت Query با Input کاربر استفاده نمیشود.
- [ ] Dynamic Identifierها فقط از Allowlist انتخاب میشوند.
- [ ] ورودیها در Server Validate میشوند.
- [ ] Database User حداقل Permission موردنیاز را دارد.
- [ ] Application با Root Database Account اجرا نمیشود.
- [ ] Error خام Database به کاربران نمایش داده نمیشود.
- [ ] Logهای امنیتی و Application Monitoring فعال هستند.
- [ ] Framework، CMS، Plugin و Dependencyها بهروز هستند.
- [ ] کدهای Raw SQL بهصورت جداگانه Review میشوند.
- [ ] API، Cookie و Header نیز بهعنوان Input غیرقابل اعتماد در نظر گرفته میشوند.
- [ ] Backup دیتابیس منظم و قابل بازیابی وجود دارد.
- [ ] WAF بهعنوان لایه مکمل فعال است، نه جایگزین اصلاح کد.
- [ ] SAST و Security Testing در فرآیند توسعه وجود دارد.
- [ ] Regression Test برای ضعفهای امنیتی رفعشده اضافه میشود.
سوالات متداول درباره حملات SQL Injection
SQL Injection چیست؟
SQL Injection یک آسیبپذیری امنیتی است که زمانی ایجاد میشود که Application داده کنترلشده توسط کاربر را به شکلی ناامن وارد Query دیتابیس کند. در نتیجه ممکن است ساختار یا رفتار Query تغییر کند.
SQL Injection چه خطری دارد؟
بسته به سطح دسترسی برنامه و نوع آسیبپذیری، ممکن است اطلاعات غیرمجاز خوانده، تغییر یا حذف شوند. در بعضی سیستمها این موضوع میتواند به Data Breach جدی منجر شود.
بهترین روش جلوگیری از SQL Injection چیست؟
مهمترین اقدام استفاده از Parameterized Query یا Prepared Statement است. Validation، Least Privilege، WAF و Monitoring نیز باید در کنار آن استفاده شوند.
آیا Escape کردن ورودی کافی است؟
بهتر است دفاع اصلی روی Parameterized Queries بنا شود. Escape دستی ممکن است در Contextهای مختلف ناقص یا اشتباه پیادهسازی شود.
آیا وردپرس SQL Injection میگیرد؟
Core و APIهای WordPress روشهایی برای Query امن ارائه میکنند، اما Plugin، Theme یا کد سفارشی آسیبپذیر میتواند SQL Injection ایجاد کند.
آیا Wordfence یا فایروال جلوی SQL Injection را میگیرد؟
WAF ممکن است بسیاری از Requestهای شناختهشده را Block کند، اما نمیتواند جای اصلاح Query آسیبپذیر را بگیرد.
آیا HTTPS جلوی SQL Injection را میگیرد؟
خیر. HTTPS ارتباط را رمزنگاری میکند، درحالیکه SQL Injection مربوط به نحوه پردازش Input در Backend و Database است.
آیا 2FA جلوی SQLi را میگیرد؟
نه بهطور مستقیم. 2FA برای امنیت Login است و Query ناامن را اصلاح نمیکند.

Prepared Statement چیست؟
روشی است که SQL Structure و Valueهای داده جدا از یکدیگر تعریف میشوند. در نتیجه مقدار ورودی بهعنوان Data پردازش میشود و نمیتواند مستقیماً Syntax Query را تغییر دهد.
آیا ORM جلوی SQL Injection را میگیرد؟
در صورت استفاده صحیح خطر را بسیار کاهش میدهد. اما Raw Query یا String Concatenation داخل ORM همچنان میتواند آسیبپذیر باشد.
آیا تغییر Prefix دیتابیس امنیت SQL Injection را افزایش میدهد؟
این کار دفاع اصلی نیست. Secure Query و Parameterization بسیار مهمتر هستند.
آیا SQL Injection فقط در فرم Login اتفاق میافتد؟
خیر. Search، API، URL Parameter، Cookie، HTTP Header، Filter، AJAX و حتی داده ذخیرهشده قبلی میتوانند در صورت استفاده ناامن در Query مشکل ایجاد کنند.
اگر سایت SQL Injection داشت باید چه کنیم؟
Endpoint آسیبپذیر باید فوراً اصلاح یا محدود شود، سپس Logs و Database برای مشخص شدن Scope حمله بررسی شوند. بعد از آن Permissionها، Credentialها، Backupها و سایر Queryهای مشابه نیز باید بازبینی شوند.
جمعبندی
حملات SQL Injection هنوز یکی از مهمترین نمونههای ضعف در مدیریت ورودی و تعامل برنامه با Database محسوب میشوند. مشکل زمانی ایجاد میشود که Application اجازه دهد اطلاعات تحت کنترل کاربر روی ساختار Query اثر بگذارند.
مهمترین اصل امنیت بسیار ساده است:
داده را از دستور جدا کنید.
برای Valueهای SQL باید از:
Prepared Statements و Parameterized Queries
استفاده شود.
در بخشهایی که مقدار ورودی قرار است نام ستون، Sort Direction یا بخش ساختاری دیگری را تعیین کند، بهتر است از:
Allowlist
استفاده شود.
اما امنیت SQL Injection فقط با یک تغییر در Query کامل نمیشود. Database User باید حداقل Permission لازم را داشته باشد، Errorهای داخلی نباید به کاربر نمایش داده شوند، Logs باید در دسترس باشند و Application و Dependencyها مرتب بهروزرسانی شوند.
WAF نیز میتواند درخواستهای مشکوک را کاهش دهد، اما نمیتواند جای کد امن را بگیرد.
برای سایتهای وردپرسی، نصب Plugin و Theme از منابع معتبر و بهروزرسانی آنها اهمیت زیادی دارد. اگر توسعه اختصاصی انجام میشود، Queryهای مستقیم Database باید با APIهای استاندارد مانند $wpdb->prepare() ساخته شوند.
همچنین نباید فقط فرم Login را بررسی کرد. SQL Injection ممکن است در API، AJAX، Search، Cookie، HTTP Header، Backend Job و حتی دادهای که قبلاً در Database ذخیره شده است ظاهر شود.
اگر Vulnerability تأیید شد، صرف اصلاح یک خط کد کافی نیست. باید مشخص شود آیا از آن سوءاستفاده شده، چه اطلاعاتی در معرض دسترسی بوده، آیا Database تغییر کرده و آیا Credential یا سایر بخشهای Server تحت تأثیر قرار گرفتهاند.
در نهایت، بهترین دفاع مقابل SQL Injection ترکیبی از:
Secure Coding + Parameterized Queries + Validation + Least Privilege + Monitoring + WAF + Backup
است.
سایتی که این اصول را از مرحله توسعه رعایت کند بسیار کمتر از سایتی که تنها بعد از Incident به فکر امنیت میافتد در معرض خطر خواهد بود.