با سلام خدمت شما خوانندگان گرامی، در این مقاله قصد داریم در رابط به قوانین مربوط به قالب بندی (formatting) کوئری ها صحبت کنیم. این قوانین به ما کمک می کنند تا از شر SQL Injection در امان باشیم اما باید توجه داشت که چرا اعمال این قوانین به صورت دستی صحیح نیست و ممکن است باعث مشکلاتی برای برنامه ی ما شود.
حقیقت این است که قوانین قالب بندی آن قدر ها هم ساده نیستند و نمی شود تمام آن ها را در یک لیست جمع کرد چرا که بر اساس موقعیت های مختلف تغییر می کنند. نکته ی جالب این جاست که معمولا برنامه نویسان با SQL طوری برخورد می کنند که انگار با یک رشته ی literal طرف هستند در حالی که SQL مانند یک برنامه است، مانند یک اسکرپت PHP است و مانند هر برنامه ی دیگری، ساختار و اجزای خاص خود را دارد. بنابراین تک تک این قسمت ها نیاز به قالب بندی خاصی دارد که مخصوص خودش است و برای دیگر اجزا کار نمی کند. این قوانین در MySQL از این قرار هستند:
Identifier ها (به معنی «مشخص کننده») در واقع همان نام پایگاه داده، نام جدول، نام ردیف ها و نام ستون ها هستند.
همانطور که میبینید این قوانین در قسمت های مختلف تفاوت دارند بنابراین نمی شود گفت تمام داده هایتان را escape کنید یا همه جا از prepared statement ها استفاده کنید. در مورد prepared statement ها می توانید دو مقاله ی زیر را مطالعه کنید:
اجرای کوئری ها و SQL Injection (قسمت اول)
اجرای کوئری ها و SQL Injection (قسمت دوم)
حتما بسیاری از شما می گویید، من که همیشه این کار ها را انجام می دادم. آیا برنامه ی من امن است؟ خیر! مشکل این است که شما این کارها را به صورت دستی انجام می دهید. شما هیچ گاه نباید به صورت دستی و تک تک این موارد را پیاده سازی کنید بلکه باید مکانیسمی داشته باشید که به طور طور خودکار این کار را برایتان انجام دهد. چرا؟
به نظر شما چرا قالب بندی به شکل دستی مشکل دارد؟ به این دلیل که به صورت دستی انجام می شود و هر چیزی که به صورت دستی انجام شود احتمال خطا را ده ها بار افزایش می دهد. قالب بندی به شکل دستی به سطح سواد برنامه نویس، به حال او، به خستگی یا شادی یا غمگین بودن او بستگی دارد و الی آخر.
در واقع متکی بودن یک سیستم به احوال برنامه نویس از مشکلات بزرگ آن محسوب می شود و می توان گفت قالب بندی دستی بزرگترین و شاید تنها دلیل حملات SQL Injection در دنیا است.
اما چرا؟
مشکل فاصله ی قالب بندی دستی تا اجرای کوئری بحث بسیار مهمی است. تقریبا همه ی ما وسوسه می شویم که به جای انجام پاک سازی (sanitization) داده در نقاط مختلف برنامه، آن ها را یکجا پاکسازی کنیم. زمانی که این کار را انجام دهیم ممکن است داده ای را پاک سازی کرده باشیم که تا زمان اجرا شدنش فاصله ی بسیار زیاده باقی مانده است. به نظر شما مشکل آن چیست؟
اول از همه باید توجه کنیم که اگر فاصله ی پاک سازی تا اجرا زیاد باشد یعنی کوئری مربوطه جلوی چشممان نیست و در این صورت نمی توانیم مطمئن شویم که این literal چه نوع داده ای است. بنابراین قوانین اول و دوم قالب بندی درجا نقض می شوند.
همچنین اگر بخواهیم از این مشکل دوری کنیم و در چند جای مختلف داده ها را پاک سازی کنیم باز هم با مشکل بزرگی روبرو می شویم؛ ممکن است به اشتباه و به دلیل شباهت برخی داده ها به هم تصور کنیم فلان داده را قبلا پاک سازی کرده ایم و آن را بدون پاکسازی وارد سیستم کنیم. تصور کنید در کار های گروهی که بین چند توسعه دهنده انجام می شود چنین مشکلی چه تبعاتی می تواند داشته باشد!
از طرفی ممکن است یک داده را دو بار پاک سازی کنیم؛ مثلا داده را در هنگام دریافت پاکسازی کرده باشیم اما قبل از اجرا نیز دوباره پاک سازی کنیم. این مورد خطری ایجاد نمی کند اما برای یک توسعه دهنده ی حرفه ای ننگ به حساب می آید.
مشکل آخر نیز از این قرار است که پاک سازی یا قالب بندی قبل از اجرا باعث می شود متغیر ما تغییر کند و دیگر در هیچ جایی به غیر از کوئری قابل استفاده نباشد. بسیاری از اوقات می خواهیم از داده ی ورودی استفاده های دیگری نیز بکنیم چرا که داده های ورودی فقط به درد پایگاه داده نمی خورند اما اگر از همان ابتدا قالب بندی شوند دیگر قابل استفاده نیستند.
با این تفاسیر راه حل چیست؟ از چه مکانیسمی استفاده کنیم تا از این مشکلات خلاص شویم؟ در جلسه ی بعد به پاسخ این سوال خواهیم رسید ...
امیدوارم از این قسمت لذت برده باشید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.