SQL Injection در توسعه موبایل: چیست، روش‌های حمله و محافظت

نویسنده: IT Sectr منتشر شده: 2026-04-06 زمان مطالعه: 9 دقیقه

SQL Injection — نوعی حمله به پایگاه داده برنامه است که در آن مهاجم کد SQL مخرب را در پارامترهای درخواست تزریق کرده و به داده‌ها دسترسی غیرمجاز یا امکان تغییر آنها را به دست می‌آورد. بر اساس داده‌های OWASP (2025)، SQL Injection یکی از آسیب‌پذیری‌های بحرانی باقی مانده است که می‌تواند منجر به به خطر افتادن کامل پایگاه داده شود. تزریق کد SQL به مهاجم اجازه می‌دهد تا رکوردها را بخواند، تغییر دهد و حذف کند و در برخی موارد — به سیستم عامل سرور دسترسی پیدا کند.

نکات اصلی

  • SQL Injection — تزریق کد SQL از طریق پارامترهای کاربر که منطق درخواست به پایگاه داده را تغییر می‌دهد
  • پارامترسازی درخواست‌ها — روش اصلی محافظت: جدا کردن کد SQL از داده‌ها با استفاده از prepared statements
  • انواع حملات — تزریق کلاسیک (از طریق WHERE)، Blind SQLi (استنتاج منطقی)، UNION-based (خواندن جداول دیگر)
  • Error-based SQLi — استخراج داده‌ها از طریق پیام‌های خطای پایگاه داده
  • چارچوب‌های ORM — در صورت استفاده صحیح خطر SQLi را کاهش می‌دهند اما آن را کاملاً از بین نمی‌برند

SQL Injection چیست؟

SQL Injection آسیب‌پذیری است که زمانی ایجاد می‌شود که برنامه با استفاده از الحاق رشته‌ها با داده‌های کاربر، درخواست SQL را تشکیل می‌دهد. مهاجم یک رشته ساخته شده ویژه را به پارامتر درخواست ارسال می‌کند که ساختار دستور SQL را تغییر می‌دهد. به جای داده بودن، رشته وارد شده بخشی از کد SQL می‌شود و به مهاجم امکان اجرای درخواست‌های دلخواه به پایگاه داده را می‌دهد. طبق Verizon Data Breach Report (2025)، SQL Injection در ۸٪ از تمام نشت‌های داده بررسی شده وجود دارد.

چرا SQL Injection هنوز هم مطرح است؟

با وجود شناخته بودن این آسیب‌پذیری (اولین اشاره‌ها — اواخر دهه ۱۹۹۰)، SQL Injection در برنامه‌های مدرن دیده می‌شود. علت — عامل انسانی: توسعه‌دهندگان اجازه الحاق رشته در کد را می‌دهند، کد قدیمی بازنویسی نمی‌شود و چارچوب‌های ORM به اشتباه استفاده می‌شوند (مثلاً درخواست‌های خام با جایگذاری مقادیر از طریق رشته‌ها). طبق داده‌های Veracode (2026)، حدود ۱۴٪ از تمام برنامه‌های اسکن شده حداقل یک آسیب‌پذیری SQLi دارند.

مهاجم چه کاری می‌تواند انجام دهد؟

SQL Injection موفق به مهاجم طیف گسترده‌ای از قابلیت‌ها را می‌دهد: خواندن هر جدولی از پایگاه داده، از جمله هش رمزهای عبور و داده‌های شخصی کاربران؛ تغییر و حذف رکوردها؛ اجرای عملیات مدیریتی (DROP TABLE, TRUNCATE)؛ در برخی پیکربندی‌ها — اجرای از راه دور دستورات از طریق xp_cmdshell (MSSQL) یا INTO OUTFILE (MySQL). عواقب — از نشت داده‌های کاربر تا از دست دادن کامل کنترل بر سیستم.

انواع تزریق SQL

تزریق‌های SQL بر اساس روش استخراج داده از پایگاه داده طبقه‌بندی می‌شوند. انتخاب روش بستگی به نحوه پردازش نتایج درخواست و پیام‌های خطا توسط برنامه دارد. طبقه‌بندی OWASP سه نوع اصلی را مشخص می‌کند: In-band (داده‌ها از طریق همان کانال استخراج می‌شوند)، Inferential/Blind (استنتاج منطقی) و Out-of-band (داده‌ها از طریق کانال دیگری منتقل می‌شوند).

