0

کوکی های ضد گلوله

 
amirpetrucci0261
amirpetrucci0261
کاربر طلایی1
تاریخ عضویت : تیر 1388 
تعداد پست ها : 27726
محل سکونت : http://zoomstar.ir/

کوکی های ضد گلوله
چهارشنبه 10 آذر 1389  5:19 AM

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

مقدمه

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


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


از آنجایی که کوکی ها به طور بسیار گسترده در ASP.NET به کاربرده میشوند، مثلا در شناسایی یک session و یا پیاده سازی ویژگی remember me و ...، شناسایی ویژگیها و آنچه که کوکی ها را آسیب پذیر میسازد اهمیت ویژه ای میابد.


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


بسیارخوب، ابتدا بیایید در باره مشکلاتی که کوکی ها دارند صحبت کنیم و برای هر مشکل مثالی بزنیم. همانطور که میدانید کوکی ها محتوایشان را در یک متن کاملا واضح به گونه ای نگهداری میکنند که توسط کاربران به راحتی قابل خواندن و ویرایش شدن هستند. برخی کوکی ها اطلاعات مربوط به کاربارن مانند نام کاربری و کلمه عبور را نگهداری میکنند.


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


مشکل دیگر کوکی ها این است که برخی برنامه تحت وب به محتوای ورودی کوکی ها به صورت کورکورانه اعتماد میکنند و فرض را بر این میگذارند که هرچه در محتوای کوکی ها وجود دارند کاملا صحیح و قابل اعتماد است! مثلا من زمانی در سایتی عضو بودم که در آن گزینه "مرا به خاطر بسپار" را انتخاب کرده بودم.وب سایت مذکور برای من یک کوکی ارسال کرده بود که در آن عدد صحیحی وجود داشت که نشاندهنده شناسه کاربری من بود. من توانستم با ویرایش آن عدد به راحتی بعنوان یک کاربر دیگر وارد سیستم آن وب سایت گردم!


برای حل این مشکل نیاز داریم که به نحوی مطلع گردیم که کوکی صادر شده توسط ما مورد ویرایش قرار نگرفته است. البته این کاملا بجاست که شما بپرسید پس رمزنگاری چه شد؟ چگونه امکان دارد فردی بتواند کوکی رمزنگاری شده را ویرایش کند؟ در پاسخ به این سوال میتوانیم این گونه بگوییم که ممکن است یک نفوذگر توانسته باشد کلیدرمزنگاری را بدست آورده و کوکی ها را رمزگشایی کند. علاوه بر آن حتی علی رغم رمزنگاری کردن بازهم مشکلاتی ممکن است رخ دهندو مثلا فرض کنید پس از رمزنگاری یک عدد صحیح مثل 125 به عبارتی مانند 13285421574875421321321879321321 تبدیل گردد. چنانچه نفوذگر مثلا 10 واحد به این عدد اضافه کند ممکن دقیقا به همان عبارتی برسد که از رمزنگاری کردن عدد مثلا 124 بدست آمده است. پس در اینجا رمزنگاری این مشکل را حل نمیکند و به راه حل بهتری نیازمندیم.


مشکل دیگر در کار با کوکی ها این است که معمولا به مرورگر در زمینه محو کردن کوکی ها پس از تاریخ انقضای آنها اعتماد میکنیم و انتظار داریم تا پس از تاریخ مذکور دیگر مرورگر کوکی مذکور را به وب سایت ما ارسال نکند. این اعتماد کاملا بیجاست! از آنجایی که کوکی ها در سمت کلاینت به راحتی قابل ویرایش هستند تاریخ انقضای آنها را نیز میتوان به سادگی تغییر داد. البته این مشکل را میتوانیم به نحوی با رمزنگاری کردن تاریخ انقضا و قرار دادن آن در متن کوکی حل کنیم.


مساله دیگری که مطرح میگردد این است که از کوکی ها برای مقید سازی یک کلاینت به یک session روی سرور استفاده میشود. از آنجایی که HTTP هیچ راهی برای شناسایی یک ارتباط به صورت یکتا ندارد، مجبور است کوکی های مخصوص به هر session را به همراه دیگر کوکی های ممکن در هر تقاضا ( request) ارسال کند. حال چنانچه یک نفوذگر بتواند به هر نحوی - مانند بوکشیدن بسته ها (packet sniffing) و یا روشهای دیگر – کوکی های مخصوص به Session را بدست آورد، متیواند در همان لحظه session مربوط به کاربر قربانی را سرقت کرده و بعنوان آن کاربر وارد سیستم گردد. در چنین شرایطی حتی رمزنگاری کردن محتوای کوکی ها هم عملا کاربردی ندارد.


اگر بخواهیم آنچه را که گفتیم جمع بندی کنیم، میباست برای ارتقای امنیت کوکی ها موارد زیر را فراهم سازیم :


• اطمینان حاصل کنیم که کوکی‌ها در یک متن واضح ذخیره نشده و رمزنگاری شده اند.


• اطمینان حاصل کنیم که کوکی‌ها ویرایش ندشه اند و اصولا به گونه ای نیستند که قابل ویرایش شدن باشند


• اطمینان حاصل کنیم که کوکی‌ها تاریخ انقضای بخصوصی دارند و این تاریخ به صورت رمزنگاری شده در متن کوکی‌ها قرار دارد.


• به بهترین شکل ممکن کوکی ها را به کامپیوتر کلاینت به گونه ای مقید سازیم که علی رغم سرقت آنها، شناسایی اصل یا جعلی بودن آنها ممکن باشد.


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


ساده ترین راهی که به ذهن میرسد شاید این باشد که کوکی را به آدرس IP کامپیوتر کلاسنت مقید سازی کنیم. کاری که مسلما کاربردی نخواهد بود چرا که نفوذگر به راحتی میتواند با انواع نرم افزارهای Proxy یا روشهای دیگر با تغییر آدرس IP خود این راهکار ما را بی اثر کند.


یک راه دیگر مقید سازی به متغیر سمت سرور “REMOTE_PORT” است. محتوای این متغیر شماره پورتی است که کامپیوتر کلاینت برای کار با سرور بازکرده است. التبه همانگونه که خود حدس زده اید این راخ هم نیز کارآمد نیست چرا که اگر کارر دو پنجره مروگر را باز کند دو پورت برای کار با سرور باز میشوند.


ممکن است SSL ( Secure Socket Layer ) را پیشنهاد دهید. البته که SSL میتواند مشکل بو کشده بسته ها یا packet sniffing را حل کند اما بازهم تضمین کننده نیست چرا که زمانی که شما یک کوکی را به عنوان چیزی که باید ایمن باشد انتخاب میکنید، بازهم این مرورگر است که انتخاب میکند چگونه کوکی را ذخیره کند و بنابریان اگر نفوذگری دسترسی فیزیکی به کامپیوتر قربانی داشته باشد میتواند به راحتی از این کوکی سوء استفاده کند.


بسیارخوب، بیایید دست به کار شویم و تمام تلاش خود را برای ایمن سازی و ضدگلوله کردن کوکی ها به کار ببریم! ابتدا بیایید با رمزنگاری کردن شروع کنیم. چیزی که محیط ASP.NET از متدها و الگوریتمها متعددی برای انجام آن برخوردار است.


در قطعه کد زیر متدری را طراحی کرده ایم که یک رشته را دریافت کرده و آن را رمزنگاری شده برمیگرداند. این نکته را به خاطر داشته باشید که هر برنامه میبایست کلید رمزنگاری ویژه خود را داشته باشد.

// must create your own keys and ivs
private static byte[] key_192 = new byte[]
{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
private static byte[] iv_128= new byte[]
{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10};
public static string EncryptRijndaelManaged(string value)
{
if (value == "")
return "";
RijndaelManaged crypto = new RijndaelManaged();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, crypto.CreateEncryptor(key_192,
iv_128), CryptoStreamMode.Write);
StreamWriter sw = new StreamWriter(cs);
sw.Write(value);
sw.Flush();
cs.FlushFinalBlock();
ms.Flush();
return Convert.ToBase64String(ms.GetBuffer(), 0, (int) ms.Length);
}

همچنین تابعی برای رمزگشایی کردن آنچه که رمزنگاری کرده ایم و تبدیل آن به متن واضح استفاده میکنیم.

public static string DecryptRijndaelManaged(string value)
{
if (value == "")
return "";
RijndaelManaged crypto = new RijndaelManaged();
MemoryStream ms = new MemoryStream(Convert.FromBase64String(value));
CryptoStream cs = new CryptoStream(ms, crypto.CreateDecryptor(key_192,
iv_128), CryptoStreamMode.Read);
StreamReader sw = new StreamReader(cs);
return sw.ReadToEnd();
}

کد بالا نیاز رمزنگاری ما را برطرف میکند اما بازهم تاکید میکنیم که برای هر برنامه باید یک کلید رمزنگاری منحصر به فرد برای آن برنامه را داشته باشید.


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


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


همچنین من تصمیم دارم تا چندین متغیر را در یک کوکی ترکیب کنم. برای این کار از یک ساختار XML مانند استفاده میکنم. این متغیرها میتوانند داده های معمولی کوکی، زمان انقضا و دیگر موارد مانند آدرس IP و ... باشند.


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


همچنین تابعی خواهیم نوشت که این رشته را بگیرد و آن را رمزگشایی کند و سپس داده های رمزگشایی شده را از نظر عدم دستگاری شدن ارزیابی کرده و نهایتا اطلاعات ترکیب شده را تفکیک کرده و به صورت اطلاعات اولیه برگرداند.

public static string SignAndSecureData(string value)
{
return SignAndSecureData(new string[] {value});
}
public static string SignAndSecureData(string[] values)
{
string xmlKey = "MUST ADD YOUR OWN DEFAULT XML RSA KEY HERE";
return SignAndSecureData(xmlKey, values);
}
public static string SignAndSecureData(string xmlKey, string[] values)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml("<x></x>");
for (int i = 0; i < values.Length; i++)
_AddNode(xmlDoc, "v" + i.ToString(), values[i]);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xmlKey);
byte[] signature = rsa.SignData(Encoding.ASCII.GetBytes(xmlDoc.InnerXml),
"SHA1");
_AddNode(xmlDoc, "s", Convert.ToBase64String(signature, 0,
signature.Length));
return EncryptRijndaelManaged(xmlDoc.InnerXml);
}
And below this we will find the code that does the decryption, verification and separation of values
public static bool DecryptAndVerifyData(string input, out string[] values)
{
string xmlKey = "MUST ADD YOUR OWN DEFAULT XML RSA KEY HERE";
return DecryptAndVerifyData(xmlKey, input, out values);
}
public static bool DecryptAndVerifyData(string xmlKey, string input,
out string[] values)
{
string xml = DecryptRijndaelManaged(input);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
values = null;
XmlNode node = xmlDoc.GetElementsByTagName("s")[0];
node.ParentNode.RemoveChild(node);
// verify
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xmlKey);
byte[] signature = Convert.FromBase64String(node.InnerText);
byte[] data = Encoding.ASCII.GetBytes(xmlDoc.InnerXml);
if (!rsa.VerifyData(data, "SHA1", signature))
return false;
// count values
int count;
for (count = 0; count < 100; count++)
{
if (xmlDoc.GetElementsByTagName("v" + count.ToString())[0] == null)
break;
}
values = new string[count];
for (int i = 0; i < count; i++)
values[i] = xmlDoc.GetElementsByTagName("v" +
i.ToString())[0].InnerText;
return true;
}

Please note that to get an XML RSA key, you can simply create an object of RSACryptoServiceProvider and call ToXmlString to get the key (include private key for signing).

توجه داشته باشید که برای گرفتن یک کلید xml rsa میتوانید به سادگی یک شیء از کلاس RSACryptoServiceProvider ساخته و سپس ToXmlString را فراخوانی کنید تا کلید را (به انضمام کلید خصوصی مورد استفاده برای امضا کردن) دریافت کند.

I will now write two functions that will make use of these two methods to sign and secure cookies and symmetrically decrypt and verify cookies

دقت داشته باشید که در این مرحله شما میتوانید از دو تابع بسیار قدرتمند به نامهای SignAndSecureData و DecryptAndVerifyData استفاده کنید که میتوانند در بخشهای متعدد کد شما برای امضا و رمزنگاری داده ها مورد استفاده قرار بگیرند.


توابع زیر با استفاده از دو تابع مذکور برای امضا کردن و رمزنگاری و رمزگشایی نوشته شده اند:

public static void SignAndSecureCookie(HttpCookie cookie,
NameValueCollection serverVariables)
{
if (cookie.HasKeys)
throw (new Exception("Does not support cookies with sub keys"));
if (cookie.Expires != DateTime.MinValue) // has an expiry date
{
cookie.Value = SignAndSecureData(new string[]
{cookie.Value,
serverVariables["REMOTE_ADDR"],
cookie.Expires.ToString()});
}
else
{
cookie.Value = SignAndSecureData(new string[]
{cookie.Value, serverVariables["REMOTE_ADDR"]});
}
}
public static string DecryptAndVerifyCookie(HttpCookie cookie,
NameValueCollection serverVariables)
{
if (cookie == null)
return null;
string[] values;
if (!DecryptAndVerifyData(cookie.Value, out values))
return null;
if (values.Length == 3) // 3 values, has an expiry date
{
DateTime expireDate = DateTime.Parse(values[2]);
if (expireDate < DateTime.Now)
return null;
}
if (values[1] != serverVariables["REMOTE_ADDR"])
return null;
return values[0];
}

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


بیایید ببینیم چگونه متیوانیم از این کدها برای انجام 2 کار معمول استفاده کنیم. کار اول ایمن کردن کوکیهای session به روشی بهتر است. راهی که ما در پیش گرفته ایم سربارگذاری و پیاده سازی 3 تابع در فایل Global.aspx است. تابع اول Session_Start است که برای ایجاد یک کوکی دیگر که در آن شناسه session موجود باشد استفاده میشود. همچنین برای امضای دیجیتالی نیز از این تابع استفاده میشود.


تابع دوم پیاده سازی مدیر رخداد PreRequestHandlerExecute است. در این تابع ما شناسه session کوکی را با آن شناسه ای مقایسه میکنم که در کوکی امضا شده مان قرار دارد. در صورت عدم تطابق، session فعلی را رها میکنیم.


در نهایت تابع Session_End را پیاده سازی خواهیم کرد که مطمئن گردیم مرورگر کوکی امضا شده ما را حذف خواهد کرد.


دقت داشته باشید که کدهای زیر در فایل Global.aspx قرار دارند.

protected void Session_Start(Object sender, EventArgs e)
{
System.Web.HttpCookie cookie
= new System.Web.HttpCookie("__signed_session",
Session.SessionID);
CHelperMethods.SignAndSecureCookie(cookie, Request.ServerVariables);
Response.Cookies.Add(cookie);
}
private void Global_PreRequestHandlerExecute(object sender,
System.EventArgs e)
{
if (CHelperMethods.DecryptAndVerifyCookie(
Request.Cookies["__signed_session"],
Request.ServerVariables) != Session.SessionID)
{
Session.Abandon();
Response.Redirect("http:// YOUR MAIN PAGE HERE");
}
}
protected void Session_End(Object sender, EventArgs e)
{
Response.Cookies["__signed_session"].Expires = DateTime.Now.AddDays(-1);
}

نکته :

وقت داشته باشید که قبل از اقدام به ایمن سازی کوکی ها در نظر داشته باشید که این کار باعث ایجاد سربار بیشتر بر روی سیستم و تلف شدن زمان میگردد. بخصوص با در نظر گرفتن این که کوکی ها با هر درخواست یا request ارسال میشوند، بنابراین در مواردی باید از این ایمن سازی استفاده شود که اطلاعات مهمی درون کوکی ذخیره شده است.

تشکرات از این پست
دسترسی سریع به انجمن ها