تصور کن یه روز عادی رو میگذرونی و توی کافیشاپ محبوبت نشستی و مشغول چک کردن شبکههای اجتماعی و ایمیلات هستی. همون لحظه، یهو با یه مشکل مواجه میشی: حسابت هک شده! حالا سوال اینجاست، چطور این اتفاق افتاده؟ پاسخ ممکنه در یک نوع حمله به نام SQL Injection باشه. در این مقاله قصد داریم تا به صورت کامل و جامع درباره SQL Injection صحبت کنیم. آمادهای؟ بزن بریم!
برگردیم به اواخر دهه 90، زمانی که اینترنت تازه داشت پا میگرفت و وبسایتها ساده و ابتدایی بودن. اون روزها کسی فکرش رو نمیکرد که یک روز حملات سایبری به این شدت گسترش پیدا کنن. SQL، یک زبان پرسش و جستجوی پایگاه داده است که به برنامهنویسان این امکان رو میده تا دادهها رو مدیریت و دستکاری کنن.
اما خیلی زود، هکرها فهمیدن که میتونن از همین زبان پرسش برای نفوذ به سیستمها استفاده کنن. اولین حملات SQL Injection زمانی رخ داد که توسعهدهندگان، بدون در نظر گرفتن امنیت، دادههای ورودی کاربران رو مستقیم به پایگاه داده میفرستادن. این باعث شد که هکرها بتونن کدهای SQL مخرب رو وارد کنن و به دادههای حساس دسترسی پیدا کنن.
حالا بریم سراغ اصل مطلب. SQL Injection یا به اختصار SQLi یک نوع حمله تزریقی است که به مهاجم این امکان رو میده تا کدهای SQL مخرب رو به یک برنامه وارد کنه. این کدها میتونن به مهاجم اجازه بدن تا به دادههای حساس دسترسی پیدا کنه، دادهها رو تغییر بده یا حتی سیستم رو به طور کامل نابود کنه.
حمله SQL Injection یکی از نگرانیهای اصلی در توسعه اپلیکیشنهای وب هست. درواقع این حمله زمانی رخ میده که اپلیکیشن، ورودیهای مخرب یک کاربر رو قبول کنه و اونها رو به عنوان بخشی از دستورات SQL برای جستجو در پایگاه داده استفاده کنه.
مهاجم میتونه کاراکترها و کلمات کلیدی کنترلی SQL مثل کوتیشن تکی (‘)، کوتیشن دوتایی (“)، علامت مساوی (=)، نظر (- -) و غیره رو تزریق کنه تا ساختار پرسش رو تغییر بده. استفاده از این کاراکترهای کنترلی با دستورات متداول SQL مثل SELECT، FROM، DELETE و غیره، امکان دسترسی یا بازیابی عناصر دادهای از سرور پایگاه داده پشتیبان رو فراهم میکنه.
برای موفقیت یک حمله، اپلیکیشن وب باید کدهای مخرب از طرف مهاجم رو در دستور SQL قرار بده. این کدهای مخرب معمولاً از یک منبع غیرمطمئن میان، ولی بعضی وقتها پایگاههای داده داخلی سیستم هم میتونن منبع دادههای مخرب باشن. وقتی که دستورات مخرب SQL در برابر یک پایگاه داده پشتیبان اجرا میشن، مهاجم میتونه پایگاه داده رو تغییر بده یا بهش دسترسی پیدا کنه. این بستگی به این داره که مهاجم چطور دادههای مخرب رو طراحی کرده باشه.
با توجه به این توضیحات، همیشه باید دقت کنیم که ورودیهای کاربران رو اعتبارسنجی کنیم و از منابع غیرمطمئن دوری کنیم تا از حملات SQL Injection جلوگیری کنیم.
تصور کن یه روز به دادگاه رفتی و یه نفر به اسم باب قراره جلوی قاضی ظاهر بشه. وقتی باب داره فرمهای قبل از دادگاه رو پر میکنه، به جای اسمش مینویسه "Bob is free to go". وقتی قاضی به پروندهاش میرسه و بلند میخونه "حالا نوبت باب است که بره"، مأمور دادگاه هم باب رو آزاد میکنه چون قاضی اینو گفته.
SQL Injection هم تقریباً همین شکلی کار میکنه. این حمله زمانی رخ میده که یه فیلد پرسش SQL که قراره فقط یه نوع داده خاص مثل یه عدد رو قبول کنه، به جای اون اطلاعات غیرمنتظرهای مثل یه فرمان رو دریافت میکنه. وقتی این فرمان اجرا میشه، از محدودههای تعیینشده خارج میشه و میتونه رفتارهای مخربی رو به دنبال داشته باشه. این فیلد پرسش معمولاً از دادههایی که توی یه فرم روی یه صفحه وب وارد شده، پر میشه.
بیایید یه مقایسه ساده بین دستورات SQL معمولی و مخرب داشته باشیم:
توی این دستور SQL معمولی، رشته studentId وارد یه دستور SQL میشه. هدف اینه که توی لیست دانشآموزان دنبال دانشآموزی بگردیم که studentId وارد شده رو داره. وقتی پیدا شد، اطلاعات اون دانشآموز برگردونده میشه. به زبان ساده، دستور میگه "برو این کاربر رو پیدا کن و اطلاعاتش رو بهم بده".
کد ممکنه چیزی شبیه این باشه:
studentId = getRequestString("studentId");
lookupStudent = "SELECT * FROM students WHERE studentId = " + studentId;
اگه یه دانشآموز، ID خودش رو به صورت 117 توی یه فرم وب وارد کنه که نوشته "لطفاً شماره دانشجویی خود را وارد کنید"، نتیجه دستور SQL اینجوری میشه:
SELECT * FROM students WHERE studentId = 117;
این دستور، رکورد مربوط به دانشآموز با studentId 117 رو برمیگردونه، که دقیقاً همون چیزی هست که توسعهدهنده API انتظار داره.
توی این مثال، یه مهاجم به جای وارد کردن یه عدد، یه فرمان SQL یا منطق شرطی رو توی فیلد ورودی وارد میکنه. مثلاً شماره دانشجویی رو اینجوری وارد میکنه:
117 OR 1=1
حالا به جای اینکه پرسش توی جدول دنبال ID مشابه بگرده، میاد چک میکنه ببینه آیا 1 برابر 1 هست یا نه. همونطور که میدونی، این شرط همیشه درسته، بنابراین نتیجه این میشه که پایگاه داده تمام اطلاعات جدول دانشآموزان رو به مهاجم برمیگردونه.
SELECT * FROM students WHERE studentId = 117 OR 1=1;
حملات SQL Injection با هدف قرار دادن رابط برنامهنویسی کاربردی (API) یا رابط نرمافزاری که سرور از طریق اون درخواستها رو دریافت میکنه و پاسخ میده، انجام میشه. ابزارهای متداولی وجود دارن که به مهاجمین اجازه میدن به صورت خودکار یه وبسایت رو جستجو کنن و فرمها رو پیدا کنن و سعی کنن انواع مختلفی از دستورات SQL رو وارد کنن تا به جوابهایی که توسعهدهندگان وبسایت انتظارش رو ندارن، برسن و از پایگاه داده سوءاستفاده کنن.
حملات SQL Injection آسونه و جالب اینجاست که جلوگیری ازش هم نسبتاً راحته اگه اصول درست توسعه رعایت بشه. واقعیت اینه که مهلتهای فشرده، توسعهدهندگان بیتجربه و کدهای قدیمی معمولاً منجر به کیفیت و امنیت متغیر کدها میشه. یه فیلد آسیبپذیر توی هر فرم یا نقطه پایانی API در یه وبسایت که به یه پایگاه داده دسترسی داره، ممکنه برای ایجاد یه آسیبپذیری کافی باشه.
SQL Injection به خاطر تواناییش در دسترسی به دادههای حساس و تغییر اونها، بسیار خطرناکه. برای مثال، یه هکر میتونه با استفاده از SQL Injection، اطلاعات کارتهای اعتباری مشتریان یک فروشگاه آنلاین رو بدزده یا حتی تمام دادههای یک پایگاه داده رو حذف کنه.
بیایید یک مثال ساده از SQL Injection رو بررسی کنیم. این مثال نشون میده که چطور یک مهاجم میتونه از یک آسیبپذیری SQL Injection استفاده کنه تا امنیت اپلیکیشن رو دور بزنه و به عنوان مدیر سیستم وارد بشه.
اسکریپت زیر یک شبهکد هست که روی یک سرور وب اجرا میشه. این یک مثال ساده از احراز هویت با استفاده از نام کاربری و رمز عبور هست. پایگاه داده نمونه ما دارای یک جدول به نام users با ستونهای username و password هست.
# تعریف متغیرهای POST
uname = request.POST['username']
passwd = request.POST['password']
# پرسش SQL آسیبپذیر به SQLi
sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + passwd + "'"
# اجرای دستور SQL
database.execute(sql)
فیلدهای ورودی در اینجا آسیبپذیر به حمله SQL Injection هستند. یک مهاجم میتونه از دستورات SQL در ورودی استفاده کنه تا دستور SQL که توسط سرور پایگاه داده اجرا میشه رو تغییر بده. مثلاً میتونه یک ترفند با استفاده از کوتیشن تکی انجام بده و فیلد passwd رو به این صورت تنظیم کنه:
password' OR 1=1
در نتیجه، سرور پایگاه داده پرسش SQL زیر رو اجرا میکنه:
SELECT id FROM users WHERE username='username' AND password='password' OR 1=1'
به خاطر وجود عبارت OR 1=1، شرط WHERE همیشه درسته و اولین id از جدول users برگردونده میشه، که معمولاً id مدیر سیستم هست. به این ترتیب، مهاجم نه تنها احراز هویت رو دور میزنه بلکه به سطح دسترسی مدیر سیستم هم دست پیدا میکنه. اونها همچنین میتونن بقیه دستور SQL رو با استفاده از نظرات کنترل کنن:
-- MySQL, MSSQL, Oracle, PostgreSQL, SQLite
' OR '1'='1' --
' OR '1'='1' /*
-- MySQL
' OR '1'='1' #
-- Access (با استفاده از کاراکترهای null)
' OR '1'='1' %00
' OR '1'='1' %16
یکی از رایجترین انواع SQL Injection استفاده از عملگر UNION هست. این عملگر به مهاجم اجازه میده نتایج دو یا چند پرسش SELECT رو با هم ترکیب کنه. این تکنیک به نام union-based SQL Injection شناخته میشه.
مثال زیر از این تکنیک استفاده میکنه.
درخواست HTTP زیر یک درخواست عادی هست که یک کاربر قانونی ارسال میکنه:
GET http://example.com/artists.php?artist=1 HTTP/1.1
Host: example.com
پارامتر artist آسیبپذیر به SQL Injection هست. در زیر، محمولهی مخرب پرسش رو به گونهای تغییر میده که به دنبال رکوردی غیرموجود بگرده. مقدار در رشته پرسش URL رو به -1 تنظیم میکنه. البته میتونه هر مقدار دیگری باشه که در پایگاه داده وجود نداره. اما مقدار منفی انتخاب خوبی هست چون شناسهها در پایگاه داده به ندرت عدد منفی هستند.
GET http://example.com/artists.php?artist=-1 UNION SELECT 1, 2, 3 HTTP/1.1
Host: example.com
در SQL Injection، عملگر UNION معمولاً برای افزودن یک پرسش SQL مخرب به پرسش اصلی که توسط اپلیکیشن وب اجرا میشه، استفاده میشه. نتیجه پرسش تزریقی با نتیجه پرسش اصلی ترکیب میشه. این به مهاجم اجازه میده تا مقادیر ستونها از جداول دیگر رو به دست بیاره.
مثال زیر نشون میده که چطور یک محموله SQL Injection میتونه برای به دست آوردن دادههای معنادارتر از این سایت عمداً آسیبپذیر استفاده بشه:
GET http://example.com/artists.php?artist=-1 UNION SELECT 1, pass, cc FROM users WHERE uname='test' HTTP/1.1
Host: example.com
به این ترتیب، مهاجم میتونه اطلاعات حساس مثل کلمات عبور و شماره کارتهای اعتباری رو از پایگاه داده استخراج کنه.
تنها راه مطمئن برای جلوگیری از حملات SQL Injection، اعتبارسنجی ورودیها و استفاده از Parametrized Query، شامل Prepared Statementها است. کد اپلیکیشن هرگز نباید ورودیها رو مستقیم استفاده کنه. توسعهدهنده باید تمام ورودیها رو پاکسازی کنه، نه فقط ورودیهای فرمهای وب مثل فرمهای ورود. باید عناصر بالقوه مخرب مثل کوتیشن تکی رو حذف کنه. همچنین بهتره که نمایش خطاهای پایگاه داده در مد Production خاموش باشه. خطاهای پایگاه داده میتونن با SQL Injection استفاده بشن تا اطلاعات بیشتری از پایگاه داده به دست بیاد. در ادامه مواردی برای جلوگیری آوردهایم.
یکی از بهترین راهها برای جلوگیری از SQL Injection استفاده از پارامترهای آمادهسازی شده است. این روش به شما این امکان رو میده تا دادههای ورودی کاربران رو جدا از کدهای SQL نگه داری و از اجرای کدهای مخرب جلوگیری کنی.
# Python example using SQLite
import sqlite3
conn = sqlite3.connect('example.db')
c = conn.cursor()
username = 'user'
password = 'pass'
# Use prepared statements
c.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password))
ORMها ابزاری هستن که به شما این امکان رو میدن تا با استفاده از زبانهای برنامهنویسی شیءگرا با پایگاه داده کار کنی. این ابزارها به طور خودکار کدهای SQL رو ایجاد میکنن و جلوی اجرای کدهای مخرب رو میگیرن.
# Python example using SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///example.db')
Session = sessionmaker(bind=engine)
session = Session()
# Use ORM to query the database
user = session.query(User).filter_by(username='user', password='pass').first()
همیشه ورودیهای کاربران رو اعتبارسنجی کن و مطمئن شو که هیچ داده مخربی وارد سیستم نمیشه. از توابع و ابزارهای مختلف برای تمیز کردن و فیلتر کردن دادهها استفاده کن.
دسترسی کاربران به پایگاه داده رو محدود کن و فقط به اونها اجازه بده تا به دادههایی که نیاز دارن دسترسی داشته باشن. این کار میتونه جلوی بسیاری از حملات رو بگیره.
به تیم توسعهات آموزش بده که چطور از SQL Injection جلوگیری کنن و اهمیت امنیت رو براشون توضیح بده. هر چقدر تیم توسعهات بیشتر با این نوع حملات آشنا باشه، بهتر میتونه جلوی اونا رو بگیره.
برای جلوگیری از حملات SQL Injection و افزایش امنیت اپلیکیشنها، استفاده از روشهای معتبر و مطمئن بسیار اهمیت داره. در اینجا چند نمونه کد رو بررسی میکنیم که به شما نشون میده چطور میتونید از تکنیکهای مختلف برای حفاظت از دادهها استفاده کنید.
استفاده از پارامترهای آمادهسازی شده یکی از بهترین روشها برای جلوگیری از حملات SQL Injection است. در این روش، ابتدا قالب کلی دستور SQL تعریف میشه و سپس مقادیر ورودی به عنوان پارامتر به اون اضافه میشن. این کار باعث میشه که دادههای ورودی به عنوان کد SQL تفسیر نشن.
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// ایجاد اتصال
$conn = new mysqli($servername, $username, $password, $dbname);
// بررسی اتصال
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$user = $_POST['username'];
$pass = $_POST['password'];
// استفاده از پارامترهای آمادهسازی شده
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $user, $pass);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo "Login successful!";
} else {
echo "Invalid username or password.";
}
$stmt->close();
$conn->close();
?>
در این کد، ابتدا اتصال به پایگاه داده ایجاد میشه و سپس از پارامترهای آمادهسازی شده برای اجرای پرسش SQL استفاده میشه. این روش باعث میشه که مقادیر ورودی به عنوان داده، و نه به عنوان کد، در نظر گرفته بشن.
استفاده از ORM (Object-Relational Mapping) یک روش دیگه برای جلوگیری از حملات SQL Injection است. ORM به شما اجازه میده تا به جای نوشتن مستقیم کد SQL، از توابع و متدهای زبان برنامهنویسی برای ارتباط با پایگاه داده استفاده کنید.
from django.db import models
class User(models.Model):
username = models.CharField(max_length=100)
password = models.CharField(max_length=100)
# Query using Django ORM
user = User.objects.filter(username='user', password='pass').first()
در این کد، از Django ORM برای اجرای پرسش SQL استفاده شده. این روش به طور خودکار ورودیهای کاربر رو اعتبارسنجی میکنه و جلوی حملات SQL Injection رو میگیره.
اعتبارسنجی ورودیها یکی از مهمترین روشها برای جلوگیری از حملات مختلفه. در اینجا یک نمونه کد JavaScript رو میبینید که ورودیهای کاربر رو پاکسازی میکنه تا از اجرای کدهای مخرب جلوگیری بشه.
function sanitizeInput(input) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
"/": '/',
};
const reg = /[&<>"'/]/ig;
return input.replace(reg, (match)=>(map[match]));
}
let userInput = '<script>alert("Hacked by 7Learn!")</script>';
let safeInput = sanitizeInput(userInput);
console.log(safeInput); // <script>alert("Hacked by 7Learn!")</script>
این کد، ورودی کاربر رو بررسی و کاراکترهای خاص رو به معادلهای HTML اونها تبدیل میکنه. این کار باعث میشه که ورودی به عنوان کد HTML یا JavaScript تفسیر نشه و به عنوان متن ساده نمایش داده بشه.
SQL Injection با تزریق کدهای SQL مخرب به یک برنامه عمل میکنه و به مهاجم این امکان رو میده تا به دادههای حساس دسترسی پیدا کنه یا اونها رو تغییر بده.
برای تست آسیبپذیری SQL Injection، میتونی از ابزارهای امنیتی مختلفی استفاده کنی یا به صورت دستی کدهای مخرب رو در ورودیهای سایت وارد کنی و ببینی آیا اجرا میشن یا نه.
فریمورکهای مختلفی مثل Django، Ruby on Rails و ASP.NET به جلوگیری از SQL Injection کمک میکنن، زیرا این فریمورکها از روشهای امنتری برای مدیریت پایگاه داده استفاده میکنن.
خیر، وبسایتهایی که به درستی ایمن شده باشن و از روشهای امن کدنویسی استفاده کنن، به احتمال کمتری به حملات SQL Injection دچار میشن.
برای افزایش امنیت وبسایت در برابر SQL Injection، باید از روشهای پارامترهای آمادهسازی شده استفاده کنی، از ORMها استفاده کنی، دادههای ورودی رو اعتبارسنجی کنی و دسترسی کاربران به پایگاه داده رو محدود کنی.
SQL Injection یکی از خطرناکترین و رایجترین روشهای نفوذ به سیستمهاست که میتونه اطلاعات حساس کاربران رو به خطر بندازه و باعث دزدی اطلاعات بشه. با این حال، با استفاده از روشهای صحیح کدنویسی و پیادهسازی ابزارهای امنیتی، میتونی به راحتی از این حملات جلوگیری کنی. امنیت وبسایتها یکی از مهمترین مسائل در دنیای امروز هست و باید همیشه به اون توجه داشته باشیم تا کاربرانمون در امنیت کامل باشن.
به یاد داشته باش، دنیای اینترنت مثل یک جنگل پر از شکارچیهاست و تو باید همیشه آماده و مجهز باشی تا از خودت و اطلاعاتت محافظت کنی. امیدوارم این مقاله برات مفید بوده باشه و بتونی ازش برای افزایش امنیت وبسایتهات استفاده کنی. موفق باشی!