Faragiri

همهٔ زبان‌ها

آموزش تزریق SQL امنیت وب

وقتی مقدار را داخل متن کوئری می‌چسبانید، یک نقل‌قول کافی است تا بقیه ورودی به دستور تبدیل شود. پارامتر این را از ریشه ممکن نمی‌کند.

۴بخش
۳دقیقه خواندن
۴عمق

همان ورودی، دو راه ساخت

کوئری چسبانده‌شده در برابر کوئری پارامتردار

هر دو ستون همان ورودی را می‌گیرند؛ ستون دوم اصلا این امکان را ندارد که ورودی به دستور تبدیل شود.

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

ستون دوم علامت خورده، چون اینجا یک جواب درست هست. ردیف چهارم تنها جایی است که ستون دوم هم به کار شما نیاز دارد.

وقتی کوئری را با چسباندن رشته می‌سازید، مقدار کاربر و دستور شما در یک متن قرار می‌گیرند و پایگاه داده هیچ راهی برای تشخیصشان ندارد. یک نقل‌قول در ورودی، رشته را می‌بندد و هرچه پس از آن بیاید بخشی از خود کوئری است.

با پارامتر، همین ورودی هیچ اثری ندارد. دلیلش این نیست که نویسه‌ها فرار داده شده‌اند؛ دلیلش این است که متن کوئری و مقدارها جدا فرستاده می‌شوند. پایگاه داده اول ساختار کوئری را می‌سازد و بعد مقدارها را در جای خالی می‌گذارد، پس مقدار هیچ‌وقت این امکان را ندارد که دستور شود.

این تفاوت اساسی است و نه یک درجه امنیت بیشتر. فرار دادن دستی یک لایه محافظت است که می‌شود دورش زد؛ پارامتر یک تغییر ساختاری است که این کلاس حمله را غیرممکن می‌کند.

جاهایی که پارامتر کار نمی‌کند

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

راه درست برای این حالت فهرست سفید است: مقدار ورودی را با فهرستی از نام‌های مجاز مقایسه کنید و اگر نبود، پیش‌فرض را بردارید. هیچ‌وقت مقدار ورودی را حتی پس از پاک‌سازی مستقیم در متن کوئری نگذارید.

لایه‌های بعدی، که جای پارامتر را نمی‌گیرند

کاربر پایگاه داده برنامه نباید مجوز حذف جدول یا خواندن جدول‌های سیستمی داشته باشد؛ این کاری نمی‌کند که تزریق رخ ندهد، ولی محدود می‌کند که با آن چه می‌شود کرد. و پیام خطای پایگاه داده هرگز نباید به کاربر نشان داده شود، چون همان پیام‌ها هستند که به مهاجم می‌گویند ساختار جدول چیست.

لایه‌بندی ابزار نگاشت شی به رابطه هم به‌خودی‌خود امن نیست: هر ابزاری راهی برای نوشتن کوئری خام دارد و همان جاست که همین مسئله برمی‌گردد: نگاشت سطر به شی، و چیزی که از زیرش بیرون می‌زند.

جایی که کار گیر می‌کند، کد قدیمی و جست‌وجوی فارسی است

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

ابزار، و سالی که این حرف درست است

در سال ۲۰۲۶، هر لایه دسترسی به پایگاه داده در هر زبان اصلی پارامتر را پشتیبانی می‌کند و رایگان است؛ جای فرار دادن دستی را گرفته که سال‌ها روش رایج بود و هیچ‌وقت کامل نبود. برای پیدا کردن جاهای باقی‌مانده، ابزارهای تحلیل ایستا هر کوئری‌ای که از رشته ساخته شده باشد را علامت می‌زنند و متن‌باز و رایگان‌اند. و در پایگاه داده، ساختن یک کاربر با کمترین مجوز لازم یک دستور است.

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

بالای این