نوعروش استخراج دادهپیچیدگیفراوانی
In-band (کلاسیک)مستقیماً از طریق نتیجه درخواستکمزیاد
Blind SQLiاستنتاج منطقی بر اساس پاسخ‌های سرورزیادمتوسط
Out-of-bandاز طریق کانال خارجی (DNS, HTTP)متوسطکم

In-band SQL Injection (کلاسیک)

رایج‌ترین نوع. مهاجم کد SQL را در پارامتر درخواست تزریق می‌کند و نتیجه تزریق مستقیماً در پاسخ سرور قابل مشاهده است. دو زیرگونه: Error-based (از طریق پیام‌های خطای پایگاه داده) و UNION-based (از طریق عملگر UNION SELECT). Error-based از اطلاعات پیام‌های خطا استفاده می‌کند، مثلاً خطای نحوی MySQL که می‌تواند نام جدول یا ساختار درخواست را فاش کند. UNION-based امکان ترکیب نتایج یک درخواست قانونی با داده‌های جداول دیگر پایگاه داده را فراهم می‌کند.

Blind SQL Injection (کور)

زمانی استفاده می‌شود که برنامه نتایج درخواست یا پیام‌های خطا را نمایش نمی‌دهد. مهاجم با ارسال درخواست‌هایی با شرایط منطقی و تحلیل تفاوت در پاسخ سرور (مثلاً زمان پاسخ یا محتوای صفحه) سؤالات «بله/خیر» می‌پرسد. Time-based Blind SQLi از تابع تأخیر (SLEEP, WAITFOR DELAY) برای تأیید شرایط استفاده می‌کند — اگر صفحه بیشتر طول بکشد، شرط درست است. این روش بسیار کند است — استخراج یک رکورد ممکن است ساعت‌ها طول بکشد.

python
# مثال 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'")

Out-of-band SQL Injection

داده‌ها نه از طریق پاسخ HTTP، بلکه از طریق کانال‌های جایگزین منتقل می‌شوند: درخواست‌های DNS، درخواست‌های HTTP به سرور خارجی، SMTP. زمانی استفاده می‌شود که برنامه نتایج درخواست را بر نمی‌گرداند و خطاها را نشان نمی‌دهد. MySQL از تابع LOAD_FILE() پشتیبانی می‌کند که می‌تواند درخواست DNS را آغاز کند و MSSQL — xp_dirtree برای ارسال داده به سرور SMB راه دور. Out-of-band SQL Injection مؤثر است اما به تنظیمات اضافی در سرور مهاجم و وجود توابع خاص پایگاه داده نیاز دارد.

تزریق SQL چگونه کار می‌کند؟

مکانیزم 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 را برمی‌گرداند و مهاجم بدون دانستن رمز عبور وارد سیستم می‌شود.

python
# مثال 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-based: خواندن جداول دیگر

عملگر UNION SELECT امکان ترکیب نتایج دو درخواست SELECT را فراهم می‌کند. اگر مهاجم یک پارامتر آسیب‌پذیر پیدا کند، UNION SELECT را با درخواست از جدول دیگر اضافه می‌کند. مثال: ' UNION SELECT username, password FROM admins --. شرط موفقیت — تعداد ستون‌ها در هر دو درخواست باید یکسان باشد. تعداد ستون‌ها از طریق ORDER BY تعیین می‌شود (جایگذاری ' ORDER BY 1--، سپس ۲، ۳... تا خطا). با دانستن تعداد ستون‌ها، مهاجم UNION SELECT با همان تعداد فیلد جایگذاری می‌کند.

SQL Injection در برنامه‌های موبایل

برنامه‌های موبایل با SQL Injection هم در سمت سرور (API) و هم در سمت کلاینت — در پایگاه‌های داده محلی (SQLite, Realm) مواجه می‌شوند. اگرچه خطر SQLi سمت سرور در API موبایل مشابه برنامه‌های وب است، پایگاه داده محلی یک بردار اضافی ایجاد می‌کند. اگر برنامه داده‌ها را در SQLite ذخیره کرده و درخواست‌ها را با الحاق رشته اجرا کند، داده‌های مخربی که از طریق API وارد پایگاه داده محلی شده‌اند می‌توانند در پردازش بعدی باعث SQLi شوند.

SQL Injection در SQLite در Android/iOS

پایگاه داده SQLite محلی روی دستگاه نیز در برابر SQL Injection آسیب‌پذیر است اگر برنامه درخواست‌ها را با الحاق رشته تشکیل دهد. Content Providers در Android و Core Data در iOS به طور پیش‌فرض از پارامترسازی استفاده می‌کنند، اما درخواست‌های خام نیاز به توجه توسعه‌دهنده دارند. SQLite از چندین درخواست از طریق نقطه‌ویرگول پشتیبانی نمی‌کند که توانایی‌های مهاجم را محدود می‌کند، اما از خواندن داده‌ها از طریق شرایط WHERE محافظت نمی‌کند. همیشه از selectionArgs در Android و NSPredicate با پارامترها در iOS استفاده کنید.

SQL Injection از طریق API برنامه موبایل

API که برنامه موبایل به آن متصل می‌شود به اندازه هر سرور وب دیگری آسیب‌پذیر است. توسعه‌دهندگان موبایل اغلب فکر می‌کنند SQLi فقط مشکل بک‌اند است، اما آسیب‌پذیری در سطح endpoint API که پارامترها را از کلاینت دریافت می‌کند ایجاد می‌شود. تقسیم مسئولیت محافظت نمی‌کند: اگر توسعه‌دهنده بک‌اند فراموش کرده باشد درخواست را پارامترسازی کند، برنامه موبایل کاربر به بردار حمله تبدیل می‌شود. از بک‌اند استفاده از ORM یا prepared statements را بخواهید.

kotlin
// مثال 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

محافظت در برابر SQL Injection بر اساس یک اصل ساده است: هرگز به ورودی کاربر در درخواست‌های SQL اعتماد نکنید. تنها روش قابل اعتماد — پارامترسازی درخواست‌ها (prepared statements) است که در آن کد SQL و داده‌ها جداگانه ارسال می‌شوند. تمام روش‌های دیگر — فرار (escaping)، اعتبارسنجی، WAF — لایه‌های اضافی محافظت هستند اما جایگزین پارامترسازی نمی‌شوند. طبق داده‌های OWASP (2025)، پارامترسازی از ۱۰۰٪ حملات SQL Injection جلوگیری می‌کند.

Prepared Statements (درخواست‌های پارامترسازی شده)

هنگام استفاده از prepared statements، درخواست SQL ابتدا بدون داده‌ها توسط سرور پایگاه داده کامپایل می‌شود و سپس مقادیر پارامترها جداگانه ارسال می‌شوند. پایگاه داده پارامترها را به عنوان داده در نظر می‌گیرد نه کد اجرایی. حتی اگر مهاجم ' OR '1'='1 ارسال کند، پایگاه داده آن را به عنوان مقدار رشته تفسیر می‌کند نه کد SQL. Prepared statements توسط تمام زبان‌ها و چارچوب‌های مدرن پشتیبانی می‌شوند: PDO در PHP، PreparedStatement در Java، cursor.execute در Python.

چارچوب‌های ORM و Query Builderها

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

تست منظم برای SQL Injection باید بخشی از خط لوله توسعه امن باشد. ترکیب تحلیل ایستا، اسکن پویا و تست نفوذ دستی بهترین نتایج را می‌دهد. طبق Synopsys Cybersecurity Report (2025)، اسکنرهای خودکار تا ۷۰٪ آسیب‌پذیری‌های SQLi را پیدا می‌کنند، اما حملات پیچیده Blind نیاز به تست دستی دارند.

  • sqlmap — محبوب‌ترین ابزار برای تشخیص خودکار و بهره‌برداری از SQL Injection
  • OWASP ZAP — اسکنر DAST رایگان با ماژول‌های اسکن فعال SQLi
  • Burp Suite Scanner — ابزار حرفه‌ای با تشخیص خودکار SQL Injection
  • SonarQube — تحلیل ایستای کد برای آسیب‌پذیری‌ها، شامل الگوهای SQLi
  • CodeQL — تحلیل معنایی کد برای جستجوی تزریق SQL در کد منبع

