SQL Injection — نوعی حمله به پایگاه داده برنامه است که در آن مهاجم کد SQL مخرب را در پارامترهای درخواست تزریق کرده و به دادهها دسترسی غیرمجاز یا امکان تغییر آنها را به دست میآورد. بر اساس دادههای OWASP (2025)، SQL Injection یکی از آسیبپذیریهای بحرانی باقی مانده است که میتواند منجر به به خطر افتادن کامل پایگاه داده شود. تزریق کد SQL به مهاجم اجازه میدهد تا رکوردها را بخواند، تغییر دهد و حذف کند و در برخی موارد — به سیستم عامل سرور دسترسی پیدا کند.
نکات اصلی
SQL Injection آسیبپذیری است که زمانی ایجاد میشود که برنامه با استفاده از الحاق رشتهها با دادههای کاربر، درخواست SQL را تشکیل میدهد. مهاجم یک رشته ساخته شده ویژه را به پارامتر درخواست ارسال میکند که ساختار دستور SQL را تغییر میدهد. به جای داده بودن، رشته وارد شده بخشی از کد SQL میشود و به مهاجم امکان اجرای درخواستهای دلخواه به پایگاه داده را میدهد. طبق Verizon Data Breach Report (2025)، SQL Injection در ۸٪ از تمام نشتهای داده بررسی شده وجود دارد.
با وجود شناخته بودن این آسیبپذیری (اولین اشارهها — اواخر دهه ۱۹۹۰)، SQL Injection در برنامههای مدرن دیده میشود. علت — عامل انسانی: توسعهدهندگان اجازه الحاق رشته در کد را میدهند، کد قدیمی بازنویسی نمیشود و چارچوبهای ORM به اشتباه استفاده میشوند (مثلاً درخواستهای خام با جایگذاری مقادیر از طریق رشتهها). طبق دادههای Veracode (2026)، حدود ۱۴٪ از تمام برنامههای اسکن شده حداقل یک آسیبپذیری SQLi دارند.
SQL Injection موفق به مهاجم طیف گستردهای از قابلیتها را میدهد: خواندن هر جدولی از پایگاه داده، از جمله هش رمزهای عبور و دادههای شخصی کاربران؛ تغییر و حذف رکوردها؛ اجرای عملیات مدیریتی (DROP TABLE, TRUNCATE)؛ در برخی پیکربندیها — اجرای از راه دور دستورات از طریق xp_cmdshell (MSSQL) یا INTO OUTFILE (MySQL). عواقب — از نشت دادههای کاربر تا از دست دادن کامل کنترل بر سیستم.
تزریقهای SQL بر اساس روش استخراج داده از پایگاه داده طبقهبندی میشوند. انتخاب روش بستگی به نحوه پردازش نتایج درخواست و پیامهای خطا توسط برنامه دارد. طبقهبندی OWASP سه نوع اصلی را مشخص میکند: In-band (دادهها از طریق همان کانال استخراج میشوند)، Inferential/Blind (استنتاج منطقی) و Out-of-band (دادهها از طریق کانال دیگری منتقل میشوند).
| نوع | روش استخراج داده | پیچیدگی | فراوانی |
|---|---|---|---|
| In-band (کلاسیک) | مستقیماً از طریق نتیجه درخواست | کم | زیاد |
| Blind SQLi | استنتاج منطقی بر اساس پاسخهای سرور | زیاد | متوسط |
| Out-of-band | از طریق کانال خارجی (DNS, HTTP) | متوسط | کم |
رایجترین نوع. مهاجم کد SQL را در پارامتر درخواست تزریق میکند و نتیجه تزریق مستقیماً در پاسخ سرور قابل مشاهده است. دو زیرگونه: Error-based (از طریق پیامهای خطای پایگاه داده) و UNION-based (از طریق عملگر UNION SELECT). Error-based از اطلاعات پیامهای خطا استفاده میکند، مثلاً خطای نحوی MySQL که میتواند نام جدول یا ساختار درخواست را فاش کند. UNION-based امکان ترکیب نتایج یک درخواست قانونی با دادههای جداول دیگر پایگاه داده را فراهم میکند.
زمانی استفاده میشود که برنامه نتایج درخواست یا پیامهای خطا را نمایش نمیدهد. مهاجم با ارسال درخواستهایی با شرایط منطقی و تحلیل تفاوت در پاسخ سرور (مثلاً زمان پاسخ یا محتوای صفحه) سؤالات «بله/خیر» میپرسد. Time-based Blind SQLi از تابع تأخیر (SLEEP, WAITFOR DELAY) برای تأیید شرایط استفاده میکند — اگر صفحه بیشتر طول بکشد، شرط درست است. این روش بسیار کند است — استخراج یک رکورد ممکن است ساعتها طول بکشد.
# مثال Blind SQL Injection (time-based)
# اگر SQLi آسیبپذیر باشد، SLEEP(2) در صورت شرط اجرا میشود
import requests
import time
payload = "' OR IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(2),0) -- "
url = "http://example.com/user?id=1" + payload
start = time.time()
response = requests.get(url)
elapsed = time.time() - start
# اگر پاسخ بعد از >۲ ثانیه آمد — حرف اول رمز عبور 'a' است
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")
دادهها نه از طریق پاسخ HTTP، بلکه از طریق کانالهای جایگزین منتقل میشوند: درخواستهای DNS، درخواستهای HTTP به سرور خارجی، SMTP. زمانی استفاده میشود که برنامه نتایج درخواست را بر نمیگرداند و خطاها را نشان نمیدهد. MySQL از تابع LOAD_FILE() پشتیبانی میکند که میتواند درخواست DNS را آغاز کند و MSSQL — xp_dirtree برای ارسال داده به سرور SMB راه دور. Out-of-band SQL Injection مؤثر است اما به تنظیمات اضافی در سرور مهاجم و وجود توابع خاص پایگاه داده نیاز دارد.
مکانیزم SQL Injection بر این اساس است که SQL از نقل قول برای ثابتهای رشتهای استفاده میکند. اگر برنامه ورودی کاربر را مستقیماً بدون فرار (escape) در درخواست SQL جایگذاری کند، مهاجم میتواند رشته را «ببندد» و کد SQL دلخواه اضافه کند. مثلاً در درخواست SELECT * FROM users WHERE name = '$input' جایگذاری ' OR '1'='1 درخواست را به SELECT * FROM users WHERE name = '' OR '1'='1' تبدیل میکند که تمام کاربران را برمیگرداند.
فرم ورود با درخواست SELECT * FROM users WHERE username = '$user' AND password = '$pass' را در نظر بگیرید. اگر مهاجم در فیلد username admin' -- و رمز عبور را خالی وارد کند، درخواست حاصل به SELECT * FROM users WHERE username = 'admin' -- ' AND password = '' تبدیل میشود. کاراکترهای -- باقی درخواست را کامنت میکنند و شرط بررسی رمز عبور غیرفعال میشود. سرور رکورد کاربر admin را برمیگرداند و مهاجم بدون دانستن رمز عبور وارد سیستم میشود.
# مثال SQL Injection — دور زدن احراز هویت
# کد آسیبپذیر: الحاق مستقیم رشته
def login_vulnerable(username, password):
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
cursor.execute(query)
return cursor.fetchone() is not None
# username = "admin' --" بررسی رمز عبور را خنثی میکند
# نسخه امن: درخواست پارامترسازی شده
def login_secure(username, password):
query = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(query, (username, password))
return cursor.fetchone() is not None
عملگر UNION SELECT امکان ترکیب نتایج دو درخواست SELECT را فراهم میکند. اگر مهاجم یک پارامتر آسیبپذیر پیدا کند، UNION SELECT را با درخواست از جدول دیگر اضافه میکند. مثال: ' UNION SELECT username, password FROM admins --. شرط موفقیت — تعداد ستونها در هر دو درخواست باید یکسان باشد. تعداد ستونها از طریق ORDER BY تعیین میشود (جایگذاری ' ORDER BY 1--، سپس ۲، ۳... تا خطا). با دانستن تعداد ستونها، مهاجم UNION SELECT با همان تعداد فیلد جایگذاری میکند.
برنامههای موبایل با SQL Injection هم در سمت سرور (API) و هم در سمت کلاینت — در پایگاههای داده محلی (SQLite, Realm) مواجه میشوند. اگرچه خطر SQLi سمت سرور در API موبایل مشابه برنامههای وب است، پایگاه داده محلی یک بردار اضافی ایجاد میکند. اگر برنامه دادهها را در SQLite ذخیره کرده و درخواستها را با الحاق رشته اجرا کند، دادههای مخربی که از طریق API وارد پایگاه داده محلی شدهاند میتوانند در پردازش بعدی باعث SQLi شوند.
پایگاه داده SQLite محلی روی دستگاه نیز در برابر SQL Injection آسیبپذیر است اگر برنامه درخواستها را با الحاق رشته تشکیل دهد. Content Providers در Android و Core Data در iOS به طور پیشفرض از پارامترسازی استفاده میکنند، اما درخواستهای خام نیاز به توجه توسعهدهنده دارند. SQLite از چندین درخواست از طریق نقطهویرگول پشتیبانی نمیکند که تواناییهای مهاجم را محدود میکند، اما از خواندن دادهها از طریق شرایط WHERE محافظت نمیکند. همیشه از selectionArgs در Android و NSPredicate با پارامترها در iOS استفاده کنید.
API که برنامه موبایل به آن متصل میشود به اندازه هر سرور وب دیگری آسیبپذیر است. توسعهدهندگان موبایل اغلب فکر میکنند SQLi فقط مشکل بکاند است، اما آسیبپذیری در سطح endpoint API که پارامترها را از کلاینت دریافت میکند ایجاد میشود. تقسیم مسئولیت محافظت نمیکند: اگر توسعهدهنده بکاند فراموش کرده باشد درخواست را پارامترسازی کند، برنامه موبایل کاربر به بردار حمله تبدیل میشود. از بکاند استفاده از ORM یا prepared statements را بخواهید.
// مثال SQL Injection در SQLite محلی در Android
// کد آسیبپذیر: الحاق مستقیم
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = $userId", null
)
}
// نسخه امن: پارامترسازی از طریق selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = ?",
arrayOf(userId)
)
}
محافظت در برابر SQL Injection بر اساس یک اصل ساده است: هرگز به ورودی کاربر در درخواستهای SQL اعتماد نکنید. تنها روش قابل اعتماد — پارامترسازی درخواستها (prepared statements) است که در آن کد SQL و دادهها جداگانه ارسال میشوند. تمام روشهای دیگر — فرار (escaping)، اعتبارسنجی، WAF — لایههای اضافی محافظت هستند اما جایگزین پارامترسازی نمیشوند. طبق دادههای OWASP (2025)، پارامترسازی از ۱۰۰٪ حملات SQL Injection جلوگیری میکند.
هنگام استفاده از prepared statements، درخواست SQL ابتدا بدون دادهها توسط سرور پایگاه داده کامپایل میشود و سپس مقادیر پارامترها جداگانه ارسال میشوند. پایگاه داده پارامترها را به عنوان داده در نظر میگیرد نه کد اجرایی. حتی اگر مهاجم ' OR '1'='1 ارسال کند، پایگاه داده آن را به عنوان مقدار رشته تفسیر میکند نه کد SQL. Prepared statements توسط تمام زبانها و چارچوبهای مدرن پشتیبانی میشوند: PDO در PHP، PreparedStatement در Java، cursor.execute در Python.
ORMهای مدرن (Hibernate, Entity Framework, SQLAlchemy, Room) هنگام اجرای درخواستها به طور خودکار از پارامترسازی استفاده میکنند، مگر اینکه توسعهدهنده به درخواستهای خام روی آورد. با این حال ORM کاملاً محافظت نمیکند: ساختارهایی مانند @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) در JPA نیاز به ارسال پارامترها از طریق پارامترهای نامگذاری شده دارند نه الحاق. Query Builderها (Knex, jOOQ) نیز در صورت عدم استفاده از روشهای خام، درخواستها را به طور پیشفرض پارامترسازی میکنند.
فرار از کاراکترهای ویژه (mysql_real_escape_string) — روش قدیمی که در برابر همه انواع SQL Injection محافظت نمیکند. مشکل: فرار به encoding بستگی دارد و میتواند با استفاده از encodingهای چندبایتی دور زده شود (مثلاً GBK در سیستمهای آسیایی). فقط در کد قدیمی که پارامترسازی ممکن نیست و همیشه همراه با اعتبارسنجی دقیق نوع داده ورودی از فرار استفاده کنید.
| روش | اثربخشی | توصیه |
|---|---|---|
| Prepared Statements | ۱۰۰٪ | اجباری برای همه درخواستها |
| ORM (استفاده صحیح) | ۹۹٪ | توصیه میشود |
| فرار از رشته | ۷۰٪ (بستگی به encoding دارد) | فقط کد قدیمی |
| اعتبارسنجی ورودی (لیست سفید) | ۵۰٪ (فقط برای اعداد) | اضافی |
| WAF (دیوار آتش برنامه وب) | ۶۰٪ | اضافی |
حساب برنامه باید حداقل دسترسیهای لازم را داشته باشد: SELECT, INSERT, UPDATE, DELETE — فقط روی جدولهایی که واقعاً برای برنامه لازم است. استفاده از DROP, TRUNCATE, CREATE را برای حساب برنامه ممنوع کنید. این کار حتی در صورت موفقیت SQL Injection خسارت را محدود میکند: مهاجم نمیتواند جدولها را حذف یا عملیات مدیریتی انجام دهد.
تست منظم برای SQL Injection باید بخشی از خط لوله توسعه امن باشد. ترکیب تحلیل ایستا، اسکن پویا و تست نفوذ دستی بهترین نتایج را میدهد. طبق Synopsys Cybersecurity Report (2025)، اسکنرهای خودکار تا ۷۰٪ آسیبپذیریهای SQLi را پیدا میکنند، اما حملات پیچیده Blind نیاز به تست دستی دارند.
برای برنامههای موبایل، تحلیل SQLite محلی نیز مهم است: تمام فراخوانیهای rawQuery، درخواستهای ContentProvider و درخواستهای Room با rawQuery را بررسی کنید. ابزارها: Android Studio Lint (SQLi در SQLite را تشخیص میدهد)، MobSF (Mobile Security Framework) برای تحلیل ایستا و پویای خودکار APK/IPA. همچنین توصیه میشود endpointهای API را از طریق sqlmap با پروکسی رهگیر ترافیک برنامه موبایل تست کنید.
سوالات متداول
SQL Injection به پایگاههای داده رابطهای از طریق درخواستهای SQL حمله میکند. NoSQL Injection بر پایگاههای داده غیررابطهای (MongoDB, Couchbase) از طریق عملگرهای درخواست آنها ($gte, $ne, $where) تأثیر میگذارد. در MongoDB تزریق زمانی ممکن است که برنامه سند BSON را از رشته JSON تشکیل دهد. مکانیزمهای محافظت مشابه هستند: پارامترسازی و اعتبارسنجی نوع.
تمام مکانهایی که درخواستهای SQL از طریق الحاق رشته با دادههای کاربر تشکیل میشوند را پیدا کنید. به دنبال الگوهایی مانند "SELECT ... WHERE id = " + userId یا f"UPDATE ... SET name = '{name}'" بگردید. هر چنین رشتی یک تزریق SQL بالقوه است. همه را با درخواستهای پارامترسازی شده یا prepared statements جایگزین کنید.
چارچوبهای ORM فقط در صورتی که از روشهای Query Builder و پارامترهای نامگذاری شده استفاده کنید به طور خودکار محافظت میکنند. اگر از درخواستهای خام (nativeQuery در JPA، rawQuery در Room) استفاده کنید، محافظت کار نمیکند — باید پارامترها را از طریق عبارات آماده ارسال کنید نه از طریق الحاق رشته.
Second-Order SQL Injection حملهای است که در آن دادههای مخرب به عنوان ایمن در پایگاه داده ذخیره میشوند اما سپس در درخواست دیگری بدون فرار استفاده میشوند. مثلاً مهاجم با username ' OR '1'='1 ثبت نام میکند. دادهها به عنوان رشته ذخیره میشوند — هنگام ثبت نام حملهای رخ نمیدهد. اما اگر درخواست دیگری username را بدون پارامترسازی در SQL جایگذاری کند، تزریق فعال میشود.
NoSQL Injection به دلیل آگاهی کمتر توسعهدهندگان بالقوه خطرناکتر است. توسعهدهندگان درباره SQL Injection میدانند و اکثراً از ORM استفاده میکنند، اما تعداد کمی درباره NoSQL Injection میدانند. در MongoDB یک درخواست نادرست میتواند تمام اسناد مجموعه را برگرداند. محافظت — همان prepared statements (پارامترسازی BSON) و اعتبارسنجی دقیق دادههای ورودی.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید