آسیب پذیری تزریق SQL چیست؟ — راهنمای مقدماتی

۲۷۹ بازدید
آخرین به‌روزرسانی: ۲۸ شهریور ۱۴۰۲
زمان مطالعه: ۴ دقیقه
آسیب پذیری تزریق SQL چیست؟ — راهنمای مقدماتی

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

ایجاد پایگاه داده

ما در این مقاله برای درک بهتر آسیب‌پذیری‌ها ابتدا یک پایگاه داده و اپلیکیشن نمونه می‌سازیم تا ببینیم SQL چگونه با یک اپلیکیشن معمولی تعامل پیدا می‌کند.

فرض کنید یک پایگاه داده ساده SQL دارید که شامل جدولی از کاربران به نام users به صورت زیر است:

در این جدول برخی کاربرهای نمونه را درج می‌کنیم تا داده‌هایی داشته‌ باشیم که بتوانیم با آن‌ها کار کنیم.

اینک یک پایگاه داده داریم که شبیه آن چیزی است که در اغلب اپلیکیشن‌ها برای احراز هویت کاربران استفاده می‌شود. برای این که ببینیم چگونه می‌توانیم با این پایگاه داده تعامل پیدا کنیم یک اپلیکیشن VB.net ابتدایی ساخته‌ایم. در این اپلیکیشن یک فرم login ساده داریم:

کدهایی که فرم ورودی را دریافت کرده و نام کاربری و رمز عبور وارد شده را با مقادیر موجود در پایگاه داده مطابقت می‌دهند به صورت زیر هستند:

1Dim sqlCommand as SqlCommand
2Dim username As String
3Dim password As String
4username = usernameTextbox.Text
5password = passwordTextbox.Text
6sqlConnection.Open()
7sqlCommand = New SqlCommand("SELECT COUNT(*) FROM Users WHERE Username = '" & username & "' AND userPassword = HASHBYTES('SHA1','" & password & "')", sqlConnection)
8If sqlCommand.ExecuteScalar() > 0 Then
9   MsgBox("Login Successful")
10Else
11   MsgBox("Login Failed")
12End If

تست اپلیکیشن

اینک می‌توانیم اپلیکیشن خود را تست کنیم تا ببینیم کوئری SQL چگونه ساخته می‌شود و به چه روشی روی پایگاه داده اجرا می‌شود. اگر یک «نقطه توقف» (breakpoint) اضافه کنیم، می‌توانیم اپلیکیشن را به صورت مرحله به مرحله اجرا کنیم تا ببینیم که مقادیر هر متغیر در هر لحظه چه قدر است و به این ترتیب درک بهتری از آن چه رخ می‌دهد خواهیم داشت. در آغاز کاربر اطلاعات ورود خود را وارد می‌کند و روی دکمه «Login» کلیک می‌کند.

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

1SELECT COUNT(*) FROM Users
2WHERE Username = 'test1'
3AND userPassword = HASHBYTES('SHA1','password')

نکته مهم که باید توجه داشت این است که این کوئری چگونه ساخته می‌شود. ورودی کاربر به صورت مستقیم وارد کوئری می‌شود و سپس کوئری روی پایگاه داده اجرا می‌شود. در کوئری فعلی، نتیجه اجرا در صورت یافتن مورد مطابقت نام کاربری و رمز عبور روی پایگاه داده، مقدار 1 را بازگشت می‌دهد و این رفتار مورد انتظار و درستی است. اما اگر کاربر یک کاراکتر ‘ در کوئری خود وارد کرده باشد چطور؟

اینک کوئری به صورت زیر ساخته می‌شود:

1SELECT COUNT(*) FROM Users
2WHERE Username = '''
3AND userPassword = HASHBYTES('SHA1','password')

اگر اجازه بدهیم این کوئری روی پایگاه داده اجرا شود، نتیجه زیر به دست می‌آید:

مخاطرات پایگاه داده

همان طور که می‌بینید که یک استثنای SQL دریافت شده که می‌گوید ساختار SQL نادرست است. در واقع زمانی که کاربر کاراکتر ‘ را در فیلد مربوطه وارد می‌کند، این کاراکتر به کوئری وارد می‌شود و کوئری نیز روی پایگاه داده اجرا می‌شود. SQL کاراکتر ‘ را به عنوان یک پایان نقل قول برای رشته نام کاربری تفسیر می‌کند و یک مورد عدم مطابقت گیومه رخ می‌دهد که موجب بروز خطای نحوی در کوئری می‌شود.

این وضعیت مناسبی نیست زیرا کاربر می‌تواند با وارد کردن یک کاراکتر منفرد موجب از کار افتادن برنامه ما شود. اما این وضعیت زمانی بدتر می‌شود که کاربر بتواند آسیب بسیار بیشتری را از طریق دستکاری کامل در کوئری وارد سازد. ورودی زیر را تصور کنید:

اگر مهاجم بخواهد این مقادیر را وارد کند، کوئری حاصل به صورت زیر خواهد بود:

1SELECT COUNT(*) FROM Users 
2WHERE Username = ''
3OR 1 = 1 -- '
4AND userPassword = HASHBYTES('SHA1','notapassword')

این کوئری را تجزیه می‌کنیم تا آن را بهتر درک کنیم. با استفاده از این فیلتر مهاجم می‌خواهد همه مدخل‌هایی را که  ‘’  =  Username و 1 =1 باشد دریافت کند، از آنجا که 1 همواره برابر با 1 است، این کوئری همه رکوردهای موجود را بازگشت می‌دهد. قطعه آخر ورودی یعنی دو خط تیره در واقع یک کامنت در SQL است. هر چیزی پس از این دو کاراکتر نادیده گرفته می‌شود.

نتیجه اجرای این کوئری بازگشت دادن تعداد همه چیز در پایگاه داده Users است. از آنجا که این موارد بیشتر از 1 خواهند بود، کاربر مجوز دسترسی به اپلیکیشن را خواهد یافت؛ هر چند که اطلاعات ورود معتبر را وارد نکرده است!

راهکار چیست؟

این وضعیت مسلماً وضعیت بدی است چون ما هرگز نمی‌خواهیم که کاربری بتواند بدون اطلاعات درست به یک اپلیکیشن دسترسی پیدا کند. بنابراین چگونه می‌توان از بروز این مشکل اجتناب کرد؟

برخی افراد ممکن است پیشنهاد دهند که همه کاراکترهای ‘ را حذف کنیم. اما تصور کنید که ممکن است ما یک کلمه معتبر مانند O’Reily را در ورودی خود داشته باشیم. بنابراین باید به دنبال راه‌حل بهتری باشیم.

بهترین پاسخ به این مسئله «کوئری‌های پارامتری شده» (parameterized queries) است. کوئری‌های پارامتری شده، طرح اجرایی SQL را پیش از افزودن ورودی ایجاد می‌کنند. یعنی از اجرای کد وارد شده از سوی کاربر جلوگیری می‌کنند. اگر بخواهیم کد اولیه خود را بر اساس کوئری‌های پارامتری شده بازنویسی کنیم، چیزی مانند زیر به دست می‌آید:

1Dim sqlCommand As SqlCommand
2Dim query As String = "SELECT COUNT(*) FROM Users WHERE Username = @username AND userPassword = HASHBYTES('SHA1',@password)"
3Dim username As String
4Dim password As String
5username = usernameTextbox.Text
6password = passwordTextbox.Text
7sqlConnection.Open()
8sqlCommand = New SqlCommand(query, sqlConnection)
9sqlCommand.Parameters.Add("@username", SqlDbType.VarChar, 300).Value = username
10sqlCommand.Parameters.Add("@password", SqlDbType.VarChar, 300).Value = password
11If sqlCommand.ExecuteScalar() > 0 Then
12MsgBox("Login Successful")
13Else
14MsgBox("Login Failed")
15End If

انجام این کار موجب حل مشکل ما خواهد شد:

نسخه پارامتری شده از کوئری به طرز چشمگیری امن‌تر است و به امن ماندن اپلیکیشن در برابر حمله‌های تزریق SQL کمک زیادی می‌کند. استفاده از این روش هنگام دسترسی به پایگاه داده SQL بسیار حائز اهمیت است چون در غیر این صورت داده‌های شما از سوی مهاجمان تخریب شده یا مورد دسترسی غیرمجاز قرار می‌گیرند.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
scottc130
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *