ARC
سه شنبه 11 آبان 1389, 10:47 صبح
در این پست هدف آموزش روش هایی هست تا شما بتونید سایت های امنتری طراحی کنید.
منبع این مقاله هفته نامه عصر ارتباط (http://www.asreertebat.com)می باشد. این مقاله هر هفته به در همین پست به روز می شود و مطالب جدید به آن اضافه می شود. پس از تکمیل کامل مقاله فایل آن به صورت PDF در اختیار شما عزیزان قرار خواهد گرفت.
امروزه گسترش وب سایتها با روندی بسیار سریع رو به رشد است چراکه به صاحبان وب سایت این امکان را میدهد که با کمترین هزینه تجارت و کار خود را به تمامی دنیا معرفی کنند. امروزه بالغ بر ۱۲۰ میلیون وب سایت در اینترنت وجود دارد. متاسفانه افراد بدون در نظر گرفتن نکات امنیتی اطلاعات محرمانه و مهم را نیز در وب سایتها قرار میدهند.
اهمیت ویژه در امنیت وب سایتها به آن دلیل است که وبسایتها برخلاف منابع دیگر اطلاعات، در همیشه و در همه مکانها قابل دسترس برای عموم هستند. بنابراین اطلاعات محرمانه و مهم باید به گونهای امن شده باشند که هیچ کس توانایی دسترسی به آنها را نداشته باشد.
برای برقراری امنیت وب سایت باید سه فاکتور امنیتی زیر رعایت شود.
۱. امنیت شخصی:
رعایت نکردن اصول امنیتی از جانب مدیر وب سایت باعث بروز مشکلات امنیتی در سایت میشود، چراکه اگر به هر دلیلی رمز عبور و سایر اطلاعات وب سایت به دست اشخاص دیگر بیفتد، امکان دسترسی به تمام اطلاعات سایت وجود دارد. روشهای مقابله با این عامل در شمارههای قبل توضیح داده شده است.
۲. امنیت نرمافزار:
مهمترین فاکتور برای برقراری امنیت وب سایت، امنیت نرمافزاری وب سایت است که در این فرصت و مقالههای بعد به بررسی این مورد میپردازیم.
۳. امنیت سرور:
این مورد برای صاحبان سرور و سایتهایی است که برای راهاندازی از سرورهای اختصاصی استفاده میکنند. تنها کاری که دارندگان سایتهای معمولی در این زمینه میتوانند انجام دهند، خرید فضای وب سایت از هاستهای معتبر و امن است. بعد از بررسی امنیت نرمافزار به این مورد خواهیم پرداخت.
نرمافزار سایت، به برنامهای گفته میشود که باعث پویا شدن صفحات میگردد. سایتهایی که از صفحات ثابت (Static Page) برای نمایش محتویات استفاده میکنند فاقد نرمافزار سایت هستند و بنابراین از لحاظ امنیت نرمافزار دچار مشکل نخواهند شد.
گسترش روزافزون وب سایتها باعث گسترش طراحان وبسایتها نیز شده است. در این میان طراحانی که دانش کمتری نسبت به استانداردهای برنامهنویسی و امنیتی دارند نیز به این عرصه پا گذاشتهاند. این ناآگاهی باعث بروز مشکلات امنیتی(حفره یا Bug) در وب سایت طراحی شده میشود. مشکلاتی که حتی ممکن است برای بهترین برنامهنویسان (بر اثر بیدقتی) نیز به وجود آید. در ذیل نام متداولترین حفرههای موجود در نرمافزارهای سایتها و توضیح مختصری از هر یک آمده است. در مقالات بعدی به توضیح کاملتر و نحوه برطرف کردن هر مشکل میپردازیم:
SQL Injection: این حفره که متداول و در عین حال خطرناک است باعث دسترسی غیرمجاز به پایگاه داده میشود.
Cross-site scripting یا XSS: این حفره که متداولترین حفره موجود در نرمافزارهای سایتها است باعث اجرا شدن کدهای سمت کاربر میشود.
Remote Code Execution یا RCE: وجود این حفره خطرناک باعث اجرای کدهای دلخواه سمت سرور توسط حملهکننده است.
Remote File Inclusion یا RFI: این حفره نیز باعث اجرای کدهای درون یک فایل در سمت سرور خواهد بود.
Local File Inclusion یا LFI: وجود این حفره باعث اجرا شدن یا نمایش کدهای فایلهای درون سرور میشود.
Session Hijacking: با وجود این حفره، نفوذگر قادر خواهد بود که به عنوان مدیر سایت وارد سایت شود.
حفرههای نامبرده، متداولترین حفرههای موجود هستند البته حفرههای بسیار دیگری نیز در نرمافزار وبسایتها وجود دارد.
تزریق کد به پایگاهداده (SQL Injection) (قسمت 1)
همانطور که در بالا اشاره شد، SQL Injection حفرهای بسیار خطرناک است که در بسیاری از برنامههای تحت وب وجود دارد. SQL Injection زمانی به وجود میآید که ورودیها بدون کنترل در دستورهای پایگاه داده(Database) استفاده میشوند، این حفره ممکن است در هر برنامه که از پایگاه داده استفاده میکند به وجود آید. بهترین راه جلوگیری از آن نیز کنترل دادههای ورودی است. یک مثال ساده:
query = "SELECT * FROM users
WHERE uname=’” + Username + “’
AND password=’” + Password + “’;”
در این شبهکد انتظار میرود فقط هنگامی که نامکاربری(Username) و رمزعبور(Password) به درستی وارد شوند عمل ورود به سایت انجام پذیرد، اما با یک ترفند ساده میتوان نامکاربری را طوری وارد کرد که بدون کنترل شدن رمزعبور، عمل ورود به سایت انجام شود. کافی است به جای نامکاربری عبارت زیر را وارد کنیم:
' OR 1=1; - -
در این صورت دستور SQL به صورت زیر اجرا خواهد شد:
SELECT * FROM users
WHERE uname=’’ OR 1=1;- -’
AND password=’’;
در ساختار SQL دستورات بعد از علامت - - اجرا نمیشوند و چون 1=1 یک عبارت همیشه درست است، اولین کاربر انتخاب میشود. به این طریق بدون داشتن نام کاربری و رمزعبور وارد سایت شدهایم.
برای جلوگیری از انجام چنین اتفاقاتی برای زبانهای برنامهنویسی مختلف، راهکارهای گوناگونی وجود دارد که در ادامه با آنها آشنا میشوید.
جلوگیری از SQL Injection در PHP
۱. همیشه از درستی نوع متغیر ورودی اطمینان حاصل کنید. در زبان PHP انواع متغیرها وجود دارند. میتوانید با استفاده از توابعی مانند ctype_digit و ctype_alnum و سایر توابع خانواده ctype یا تابع gettype نوع ورودی را کنترل کنید. همچنین میتوانید با استفاده از(regular expression (PCRE از صحت اطلاعات اطمینان حاصل یابید.
۲. اگر قرار است در دستور SQL عدد وارد شود، با توابعی مانند is_numeric اطمینان حاصل کنید که ورودی حتما عدد است یا همیشه نوع ورودی را با تابعی مانند settype یا intval یا floatval یا … تغییر دهید.
۳. ورودیهایی که از نوع رشته(string) هستند را با توابع پایگاه داده مورد نظر escape کنید (مانند mysql_real_escape_string یا sqlite_escape_string یا ...) و اگر برای پایگاه داده مورد نظر شما چنین تابعی موجود نیست با استفاده از توابعی مانند addslashes یا str_replace این کار را انجام دهید. این عمل باعث میشود تا کاراکتری مانند ' در ساختار SQL تاثیری نگذارد و ورودی به عنوان متغیر به دستور داده شود و تاثیری بر دستور نداشته باشد.
۴. استفاده از stored procedures یکی از بهترین روشهای جلوگیری از SQL Injection در پایگاه دادههایی است که این قابلیت را دارند. اما متاسفانه همه پایگاهدادهها این قابلیت را ندارند.
۵. سعی کنید در هیچ شرایطی خطای رخ داده در پایگاه داده به کاربر نشان داده نشود، چراکه نمایش این خطاها میتواند به حملهکننده این امکان را بدهد که بداند چه اتفاقی در پایگاه داده انجام گرفته است. در PHP راههای مختلفی برای جلوگیری از نمایش خطاها وجود دارد. یکی از معروفترین آنها استفاده از عملگر @ قبل از دستورالعمل مورد نظر است. هنگامی که از این عملگر استفاده شود، PHP از پیامهای خطای دستور مورد نظر صرف نظر میکند. یک نمونه استفاده از عملگر:
$my_file=@file (‘names.php’) or die (‘failed’);
۶. سعی کنید دستورات اجرا شده در پایگاه داده را ثبت کنید. هر چند این امر به جلوگیری از SQL Injection کمکی نمیکند، اما به شما این امکان را میدهد تا با دیدن دستورات اجرا شده پی به اشتباهات خود برده و آنها را برطرف کنید. برای ثبت دستورات میتوانید از پایگاهدادههایی که این قابلیت را دارند استفاده یا با بهرهگیری از دستورات PHP آنها را درجایی امن ذخیره کنید.?>
تزریق کد به پایگاهداده(SQL Injection) (قسمت 2)
در بالا خواندیم که SQL Injection چیست و چگونه در زبان برنامهنویسی PHP از آن جلوگیری کنیم، در این مقاله میخوانیم که چگونه از این حفره امنیتی در زبان ASP.NET جلوگیری کنیم.
باید توجه داشت که برای تمامی زبانهای برنامهنویسی، راهکارهای متفاوتی وجود دارد. البته در باطن تمام این راهکارها هدف و نتیجه یکسانی دارند.
جلوگیری از SQL Injection در ASP.NET :
۱. از ورودیها اطمینان حاصل کنید.
سعی کنید تا جای ممکن تمامی ورودیها را از لحاظ نوع داده، طول رشته، بازه عددی و سایر موارد کنترل کنید. برای این کار میتوانید از Regex،RegularExpressionValidator یا RangeValidator استفاده کنید.
۲. استفاده از ورودیها در Stored Procedureها راه بسیار مناسبی است.
توجه داشته باشید که استفاده از Stored Procedureها بدون استفاده از ورودی باعث جلوگیری از SQL Injection نمیشود. برای این کار میتوانید ازSqlParameter و SqlParameterCollection استفاده کنید.
همچنین در صورتی که مجبور به استفاده از دستورات پویا (Dynamic) هستید، با استفاده از SqlParameterCollection نوع ورودیها را مشخص کنید.
۳. سعی کنید تا جای ممکن از API هایی مانند ADO.Net و قابلیتهای آن استفاده کنید، چرا که با کمک این رابطهای برنامهنویسی میتوان نوع دقیق دادهها را مشخص کرد و همچنین این اطمینان را داشت که ورودیها به طرز صحیحی Escape میشوند.
۴. تا حد امکان از کاربرانی با سطح دسترسی کم برای اتصال به پایگاه داده استفاده کنید. این کار باعث جلوگیری از SQL Injection نمیشود اما به این موضوع کمک میکند که کسی نتواند کدهای مخرب را روی بانک اطلاعاتی اجرا کند و بنابراین در صورت وجود SQL Injection نفوذگر قدرت مانور کمتری خواهد داشت.
۵. همیشه اطلاعات مهم مانند رمزهای عبور را به صورت کدشده در پایگاه داده(Database) ذخیره کنید. این کار نیز باعث جلوگیری از SQL Injection نمیشود اما باعث میشود در صورتی که مهاجم به پایگاه داده نفوذ کرد، نتواند اطلاعاتی مانند رمزهای عبور را به راحتی به دست بیاورد.
مثال: در نمونه کدهای زیر با کمک روشهای گفته شده تا حد امکان جلوی این حفره گرفته شده است:
<%@ language=»C#» %>
using System;
usingSystem.Text.RegularExpressions;
public void Login(string uname, string password)
{
if ( !Regex.IsMatch(uname, @"^[a-zA-Z'./s]{1,20}$"))
throw new FormatException("Invalid username");
if ( !Regex.IsMatch(password,@"^(?=.*\d)(?=.*[a-z])
(?=.*[A-Z]).{6,15}$" ))
throw new FormatException("Invalid password");
//...
}
usingSystem.Data;
usingSystem.Data.SqlClient;
using (SqlConnection connection = new SqlConnection(connectionString))
{
DataSet dataset = new DataSet();
SqlDataAdapter command = new SqlDataAdapter ("LoginStoredProce dure", connection);
command.SelectCommand.CommandType = CommandType.StoredProcedure;
command.SelectCommand.Parameters.Add("@au_id", SqlDbType.VarChar, 11);
command.SelectCommand.Parameters["@au_id"].Value = SSN.Text;
command.Fill(dataset);
}
تزریق کد به پایگاهداده (SQL Injection) (قسمت 3)
طی دو قسمت گذشته ما درباره مفهوم تزریق کد به پایگاه داده و راههای مقابله با آن در زبانهای برنامهنویسی مهم و پرکاربرد PHP و ASP.NET صحبت کردیم. اما هنوز درباره روشهای مقابله با SQL Injection در محبوبترین زبان برنامهنویسی، یعنی Java صحبت نکردهایم. این قسمت قصد داریم نگاهی به این زبان بیندازیم. پس با ما همراه باشید.
جلوگیری از SQL Injection در Java
در زبان Java نیز راهکارهایی مانند راهکارهای PHP و ASP.NET ارایه میشود.
۱. استفاده از پارامترها در Java راهی بسیار مناسب برای جلوگیری از SQL Injection است. استفاده از PreparedStatement یا CallableStatement برای این کار بسیار مناسب خواهد بود.
۲. برای استفاده از دستورات پویا(dynamic) نیز تا حد امکان از پارامترها استفاده شود. این امر باعث میشود که از صحت اطلاعاتی ورودی مانند اعداد اطمینان حاصل کنیم.
هیچگاه از اتصال دو رشته برای استفاده از دستورات پویا استفاده نشود. توجه به این نکته بسیار اساسی است. استفاده از کلاس java.sql.Statement برای اجرای دستورات پویا را به هیچ عنوان توصیه نمیکنیم.
۳. استفاده از Regex راهی بسیار مناسب برای صحت اطلاعات است. هر چند استفاده از Regex کار سادهای نیست، اما با کمک آن میتوان از صحت تمامی اطلاعات ورودی اطمینان خاطر حاصل کرد.
۴. درست کد کردن اطلاعات ورودی یا به اصطلاح escape کردن دادههای ورودی امری است که در تمامی زبانهای برنامهنویسی باید انجام گیرد. استفاده از تمام روشهای دیگر نیز در انتها منجر به انجام این کار میشود.
البته در زبان Java نسبت به زبانهای برنامهنویسی دیگر نیاز کمتری به استفاده از escape کردن وجود دارد. اما این امر را باید در نظر داشت که هر کار دیگر در نهایت برای escape کردن ورودیها است.
مثال:
در نمونه کدهای زیر نحوه استفاده از نکات گفته شده را میبینیم:
String selectString = "SELECT * FROM News WHERE newsId = ? ";
PreparedStatement selectQuery = con.prepareStatement(selectString);
selectQuery.setString(1, newsId);
ResultSet rs = selectQuery.executeQuery();
private static final Pattern codePattern = Pattern.compile("^\d{6}(-\d{3})?$");
public void processData( HttpServletRequest request, HttpServletResponse response)
{
try
{
String code = request.getParameter( "code" );
if ( !codePattern.matcher( code ).matches() {
throw new ValidationException( "Improper zipcode format." );
}
// ...
}catch(ValidationException e){
response.sendError( response.SC_BAD_REQUEST, e.getMessage() );
}
}
XSS (Cross Site Scripting)
امروزه وبسایتها به گونهای طراحی میشوند که قسمت عمدهای از خروجی باید به صورت پویا (Dynamic) باشد. پویا بودن سایت باعث راحتی کاربران است اما از طرفی این پویا بودن باعث به وجود آمدن حفرههای امنیتی زیادی نیز میشود.
XSS یا در اصل Cross Site Scripting عمدهترین حفره در وبسایتهای پویا محسوب میشود، به طوری که بیش از 80 درصد حفرههای گزارش شده وبسایتها از این نوع است(XSS را با CSS یا همان Cascading Style Sheets اشتباه نگیرید).
XSS به دو نوع کلی تقسیمبندی میشود، نوع اول که به ناپایدار(Non-persistent) معروف است دارای خطر زیادی نیست اما نوع دیگر که با نام پایدار(Persistent) آن را میشناسند، خطری جدی برای سایت محسوب میشود. قابل ذکر است که نوع اول بسیار متداولتر از نوع دوم است.
مهمترین خطری که به طور کلی از طریق این حفره رخ میدهد به سرقت رفتن cookieها و sessionهای مدیر سایت است که از طریق آن، حملهکننده خود را به عنوان مدیر سایت به نرمافزار سایت معرفی میکند.
این حفره در جاهایی رخ میدهد که ورودی بدون فیلتر شدن، همانگونه که وارد شده است به کاربر نمایش داده میشود. اگر این نمایش اطلاعات فقط در همان لحظه باشد، از نوع «ناپایدار» و در صورتی که در پایگاه داده یا جایی مانند آن ذخیره شود تا دوباره به کاربر نمایش داده شود، «پایدار» خواهد بود. در چنین مواردی کاربر با وارد کردن کدهای HTML خطرناک(مانند تگ script)، کدهای JavaScript یا استفاده کردن از iframe و نمایش صفحه دلخواه خود در آن، میتواند اطلاعات مهمی از بازدیدکننده دریافت کند(اغلب اوقات خود مدیر سایت).
راههای جلوگیری
۱. فیلتر کردن ورودیها: بهترین راه فیلتر کردن ورودیها است. جلوگیری از وارد کردن تگهای HTML یا فقط اجازه وارد کردن برخی از تگ های HTML راه بسیار مناسبی برای جلوگیری از این حفره خواهد بود.
۲. امنیت کوکیها: علاوه بر روش قبل، امن کردن کوکیها کاری است که حتی در صورت وجود این حفره میتواند از سرقت رفتن برخی اطلاعات مربوط به ورود به سایت که معمولا در کوکیها یا جلسات (session) ذخیره میشوند، جلوگیری کرد. قابلیتی در مرورگرهای جدید گنجانده شده است که با کمک آن میتوان برخی از کوکیهای مهم را به صورت HTTP-Only قرار داد که باعث میشود کوکیها از طریق جاوا اسکریپت قابل دسترس نباشند و لذا با این قابلیت، سرقت کوکیها از این طریق دیگر امکانپذیر نخواهد بود.
۳. غیرفعال کردن جاوا اسکریپت: با وجود Web2 و AJAX همچنان سایتهایی موجود هستند که از جاوا اسکریپت استفاده نمیکنند. همچنین وبسایتها برای جلوگیری از به سرقت رفتن کوکیها میتوانند به طور کل جاوا اسکریپت را غیرفعال کرده که در این صورت دیگر از این طریق امکان سرقت کوکیها وجود نخواهد داشت.
در بین روشهایی که گفته شد تنها روش اول توصیه میشود و در روشهای دیگر این مشکل حل نمیشود، بلکه امنیت شخصی کمی بهتر میشود.
جلوگیری از این روش در زبانهای برنامهنویسی مختلف
در زبان PHP برای جلوگیری از ورود هر گونه تگ HTML میتوان از تابع htmlentities استفاده کرد. این تابع تمام دادههای ورودی را طوری تغییر میدهد که در صورت وجود تگهای HTML در این دادهها فقط به عنوان کاراکتر شناخته شده و اجرا نشوند.
<?php
$name = htmlentities($_POST['nam ']);
echo $name;
?>
تابع strip_tags نیز در PHP به این منظور است که تمام تگهای HTML را از داده ورودی حذف کند. البته این قابلیت را نیز دارد که تگهایی را که برنامهنویس برای آن تعریف میکند را به عنوان ورودی قبول کند.
<?php
$text = strip_tags($_POST[<text>],><a><b><i><u><p>>);
echo $text;
?>
در زبان ASPX نیز میتوان از <asp:Literal> و HtmlEncode استفاده کرد:
<%@ Page Language=”C#” AutoEventWireup=”true”%>
<html>
<asp:Literal ID=”Literal1” runat=”server”></asp:Literal>
</html>
<script runat=”server”>
Literal1.Text = Server.HtmlEncode(Text1.Text);
</script>
یا:
Response.Write(HttpUtility.HtmlEncode(Request.Form[“text”]));
توجه داشته باشید که توابع گفته شده در بالا تنها یک نمونه هستند و طبیعتا توابع دیگری نیز برای این کار وجود دارد.
Remote Code Execution (RCE)
حفره امنیت صفحات اینترنتی که این هفته میخواهیم در مورد آن صحبت کنیم، RCE است. این حفره به نفوذگر توانایی اجرای کد در سرور را میدهد. بنابراین با اجرای کدهای مورد نظر خود، تقریبا هر کاری روی وب سایت مورد حمله میتواند انجام دهد. این حفره به ندرت رخ میدهد اما در عین حال بسیار خطرناک است. RCE در زمانی رخ میدهد که در کد نوشته شده جایی وجود داشته باشد که در آن کدها به صورت dynamic (پویا) اجرا میشوند و بتوان به هر نحو محتویات کد پویا را تغییر داد. توجه داشته باشید که پویا بودن سایت با پویا بودن کد بسیار متفاوت است، چراکه پویا بودن کد بدان معنا است که کد برنامه به نسبت شرایط مختلف، تغییر کند. اما پویا بودن سایت بدان معناست که محتویات نمایش داده شده (خروجی) سایت تغییر کند. باید به این نکته توجه داشت که SQL Injection و XSS که در مقالههای قبل به آنها پرداخته شد، خود نوعی RCE هستند که روی پایگاه داده و خروجی HTML رخ میدهند اما چیزی که در اینجا حائز اهمیت خواهد بود این است که در برخی موارد ممکن است اجرای کد در کدهای برنامه یا در برنامههای دیگری که برنامه ما صدا میزند، رخ دهد. با اینکه RCE به طور کل به تمامی موارد گفته میشود اما اصطلاحا فقط برای این مورد از RCE استفاده میکنیم و سایر موارد را با نامهای دیگر میشناسیم. RCE نیز مانند بسیاری از حفرههای دیگر با کنترل ورودیها برطرف میشود چرا که زمانی این حفره پدیدار میشود که ورودیها شامل کدهای خطرناک باشند. برای جلوگیری از RCE بهترین راه این است که تا حد امکان از قابلیت پویا بودن کد استفاده نکنیم چرا که در بسیاری از موارد(به جز موارد خاص) این قابلیت استفادهای ندارد و میتوان با سایر دستورات برنامهنویسی مانند حلقهها، شرطها و ... کار مورد نظر را انجام داد. پس تاکید میشود که تا حد ممکن از امکانات پویا بودن کد استفاده نکنید. باید تا جای امکان از ورودیها اطمینان حاصل کرد. برای اطمینان از ورودیها میتوان از روشهایی که در مقالات قبل خواندیم، استفاده کنیم.
چند مثال ساده: در زبان PHP یکی از سادهترین توابعی که باعث بروز RCE میشود، تابع eval است:
<?php
$myvar = “testvar”;
$value = intval($_GET[‘input’]);
eval(“\$myvar = \$value;”);
?>
توجه داشته باشید که در مثال یاد شده اگر از تابع intval استفاده نشود، ورودی میتواند طوری وارد شود که حمله کننده بتواند هر دستور PHP دلخواه را اجرا کند.
در این مثال PHP نیز برنامهای جداگانه صدا زده میشود که کار ترجمه متن را انجام میدهد:
<?php
passthru( «/user/dic/en2fa « . escapeshellarg($_GET['text']) );
?>
در مثال فوق تابع escapeshellarg باعث میشود که ورودی هیچ کد مخربی نباشد و اگر از این تابع استفاده نشود میتوان کدهای بسیار خطرناکی را اجرا کرد. در زبان ASP.NET نیز همین تهدیدها موجود هستند. در مثال زیر کدهای مورد نظر در فایل ذخیره و در مواقع لزوم بازخوانی میشوند:
<%
If Not IsEmpty(Request(«username»)) Then
Dim fso, f
Set fso = CreateObject(«Scripting.FileSystemObject»)
Set f = fso.OpenTextFile(Server.MapPath( «userlog.txt» ), 8, True)
f.Write Request(«username») & vbCrLf
f.close
Set f = nothing
Set fso = Nothing
%>
<h1>List of logged users:</h1>
<pre><%
Server.Execute(«userlog.txt»)
%></pre><%
Else
%>
<form>
<input name=»username» /><input type=»submit» name=»submit» />
</form>
<%
End If
%>
در مثال یاد شده username بدون کنترل وارد فایل و به عنوان کد اجرا میشود. برای جلوگیری از چنین مشکلی توصیه میشود که مثالی مانند این استفاده نشود و به جای آن از پایگاه داده برای ذخیره استفاده یا به جای Server.Execute به طوری معمولی محتویات فایل خوانده شود.
RFI (Remote File Inclusion) و LFI (Local File Inclusion)
در بخش قبل با حفره RCE و نحوه برطرف کردن آن آشنا شدیم. همانطور که خواندیم RCE زمانی رخ میدهد که کدهای برنامه به صورت پویا اجرا شوند. پویا بودن کدها به صورتی که در مقاله قبل به آن اشاره شد متداول نيست و فقط در موارد خاص رخ میدهد اما تکه کردن کدهای مختلف در فایلهای مختلف و فراخوانی آنها در مواقع لزوم(include کردن) کاری بسیار متداول است و تقریبا تمام برنامهنویسها با آن آشنایی دارند.
فراخوانی فایل دیگر به طور عادی خطری ندارد اما در مواقعی خاص میتواند به یکی از خطرناکترین حفرهها تبدیل شود. فراخوانی فایل در این دو حالت کلی میتواند خطرناک واقع شود:
1. زمانی که فایل فراخوانی شده در جایی ذخیره شود که بتوان از آنجا دسترسی گرفت.
2. زمانی که نام فایل فراخوانی شده متغیر باشد و در شرایط مختلف مقادیر مختلفی بگیرد.
در حالات گفته شده حالت دوم متداولتر است. برای حالت اول در حد امکان تمام فایلها را در جایی امن ذخیره و از فایلهای خارج از server در حد امکان استفاده نمیکنیم.
برای برطرف کردن حالت دوم سادهترین راه پاک کردن صورت مساله است. یعنی در حد امکان نام فایلها را متغیر قرار ندهیم. اما در برخی از موارد اینکار عملی نيست و لازم است که نام فایل فراخوانی شده با توجه به شرایط تغییر کند.
برای چنین مواردی لازم است تا متغیر از هر لحاظ فیلتر شود. میتوان قسمتی از نام فایل را که برای همه فایلها یکسان است را ثابت قرار داد و فقط قسمتی که احتیاج به تغییر دارد را متغیر قرار داد. در این صورت، اگر قبل از متغیر ثابت باشد، از RFI جلوگیری میشود و در سایر موارد کار نفوذ کردن بسیار سختتر خواهد شد.
بهترین راه برای جلوگیری از این حفره استفاده از لیستی ثابت از نام فایلهای مورد نظر است و در صورت نبود مقدار متغیر در لیست میتوان از include کردن فایل جلوگیری کرد. همچنین حذف کردن تمامی علامتهایی که باعث میشوند فایل از پوشهای دیگر خوانده شود(مانند / و \ )، باعث میشود تا جلوی فراخوانی فایلهای خطرناک گرفته شود.
به طور کلی زمانی RFI (وارد كردن فايل راه دور) رخ میدهد که به جای نام فایل بتوان هر آدرس دلخواهی قرار داد. در این صورت حملهکننده میتواند فایل مخرب خود را روی سایت خود آپلود کرده و با دادن آدرس آن از سایت شما بخواهد که آنرا اجرا کنید و به این صورت هر دستوری را روی سایت شما اجرا کند.
LFI(وارد كردن فايل محلي) نیز زمانی رخ میدهد که بتوان نام فایل را تغییر داد و به جای آن هر فایل روی server را فراخوانی کرد. به این ترتیب میتوان فایلهای مهم سیستمی، فایلهای تنظیماتی و… را فراخوانی کرد.
یک مثال ساده:
<?php
$file = basename($_GET['name']);
include(“include/” . $file . “.php”);
در این نمونه کد php، وجود تابع basename باعث میشود که ورودی فقط نام یک فایل باشد و نتوان آنرا به صورت آدرس یا مسیر یک پوشه وارد کرد. همچنین محدود کردن اول نام متغیر به پوشه include و انتهای آن به پسوند php باعث میشود که فقط فایلهای php در پوشه include تغییر کنند و لذا از LFI و RFI جلوگیری كردهايم.
در سایر زبانهای برنامهنویسی نیز به روشهای مشابه از LFI و RFI میتوان جلوگیری کرد. البته به این نکته نیز توجه داشته باشید که LFI و RFI بیشتر در زبان PHP رخ میدهد و در زبانهای برنامهنویسی دیگر به اندازه زبان PHP این حفره رخ نمیدهد.
به طور مثال در jsp ممکن است این حفره با کد زیر رخ دهد:
<%
String fileName = request.getParameter(“fileName”);
%>
<jsp:include page=”<%=fileName%>” flush=”true”/>
که باز هم با روشهای گفته شده میتوان این مشکل را حل کرد.
Session hijacking
استراق نشستها یا (Session Hijacking ) به طور کلی به حملهای گفته میشود که در آن session(یا در اصل session key) جعل میشود.
جلسه(session) اطلاعاتی هستند که به طور موقت ذخیره میشوند و تا زمان اعتبار آن، آن اطلاعات باقی میمانند.
معمولا از این اطلاعات در وب سایتها استفاده میشود تا کاربر مجبور نباشد این اطلاعات را چندین بار وارد کند. مرسومترین استفاده آن برای اطلاعات ورود به سایت است.
بدین گونه که پس از وارد کردن اطلاعات ورود به سایت، نرمافزار سایت یک جلسه ایجاد و در آن اطلاعات ورود را ذخیره میکند و تا زمان مشخص شده، این اطلاعات ذخیره میمانند.
بنابراین کاربر با داشتن کلید جلسه (session key) در مرورگر خود، برای باز کردن صفحات دیگر وب سایت نیاز به وارد کردن دوباره رمز عبور نخواهد داشت.
طبق توضیحات داده شده در مورد نحوه عملکرد جلسهها طبیعی است که شخص نفوذگر با داشتن كليد جلسه میتواند خود را به عنوان کاربر وارد شده در سایت معرفی کند و بدیهی است که این امر بدون داشتن نام کاربری یا رمز عبور قابل انجام خواهد بود که در اغلب مواقع نیز نفوذگر كليد جلسه مدیرسایت را جعل میکند و از این طریق وارد صفحه مدیریت وبسایت میشود.
راهها و راهکارها:
برای جعل کردن كليد جلسه راههای بسیاری وجود دارد:
۱. از طریق XSS
همانگونه که در قسمت های قبل دیدیم، با وجود حفره XSS، نفوذگر میتواند کدهای جاوااسكريپت روی سیستم قربانی اجرا کند.
از جمله کارهایی که میتوان با جاوا اسكريپت انجام داد، سرقت Cookieها و جلسهها است.
جلوگیری از XSS با روشهای گفته شده و همچنین HttpOnly کردن جلسهها و كوكيها به نحوی که در قسمت های قبل به آن اشاره شد از جمله راههای جلوگیری از این روش هستند.
۲. از طریق استراق سمع(Session sidejacking)
در این روش نفوذگر از طریق packetهای TCP/IP اطلاعات رد و بدل شده را به روش استراق سمع (معمولا در شبکههای بیسیم) دريافت و از آن كليد جلسه را استخراج میکند.
برای جلوگیری از این راه علاوه بر تامین امنیت شخصی، استفاده از پروتکل امن(https) میتواند به جلوگیری از این روش کمک کند. شایان ذکر است که اگر در https استراق سمع صورت گیرد، مرورگر با نمایش پیغام خطا شما را مطلع میکند.
۳. تثبیت نشست (session fixation)
در این روش نفوذگر كليد جلسه شخص مورد هدف را آن چیزی قرار میدهد که خود میخواهد. از این طریق شخص قربانی با وارد شدن به سایت(همراه با كليد جلسه که نفوذگر به او تحمیل کرده) این امکان را به مهاجم میدهد تا با استفاده از همان كليد جلسه خود را به جای قربانی معرفی کند.
برای جلوگیری از این روش، مهمترین کار برقراری امنیت شخصی است و اعتماد نداشتن به لینکهایی که فرستاده میشود.
به عنوان مثال اگر مهاجم آدرس http://site.com/?SID=TEST را برای قربانی ارسال کند و قربانی بدون توجه به آدرس آنرا باز کند، با كليد جلسه معادل TEST وارد سایت میشود و اگر در سایت وارد شود، مهاجم نیز میتواند با TEST(یعنی مراجعه به آدرس گفته شده در بالا) وارد سایت شود.
از راههای دیگر میتوان دسترسی داشتن از سرور و خواندن كليد جلسهها یا حتی به حدس زدن كليد جلسه اشاره کرد.
به طور کلی علاوه بر روشهای گفته شده، محدود کردن كليد جلسه به یک IP خاص نیز راه مناسبی برای مقابله با این نفوذ است. به این صورت که میتوان در اطلاعات ذخیره شده جلسه، اطلاعاتی از قبیل IP نیز ذخیره و زمانی که اطلاعات درون جلسه تطابق داده میشوند، IP نیز تطابق داده شود و در صورتی ناهمخوانی، آن جلسه را باطل کرد.
يکي از راههای دیگری که میتواند در بهبود امنیت کمک کند، تعریف زمان timeout است. به این صورت که به عنوان مثال اگر بعد از گذشت مثلا پنج دقیقه، کاربر هیچ فعالیتی در سایت نداشته باشد، آن جلسه باطل شود.
این امر باعث میشود که حتی در صورت به سرقت رفتن كليد جلسه، اگر پس از مدتی نفوذگر بخواهد وارد سایت شود، سیستم اجازه ورود را به او ندهد.
لینک نسخه PDF این مقاله. (http://barnamenevis.org/forum/attachment.php?attachmentid=60288&stc=1&d=1289770314)
منبع این مقاله هفته نامه عصر ارتباط (http://www.asreertebat.com)می باشد. این مقاله هر هفته به در همین پست به روز می شود و مطالب جدید به آن اضافه می شود. پس از تکمیل کامل مقاله فایل آن به صورت PDF در اختیار شما عزیزان قرار خواهد گرفت.
امروزه گسترش وب سایتها با روندی بسیار سریع رو به رشد است چراکه به صاحبان وب سایت این امکان را میدهد که با کمترین هزینه تجارت و کار خود را به تمامی دنیا معرفی کنند. امروزه بالغ بر ۱۲۰ میلیون وب سایت در اینترنت وجود دارد. متاسفانه افراد بدون در نظر گرفتن نکات امنیتی اطلاعات محرمانه و مهم را نیز در وب سایتها قرار میدهند.
اهمیت ویژه در امنیت وب سایتها به آن دلیل است که وبسایتها برخلاف منابع دیگر اطلاعات، در همیشه و در همه مکانها قابل دسترس برای عموم هستند. بنابراین اطلاعات محرمانه و مهم باید به گونهای امن شده باشند که هیچ کس توانایی دسترسی به آنها را نداشته باشد.
برای برقراری امنیت وب سایت باید سه فاکتور امنیتی زیر رعایت شود.
۱. امنیت شخصی:
رعایت نکردن اصول امنیتی از جانب مدیر وب سایت باعث بروز مشکلات امنیتی در سایت میشود، چراکه اگر به هر دلیلی رمز عبور و سایر اطلاعات وب سایت به دست اشخاص دیگر بیفتد، امکان دسترسی به تمام اطلاعات سایت وجود دارد. روشهای مقابله با این عامل در شمارههای قبل توضیح داده شده است.
۲. امنیت نرمافزار:
مهمترین فاکتور برای برقراری امنیت وب سایت، امنیت نرمافزاری وب سایت است که در این فرصت و مقالههای بعد به بررسی این مورد میپردازیم.
۳. امنیت سرور:
این مورد برای صاحبان سرور و سایتهایی است که برای راهاندازی از سرورهای اختصاصی استفاده میکنند. تنها کاری که دارندگان سایتهای معمولی در این زمینه میتوانند انجام دهند، خرید فضای وب سایت از هاستهای معتبر و امن است. بعد از بررسی امنیت نرمافزار به این مورد خواهیم پرداخت.
نرمافزار سایت، به برنامهای گفته میشود که باعث پویا شدن صفحات میگردد. سایتهایی که از صفحات ثابت (Static Page) برای نمایش محتویات استفاده میکنند فاقد نرمافزار سایت هستند و بنابراین از لحاظ امنیت نرمافزار دچار مشکل نخواهند شد.
گسترش روزافزون وب سایتها باعث گسترش طراحان وبسایتها نیز شده است. در این میان طراحانی که دانش کمتری نسبت به استانداردهای برنامهنویسی و امنیتی دارند نیز به این عرصه پا گذاشتهاند. این ناآگاهی باعث بروز مشکلات امنیتی(حفره یا Bug) در وب سایت طراحی شده میشود. مشکلاتی که حتی ممکن است برای بهترین برنامهنویسان (بر اثر بیدقتی) نیز به وجود آید. در ذیل نام متداولترین حفرههای موجود در نرمافزارهای سایتها و توضیح مختصری از هر یک آمده است. در مقالات بعدی به توضیح کاملتر و نحوه برطرف کردن هر مشکل میپردازیم:
SQL Injection: این حفره که متداول و در عین حال خطرناک است باعث دسترسی غیرمجاز به پایگاه داده میشود.
Cross-site scripting یا XSS: این حفره که متداولترین حفره موجود در نرمافزارهای سایتها است باعث اجرا شدن کدهای سمت کاربر میشود.
Remote Code Execution یا RCE: وجود این حفره خطرناک باعث اجرای کدهای دلخواه سمت سرور توسط حملهکننده است.
Remote File Inclusion یا RFI: این حفره نیز باعث اجرای کدهای درون یک فایل در سمت سرور خواهد بود.
Local File Inclusion یا LFI: وجود این حفره باعث اجرا شدن یا نمایش کدهای فایلهای درون سرور میشود.
Session Hijacking: با وجود این حفره، نفوذگر قادر خواهد بود که به عنوان مدیر سایت وارد سایت شود.
حفرههای نامبرده، متداولترین حفرههای موجود هستند البته حفرههای بسیار دیگری نیز در نرمافزار وبسایتها وجود دارد.
تزریق کد به پایگاهداده (SQL Injection) (قسمت 1)
همانطور که در بالا اشاره شد، SQL Injection حفرهای بسیار خطرناک است که در بسیاری از برنامههای تحت وب وجود دارد. SQL Injection زمانی به وجود میآید که ورودیها بدون کنترل در دستورهای پایگاه داده(Database) استفاده میشوند، این حفره ممکن است در هر برنامه که از پایگاه داده استفاده میکند به وجود آید. بهترین راه جلوگیری از آن نیز کنترل دادههای ورودی است. یک مثال ساده:
query = "SELECT * FROM users
WHERE uname=’” + Username + “’
AND password=’” + Password + “’;”
در این شبهکد انتظار میرود فقط هنگامی که نامکاربری(Username) و رمزعبور(Password) به درستی وارد شوند عمل ورود به سایت انجام پذیرد، اما با یک ترفند ساده میتوان نامکاربری را طوری وارد کرد که بدون کنترل شدن رمزعبور، عمل ورود به سایت انجام شود. کافی است به جای نامکاربری عبارت زیر را وارد کنیم:
' OR 1=1; - -
در این صورت دستور SQL به صورت زیر اجرا خواهد شد:
SELECT * FROM users
WHERE uname=’’ OR 1=1;- -’
AND password=’’;
در ساختار SQL دستورات بعد از علامت - - اجرا نمیشوند و چون 1=1 یک عبارت همیشه درست است، اولین کاربر انتخاب میشود. به این طریق بدون داشتن نام کاربری و رمزعبور وارد سایت شدهایم.
برای جلوگیری از انجام چنین اتفاقاتی برای زبانهای برنامهنویسی مختلف، راهکارهای گوناگونی وجود دارد که در ادامه با آنها آشنا میشوید.
جلوگیری از SQL Injection در PHP
۱. همیشه از درستی نوع متغیر ورودی اطمینان حاصل کنید. در زبان PHP انواع متغیرها وجود دارند. میتوانید با استفاده از توابعی مانند ctype_digit و ctype_alnum و سایر توابع خانواده ctype یا تابع gettype نوع ورودی را کنترل کنید. همچنین میتوانید با استفاده از(regular expression (PCRE از صحت اطلاعات اطمینان حاصل یابید.
۲. اگر قرار است در دستور SQL عدد وارد شود، با توابعی مانند is_numeric اطمینان حاصل کنید که ورودی حتما عدد است یا همیشه نوع ورودی را با تابعی مانند settype یا intval یا floatval یا … تغییر دهید.
۳. ورودیهایی که از نوع رشته(string) هستند را با توابع پایگاه داده مورد نظر escape کنید (مانند mysql_real_escape_string یا sqlite_escape_string یا ...) و اگر برای پایگاه داده مورد نظر شما چنین تابعی موجود نیست با استفاده از توابعی مانند addslashes یا str_replace این کار را انجام دهید. این عمل باعث میشود تا کاراکتری مانند ' در ساختار SQL تاثیری نگذارد و ورودی به عنوان متغیر به دستور داده شود و تاثیری بر دستور نداشته باشد.
۴. استفاده از stored procedures یکی از بهترین روشهای جلوگیری از SQL Injection در پایگاه دادههایی است که این قابلیت را دارند. اما متاسفانه همه پایگاهدادهها این قابلیت را ندارند.
۵. سعی کنید در هیچ شرایطی خطای رخ داده در پایگاه داده به کاربر نشان داده نشود، چراکه نمایش این خطاها میتواند به حملهکننده این امکان را بدهد که بداند چه اتفاقی در پایگاه داده انجام گرفته است. در PHP راههای مختلفی برای جلوگیری از نمایش خطاها وجود دارد. یکی از معروفترین آنها استفاده از عملگر @ قبل از دستورالعمل مورد نظر است. هنگامی که از این عملگر استفاده شود، PHP از پیامهای خطای دستور مورد نظر صرف نظر میکند. یک نمونه استفاده از عملگر:
$my_file=@file (‘names.php’) or die (‘failed’);
۶. سعی کنید دستورات اجرا شده در پایگاه داده را ثبت کنید. هر چند این امر به جلوگیری از SQL Injection کمکی نمیکند، اما به شما این امکان را میدهد تا با دیدن دستورات اجرا شده پی به اشتباهات خود برده و آنها را برطرف کنید. برای ثبت دستورات میتوانید از پایگاهدادههایی که این قابلیت را دارند استفاده یا با بهرهگیری از دستورات PHP آنها را درجایی امن ذخیره کنید.?>
تزریق کد به پایگاهداده(SQL Injection) (قسمت 2)
در بالا خواندیم که SQL Injection چیست و چگونه در زبان برنامهنویسی PHP از آن جلوگیری کنیم، در این مقاله میخوانیم که چگونه از این حفره امنیتی در زبان ASP.NET جلوگیری کنیم.
باید توجه داشت که برای تمامی زبانهای برنامهنویسی، راهکارهای متفاوتی وجود دارد. البته در باطن تمام این راهکارها هدف و نتیجه یکسانی دارند.
جلوگیری از SQL Injection در ASP.NET :
۱. از ورودیها اطمینان حاصل کنید.
سعی کنید تا جای ممکن تمامی ورودیها را از لحاظ نوع داده، طول رشته، بازه عددی و سایر موارد کنترل کنید. برای این کار میتوانید از Regex،RegularExpressionValidator یا RangeValidator استفاده کنید.
۲. استفاده از ورودیها در Stored Procedureها راه بسیار مناسبی است.
توجه داشته باشید که استفاده از Stored Procedureها بدون استفاده از ورودی باعث جلوگیری از SQL Injection نمیشود. برای این کار میتوانید ازSqlParameter و SqlParameterCollection استفاده کنید.
همچنین در صورتی که مجبور به استفاده از دستورات پویا (Dynamic) هستید، با استفاده از SqlParameterCollection نوع ورودیها را مشخص کنید.
۳. سعی کنید تا جای ممکن از API هایی مانند ADO.Net و قابلیتهای آن استفاده کنید، چرا که با کمک این رابطهای برنامهنویسی میتوان نوع دقیق دادهها را مشخص کرد و همچنین این اطمینان را داشت که ورودیها به طرز صحیحی Escape میشوند.
۴. تا حد امکان از کاربرانی با سطح دسترسی کم برای اتصال به پایگاه داده استفاده کنید. این کار باعث جلوگیری از SQL Injection نمیشود اما به این موضوع کمک میکند که کسی نتواند کدهای مخرب را روی بانک اطلاعاتی اجرا کند و بنابراین در صورت وجود SQL Injection نفوذگر قدرت مانور کمتری خواهد داشت.
۵. همیشه اطلاعات مهم مانند رمزهای عبور را به صورت کدشده در پایگاه داده(Database) ذخیره کنید. این کار نیز باعث جلوگیری از SQL Injection نمیشود اما باعث میشود در صورتی که مهاجم به پایگاه داده نفوذ کرد، نتواند اطلاعاتی مانند رمزهای عبور را به راحتی به دست بیاورد.
مثال: در نمونه کدهای زیر با کمک روشهای گفته شده تا حد امکان جلوی این حفره گرفته شده است:
<%@ language=»C#» %>
using System;
usingSystem.Text.RegularExpressions;
public void Login(string uname, string password)
{
if ( !Regex.IsMatch(uname, @"^[a-zA-Z'./s]{1,20}$"))
throw new FormatException("Invalid username");
if ( !Regex.IsMatch(password,@"^(?=.*\d)(?=.*[a-z])
(?=.*[A-Z]).{6,15}$" ))
throw new FormatException("Invalid password");
//...
}
usingSystem.Data;
usingSystem.Data.SqlClient;
using (SqlConnection connection = new SqlConnection(connectionString))
{
DataSet dataset = new DataSet();
SqlDataAdapter command = new SqlDataAdapter ("LoginStoredProce dure", connection);
command.SelectCommand.CommandType = CommandType.StoredProcedure;
command.SelectCommand.Parameters.Add("@au_id", SqlDbType.VarChar, 11);
command.SelectCommand.Parameters["@au_id"].Value = SSN.Text;
command.Fill(dataset);
}
تزریق کد به پایگاهداده (SQL Injection) (قسمت 3)
طی دو قسمت گذشته ما درباره مفهوم تزریق کد به پایگاه داده و راههای مقابله با آن در زبانهای برنامهنویسی مهم و پرکاربرد PHP و ASP.NET صحبت کردیم. اما هنوز درباره روشهای مقابله با SQL Injection در محبوبترین زبان برنامهنویسی، یعنی Java صحبت نکردهایم. این قسمت قصد داریم نگاهی به این زبان بیندازیم. پس با ما همراه باشید.
جلوگیری از SQL Injection در Java
در زبان Java نیز راهکارهایی مانند راهکارهای PHP و ASP.NET ارایه میشود.
۱. استفاده از پارامترها در Java راهی بسیار مناسب برای جلوگیری از SQL Injection است. استفاده از PreparedStatement یا CallableStatement برای این کار بسیار مناسب خواهد بود.
۲. برای استفاده از دستورات پویا(dynamic) نیز تا حد امکان از پارامترها استفاده شود. این امر باعث میشود که از صحت اطلاعاتی ورودی مانند اعداد اطمینان حاصل کنیم.
هیچگاه از اتصال دو رشته برای استفاده از دستورات پویا استفاده نشود. توجه به این نکته بسیار اساسی است. استفاده از کلاس java.sql.Statement برای اجرای دستورات پویا را به هیچ عنوان توصیه نمیکنیم.
۳. استفاده از Regex راهی بسیار مناسب برای صحت اطلاعات است. هر چند استفاده از Regex کار سادهای نیست، اما با کمک آن میتوان از صحت تمامی اطلاعات ورودی اطمینان خاطر حاصل کرد.
۴. درست کد کردن اطلاعات ورودی یا به اصطلاح escape کردن دادههای ورودی امری است که در تمامی زبانهای برنامهنویسی باید انجام گیرد. استفاده از تمام روشهای دیگر نیز در انتها منجر به انجام این کار میشود.
البته در زبان Java نسبت به زبانهای برنامهنویسی دیگر نیاز کمتری به استفاده از escape کردن وجود دارد. اما این امر را باید در نظر داشت که هر کار دیگر در نهایت برای escape کردن ورودیها است.
مثال:
در نمونه کدهای زیر نحوه استفاده از نکات گفته شده را میبینیم:
String selectString = "SELECT * FROM News WHERE newsId = ? ";
PreparedStatement selectQuery = con.prepareStatement(selectString);
selectQuery.setString(1, newsId);
ResultSet rs = selectQuery.executeQuery();
private static final Pattern codePattern = Pattern.compile("^\d{6}(-\d{3})?$");
public void processData( HttpServletRequest request, HttpServletResponse response)
{
try
{
String code = request.getParameter( "code" );
if ( !codePattern.matcher( code ).matches() {
throw new ValidationException( "Improper zipcode format." );
}
// ...
}catch(ValidationException e){
response.sendError( response.SC_BAD_REQUEST, e.getMessage() );
}
}
XSS (Cross Site Scripting)
امروزه وبسایتها به گونهای طراحی میشوند که قسمت عمدهای از خروجی باید به صورت پویا (Dynamic) باشد. پویا بودن سایت باعث راحتی کاربران است اما از طرفی این پویا بودن باعث به وجود آمدن حفرههای امنیتی زیادی نیز میشود.
XSS یا در اصل Cross Site Scripting عمدهترین حفره در وبسایتهای پویا محسوب میشود، به طوری که بیش از 80 درصد حفرههای گزارش شده وبسایتها از این نوع است(XSS را با CSS یا همان Cascading Style Sheets اشتباه نگیرید).
XSS به دو نوع کلی تقسیمبندی میشود، نوع اول که به ناپایدار(Non-persistent) معروف است دارای خطر زیادی نیست اما نوع دیگر که با نام پایدار(Persistent) آن را میشناسند، خطری جدی برای سایت محسوب میشود. قابل ذکر است که نوع اول بسیار متداولتر از نوع دوم است.
مهمترین خطری که به طور کلی از طریق این حفره رخ میدهد به سرقت رفتن cookieها و sessionهای مدیر سایت است که از طریق آن، حملهکننده خود را به عنوان مدیر سایت به نرمافزار سایت معرفی میکند.
این حفره در جاهایی رخ میدهد که ورودی بدون فیلتر شدن، همانگونه که وارد شده است به کاربر نمایش داده میشود. اگر این نمایش اطلاعات فقط در همان لحظه باشد، از نوع «ناپایدار» و در صورتی که در پایگاه داده یا جایی مانند آن ذخیره شود تا دوباره به کاربر نمایش داده شود، «پایدار» خواهد بود. در چنین مواردی کاربر با وارد کردن کدهای HTML خطرناک(مانند تگ script)، کدهای JavaScript یا استفاده کردن از iframe و نمایش صفحه دلخواه خود در آن، میتواند اطلاعات مهمی از بازدیدکننده دریافت کند(اغلب اوقات خود مدیر سایت).
راههای جلوگیری
۱. فیلتر کردن ورودیها: بهترین راه فیلتر کردن ورودیها است. جلوگیری از وارد کردن تگهای HTML یا فقط اجازه وارد کردن برخی از تگ های HTML راه بسیار مناسبی برای جلوگیری از این حفره خواهد بود.
۲. امنیت کوکیها: علاوه بر روش قبل، امن کردن کوکیها کاری است که حتی در صورت وجود این حفره میتواند از سرقت رفتن برخی اطلاعات مربوط به ورود به سایت که معمولا در کوکیها یا جلسات (session) ذخیره میشوند، جلوگیری کرد. قابلیتی در مرورگرهای جدید گنجانده شده است که با کمک آن میتوان برخی از کوکیهای مهم را به صورت HTTP-Only قرار داد که باعث میشود کوکیها از طریق جاوا اسکریپت قابل دسترس نباشند و لذا با این قابلیت، سرقت کوکیها از این طریق دیگر امکانپذیر نخواهد بود.
۳. غیرفعال کردن جاوا اسکریپت: با وجود Web2 و AJAX همچنان سایتهایی موجود هستند که از جاوا اسکریپت استفاده نمیکنند. همچنین وبسایتها برای جلوگیری از به سرقت رفتن کوکیها میتوانند به طور کل جاوا اسکریپت را غیرفعال کرده که در این صورت دیگر از این طریق امکان سرقت کوکیها وجود نخواهد داشت.
در بین روشهایی که گفته شد تنها روش اول توصیه میشود و در روشهای دیگر این مشکل حل نمیشود، بلکه امنیت شخصی کمی بهتر میشود.
جلوگیری از این روش در زبانهای برنامهنویسی مختلف
در زبان PHP برای جلوگیری از ورود هر گونه تگ HTML میتوان از تابع htmlentities استفاده کرد. این تابع تمام دادههای ورودی را طوری تغییر میدهد که در صورت وجود تگهای HTML در این دادهها فقط به عنوان کاراکتر شناخته شده و اجرا نشوند.
<?php
$name = htmlentities($_POST['nam ']);
echo $name;
?>
تابع strip_tags نیز در PHP به این منظور است که تمام تگهای HTML را از داده ورودی حذف کند. البته این قابلیت را نیز دارد که تگهایی را که برنامهنویس برای آن تعریف میکند را به عنوان ورودی قبول کند.
<?php
$text = strip_tags($_POST[<text>],><a><b><i><u><p>>);
echo $text;
?>
در زبان ASPX نیز میتوان از <asp:Literal> و HtmlEncode استفاده کرد:
<%@ Page Language=”C#” AutoEventWireup=”true”%>
<html>
<asp:Literal ID=”Literal1” runat=”server”></asp:Literal>
</html>
<script runat=”server”>
Literal1.Text = Server.HtmlEncode(Text1.Text);
</script>
یا:
Response.Write(HttpUtility.HtmlEncode(Request.Form[“text”]));
توجه داشته باشید که توابع گفته شده در بالا تنها یک نمونه هستند و طبیعتا توابع دیگری نیز برای این کار وجود دارد.
Remote Code Execution (RCE)
حفره امنیت صفحات اینترنتی که این هفته میخواهیم در مورد آن صحبت کنیم، RCE است. این حفره به نفوذگر توانایی اجرای کد در سرور را میدهد. بنابراین با اجرای کدهای مورد نظر خود، تقریبا هر کاری روی وب سایت مورد حمله میتواند انجام دهد. این حفره به ندرت رخ میدهد اما در عین حال بسیار خطرناک است. RCE در زمانی رخ میدهد که در کد نوشته شده جایی وجود داشته باشد که در آن کدها به صورت dynamic (پویا) اجرا میشوند و بتوان به هر نحو محتویات کد پویا را تغییر داد. توجه داشته باشید که پویا بودن سایت با پویا بودن کد بسیار متفاوت است، چراکه پویا بودن کد بدان معنا است که کد برنامه به نسبت شرایط مختلف، تغییر کند. اما پویا بودن سایت بدان معناست که محتویات نمایش داده شده (خروجی) سایت تغییر کند. باید به این نکته توجه داشت که SQL Injection و XSS که در مقالههای قبل به آنها پرداخته شد، خود نوعی RCE هستند که روی پایگاه داده و خروجی HTML رخ میدهند اما چیزی که در اینجا حائز اهمیت خواهد بود این است که در برخی موارد ممکن است اجرای کد در کدهای برنامه یا در برنامههای دیگری که برنامه ما صدا میزند، رخ دهد. با اینکه RCE به طور کل به تمامی موارد گفته میشود اما اصطلاحا فقط برای این مورد از RCE استفاده میکنیم و سایر موارد را با نامهای دیگر میشناسیم. RCE نیز مانند بسیاری از حفرههای دیگر با کنترل ورودیها برطرف میشود چرا که زمانی این حفره پدیدار میشود که ورودیها شامل کدهای خطرناک باشند. برای جلوگیری از RCE بهترین راه این است که تا حد امکان از قابلیت پویا بودن کد استفاده نکنیم چرا که در بسیاری از موارد(به جز موارد خاص) این قابلیت استفادهای ندارد و میتوان با سایر دستورات برنامهنویسی مانند حلقهها، شرطها و ... کار مورد نظر را انجام داد. پس تاکید میشود که تا حد ممکن از امکانات پویا بودن کد استفاده نکنید. باید تا جای امکان از ورودیها اطمینان حاصل کرد. برای اطمینان از ورودیها میتوان از روشهایی که در مقالات قبل خواندیم، استفاده کنیم.
چند مثال ساده: در زبان PHP یکی از سادهترین توابعی که باعث بروز RCE میشود، تابع eval است:
<?php
$myvar = “testvar”;
$value = intval($_GET[‘input’]);
eval(“\$myvar = \$value;”);
?>
توجه داشته باشید که در مثال یاد شده اگر از تابع intval استفاده نشود، ورودی میتواند طوری وارد شود که حمله کننده بتواند هر دستور PHP دلخواه را اجرا کند.
در این مثال PHP نیز برنامهای جداگانه صدا زده میشود که کار ترجمه متن را انجام میدهد:
<?php
passthru( «/user/dic/en2fa « . escapeshellarg($_GET['text']) );
?>
در مثال فوق تابع escapeshellarg باعث میشود که ورودی هیچ کد مخربی نباشد و اگر از این تابع استفاده نشود میتوان کدهای بسیار خطرناکی را اجرا کرد. در زبان ASP.NET نیز همین تهدیدها موجود هستند. در مثال زیر کدهای مورد نظر در فایل ذخیره و در مواقع لزوم بازخوانی میشوند:
<%
If Not IsEmpty(Request(«username»)) Then
Dim fso, f
Set fso = CreateObject(«Scripting.FileSystemObject»)
Set f = fso.OpenTextFile(Server.MapPath( «userlog.txt» ), 8, True)
f.Write Request(«username») & vbCrLf
f.close
Set f = nothing
Set fso = Nothing
%>
<h1>List of logged users:</h1>
<pre><%
Server.Execute(«userlog.txt»)
%></pre><%
Else
%>
<form>
<input name=»username» /><input type=»submit» name=»submit» />
</form>
<%
End If
%>
در مثال یاد شده username بدون کنترل وارد فایل و به عنوان کد اجرا میشود. برای جلوگیری از چنین مشکلی توصیه میشود که مثالی مانند این استفاده نشود و به جای آن از پایگاه داده برای ذخیره استفاده یا به جای Server.Execute به طوری معمولی محتویات فایل خوانده شود.
RFI (Remote File Inclusion) و LFI (Local File Inclusion)
در بخش قبل با حفره RCE و نحوه برطرف کردن آن آشنا شدیم. همانطور که خواندیم RCE زمانی رخ میدهد که کدهای برنامه به صورت پویا اجرا شوند. پویا بودن کدها به صورتی که در مقاله قبل به آن اشاره شد متداول نيست و فقط در موارد خاص رخ میدهد اما تکه کردن کدهای مختلف در فایلهای مختلف و فراخوانی آنها در مواقع لزوم(include کردن) کاری بسیار متداول است و تقریبا تمام برنامهنویسها با آن آشنایی دارند.
فراخوانی فایل دیگر به طور عادی خطری ندارد اما در مواقعی خاص میتواند به یکی از خطرناکترین حفرهها تبدیل شود. فراخوانی فایل در این دو حالت کلی میتواند خطرناک واقع شود:
1. زمانی که فایل فراخوانی شده در جایی ذخیره شود که بتوان از آنجا دسترسی گرفت.
2. زمانی که نام فایل فراخوانی شده متغیر باشد و در شرایط مختلف مقادیر مختلفی بگیرد.
در حالات گفته شده حالت دوم متداولتر است. برای حالت اول در حد امکان تمام فایلها را در جایی امن ذخیره و از فایلهای خارج از server در حد امکان استفاده نمیکنیم.
برای برطرف کردن حالت دوم سادهترین راه پاک کردن صورت مساله است. یعنی در حد امکان نام فایلها را متغیر قرار ندهیم. اما در برخی از موارد اینکار عملی نيست و لازم است که نام فایل فراخوانی شده با توجه به شرایط تغییر کند.
برای چنین مواردی لازم است تا متغیر از هر لحاظ فیلتر شود. میتوان قسمتی از نام فایل را که برای همه فایلها یکسان است را ثابت قرار داد و فقط قسمتی که احتیاج به تغییر دارد را متغیر قرار داد. در این صورت، اگر قبل از متغیر ثابت باشد، از RFI جلوگیری میشود و در سایر موارد کار نفوذ کردن بسیار سختتر خواهد شد.
بهترین راه برای جلوگیری از این حفره استفاده از لیستی ثابت از نام فایلهای مورد نظر است و در صورت نبود مقدار متغیر در لیست میتوان از include کردن فایل جلوگیری کرد. همچنین حذف کردن تمامی علامتهایی که باعث میشوند فایل از پوشهای دیگر خوانده شود(مانند / و \ )، باعث میشود تا جلوی فراخوانی فایلهای خطرناک گرفته شود.
به طور کلی زمانی RFI (وارد كردن فايل راه دور) رخ میدهد که به جای نام فایل بتوان هر آدرس دلخواهی قرار داد. در این صورت حملهکننده میتواند فایل مخرب خود را روی سایت خود آپلود کرده و با دادن آدرس آن از سایت شما بخواهد که آنرا اجرا کنید و به این صورت هر دستوری را روی سایت شما اجرا کند.
LFI(وارد كردن فايل محلي) نیز زمانی رخ میدهد که بتوان نام فایل را تغییر داد و به جای آن هر فایل روی server را فراخوانی کرد. به این ترتیب میتوان فایلهای مهم سیستمی، فایلهای تنظیماتی و… را فراخوانی کرد.
یک مثال ساده:
<?php
$file = basename($_GET['name']);
include(“include/” . $file . “.php”);
در این نمونه کد php، وجود تابع basename باعث میشود که ورودی فقط نام یک فایل باشد و نتوان آنرا به صورت آدرس یا مسیر یک پوشه وارد کرد. همچنین محدود کردن اول نام متغیر به پوشه include و انتهای آن به پسوند php باعث میشود که فقط فایلهای php در پوشه include تغییر کنند و لذا از LFI و RFI جلوگیری كردهايم.
در سایر زبانهای برنامهنویسی نیز به روشهای مشابه از LFI و RFI میتوان جلوگیری کرد. البته به این نکته نیز توجه داشته باشید که LFI و RFI بیشتر در زبان PHP رخ میدهد و در زبانهای برنامهنویسی دیگر به اندازه زبان PHP این حفره رخ نمیدهد.
به طور مثال در jsp ممکن است این حفره با کد زیر رخ دهد:
<%
String fileName = request.getParameter(“fileName”);
%>
<jsp:include page=”<%=fileName%>” flush=”true”/>
که باز هم با روشهای گفته شده میتوان این مشکل را حل کرد.
Session hijacking
استراق نشستها یا (Session Hijacking ) به طور کلی به حملهای گفته میشود که در آن session(یا در اصل session key) جعل میشود.
جلسه(session) اطلاعاتی هستند که به طور موقت ذخیره میشوند و تا زمان اعتبار آن، آن اطلاعات باقی میمانند.
معمولا از این اطلاعات در وب سایتها استفاده میشود تا کاربر مجبور نباشد این اطلاعات را چندین بار وارد کند. مرسومترین استفاده آن برای اطلاعات ورود به سایت است.
بدین گونه که پس از وارد کردن اطلاعات ورود به سایت، نرمافزار سایت یک جلسه ایجاد و در آن اطلاعات ورود را ذخیره میکند و تا زمان مشخص شده، این اطلاعات ذخیره میمانند.
بنابراین کاربر با داشتن کلید جلسه (session key) در مرورگر خود، برای باز کردن صفحات دیگر وب سایت نیاز به وارد کردن دوباره رمز عبور نخواهد داشت.
طبق توضیحات داده شده در مورد نحوه عملکرد جلسهها طبیعی است که شخص نفوذگر با داشتن كليد جلسه میتواند خود را به عنوان کاربر وارد شده در سایت معرفی کند و بدیهی است که این امر بدون داشتن نام کاربری یا رمز عبور قابل انجام خواهد بود که در اغلب مواقع نیز نفوذگر كليد جلسه مدیرسایت را جعل میکند و از این طریق وارد صفحه مدیریت وبسایت میشود.
راهها و راهکارها:
برای جعل کردن كليد جلسه راههای بسیاری وجود دارد:
۱. از طریق XSS
همانگونه که در قسمت های قبل دیدیم، با وجود حفره XSS، نفوذگر میتواند کدهای جاوااسكريپت روی سیستم قربانی اجرا کند.
از جمله کارهایی که میتوان با جاوا اسكريپت انجام داد، سرقت Cookieها و جلسهها است.
جلوگیری از XSS با روشهای گفته شده و همچنین HttpOnly کردن جلسهها و كوكيها به نحوی که در قسمت های قبل به آن اشاره شد از جمله راههای جلوگیری از این روش هستند.
۲. از طریق استراق سمع(Session sidejacking)
در این روش نفوذگر از طریق packetهای TCP/IP اطلاعات رد و بدل شده را به روش استراق سمع (معمولا در شبکههای بیسیم) دريافت و از آن كليد جلسه را استخراج میکند.
برای جلوگیری از این راه علاوه بر تامین امنیت شخصی، استفاده از پروتکل امن(https) میتواند به جلوگیری از این روش کمک کند. شایان ذکر است که اگر در https استراق سمع صورت گیرد، مرورگر با نمایش پیغام خطا شما را مطلع میکند.
۳. تثبیت نشست (session fixation)
در این روش نفوذگر كليد جلسه شخص مورد هدف را آن چیزی قرار میدهد که خود میخواهد. از این طریق شخص قربانی با وارد شدن به سایت(همراه با كليد جلسه که نفوذگر به او تحمیل کرده) این امکان را به مهاجم میدهد تا با استفاده از همان كليد جلسه خود را به جای قربانی معرفی کند.
برای جلوگیری از این روش، مهمترین کار برقراری امنیت شخصی است و اعتماد نداشتن به لینکهایی که فرستاده میشود.
به عنوان مثال اگر مهاجم آدرس http://site.com/?SID=TEST را برای قربانی ارسال کند و قربانی بدون توجه به آدرس آنرا باز کند، با كليد جلسه معادل TEST وارد سایت میشود و اگر در سایت وارد شود، مهاجم نیز میتواند با TEST(یعنی مراجعه به آدرس گفته شده در بالا) وارد سایت شود.
از راههای دیگر میتوان دسترسی داشتن از سرور و خواندن كليد جلسهها یا حتی به حدس زدن كليد جلسه اشاره کرد.
به طور کلی علاوه بر روشهای گفته شده، محدود کردن كليد جلسه به یک IP خاص نیز راه مناسبی برای مقابله با این نفوذ است. به این صورت که میتوان در اطلاعات ذخیره شده جلسه، اطلاعاتی از قبیل IP نیز ذخیره و زمانی که اطلاعات درون جلسه تطابق داده میشوند، IP نیز تطابق داده شود و در صورتی ناهمخوانی، آن جلسه را باطل کرد.
يکي از راههای دیگری که میتواند در بهبود امنیت کمک کند، تعریف زمان timeout است. به این صورت که به عنوان مثال اگر بعد از گذشت مثلا پنج دقیقه، کاربر هیچ فعالیتی در سایت نداشته باشد، آن جلسه باطل شود.
این امر باعث میشود که حتی در صورت به سرقت رفتن كليد جلسه، اگر پس از مدتی نفوذگر بخواهد وارد سایت شود، سیستم اجازه ورود را به او ندهد.
لینک نسخه PDF این مقاله. (http://barnamenevis.org/forum/attachment.php?attachmentid=60288&stc=1&d=1289770314)