برای برنامه‌های موبایل، تحلیل SQLite محلی نیز مهم است: تمام فراخوانی‌های rawQuery، درخواست‌های ContentProvider و درخواست‌های Room با rawQuery را بررسی کنید. ابزارها: Android Studio Lint (SQLi در SQLite را تشخیص می‌دهد)، MobSF (Mobile Security Framework) برای تحلیل ایستا و پویای خودکار APK/IPA. همچنین توصیه می‌شود endpointهای API را از طریق sqlmap با پروکسی رهگیر ترافیک برنامه موبایل تست کنید.

سوالات متداول

تفاوت بین SQL Injection و NoSQL Injection چیست؟

SQL Injection به پایگاه‌های داده رابطه‌ای از طریق درخواست‌های SQL حمله می‌کند. NoSQL Injection بر پایگاه‌های داده غیررابطه‌ای (MongoDB, Couchbase) از طریق عملگرهای درخواست آنها ($gte, $ne, $where) تأثیر می‌گذارد. در MongoDB تزریق زمانی ممکن است که برنامه سند BSON را از رشته JSON تشکیل دهد. مکانیزم‌های محافظت مشابه هستند: پارامترسازی و اعتبارسنجی نوع.

چگونه SQL Injection را در کد خود تشخیص دهیم؟

تمام مکان‌هایی که درخواست‌های SQL از طریق الحاق رشته با داده‌های کاربر تشکیل می‌شوند را پیدا کنید. به دنبال الگوهایی مانند "SELECT ... WHERE id = " + userId یا f"UPDATE ... SET name = '{name}'" بگردید. هر چنین رشتی یک تزریق SQL بالقوه است. همه را با درخواست‌های پارامترسازی شده یا prepared statements جایگزین کنید.

آیا ORM به طور خودکار از SQL Injection محافظت می‌کند؟

چارچوب‌های ORM فقط در صورتی که از روش‌های Query Builder و پارامترهای نام‌گذاری شده استفاده کنید به طور خودکار محافظت می‌کنند. اگر از درخواست‌های خام (nativeQuery در JPA، rawQuery در Room) استفاده کنید، محافظت کار نمی‌کند — باید پارامترها را از طریق عبارات آماده ارسال کنید نه از طریق الحاق رشته.

Second-Order SQL Injection چیست؟

Second-Order SQL Injection حمله‌ای است که در آن داده‌های مخرب به عنوان ایمن در پایگاه داده ذخیره می‌شوند اما سپس در درخواست دیگری بدون فرار استفاده می‌شوند. مثلاً مهاجم با username ' OR '1'='1 ثبت نام می‌کند. داده‌ها به عنوان رشته ذخیره می‌شوند — هنگام ثبت نام حمله‌ای رخ نمی‌دهد. اما اگر درخواست دیگری username را بدون پارامترسازی در SQL جایگذاری کند، تزریق فعال می‌شود.

آیا NoSQL Injection می‌تواند خطرناک‌تر از SQL Injection باشد؟

NoSQL Injection به دلیل آگاهی کمتر توسعه‌دهندگان بالقوه خطرناک‌تر است. توسعه‌دهندگان درباره SQL Injection می‌دانند و اکثراً از ORM استفاده می‌کنند، اما تعداد کمی درباره NoSQL Injection می‌دانند. در MongoDB یک درخواست نادرست می‌تواند تمام اسناد مجموعه را برگرداند. محافظت — همان prepared statements (پارامترسازی BSON) و اعتبارسنجی دقیق داده‌های ورودی.

خلاصه

  • SQL Injection — حمله تزریق کد SQL از طریق پارامترهای کاربر فرار نشده در درخواست‌های پایگاه داده
  • انواع اصلی — In-band (کلاسیک)، Blind (کور، زمانی)، Out-of-band (از طریق کانال خارجی)
  • پارامترسازی درخواست‌ها — تنها روش ۱۰۰٪ قابل اعتماد محافظت در برابر SQL Injection
  • افسانه ORM — ORM فقط با روش‌های داخلی محافظت می‌کند، درخواست‌های خام با الحاق همچنان آسیب‌پذیرند
  • اصل حداقل دسترسی — محدود کردن دسترسی‌های حساب پایگاه داده خسارت را در حمله موفق به حداقل می‌رساند
  • تست منظم — sqlmap, OWASP ZAP, SonarQube باید بخشی از خط لوله CI/CD باشند
  • SQLite محلی — برنامه‌های موبایل نیز باید درخواست‌ها به پایگاه داده محلی را پارامترسازی کنند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید