۱۳۸۷/۱۱/۰۷

تعيين اعتبار يك GUID در دات نت


GUID يا Globally unique identifier يك عدد صحيح 128 بيتي است (بنابراين 2 به توان 128 حالت را مي‌توان براي آن درنظر گرفت). از لحاظ آماري توليد دو GUID يكسان تقريبا صفر مي‌باشد. به همين جهت از آن با اطمينان مي‌توان به عنوان يك شناسه منحصربفرد استفاده كرد. براي مثال اگر به لينك‌هاي دانلود فايل‌ها از سايت مايكروسافت دقت كنيد، اين نوع GUID ها را به وفور مي‌توانيد ملاحظه نمائيد. يا زمانيكه قرار است فايلي را كه بر روي سرور آپلود شده، ذخيره نمائيم، مي‌توان نام آن‌را يك GUID درنظر گرفت بدون اينكه نگران باشيم آيا فايل آپلود شده بر روي يكي از فايل‌هاي موجود overwrite مي‌شود يا خير. يا مثلا استفاده از آن در سناريوي بازيابي كلمه عبور در يك سايت. هنگاميكه كاربري درخواست بازيابي كلمه عبور فراموش شده خود را داد، يك GUID براي آن توليد كرده و به او ايميل مي‌زنيم و در آخر آن‌را در كوئري استرينگي دريافت كرده و با مقدار موجود در ديتابيس مقايسه مي‌كنيم. مطمئن هستيم كه اين عبارت قابل حدس زدن نيست و همچنين يكتا است.

براي توليد GUID ها در دات نت مي‌توان مانند مثال زير عمل كرد و خروجي‌هاي دلخواهي را با فرمت‌هاي مختلفي دريافت كرد:

System.Guid.NewGuid().ToString() = 81276701-9dd7-42e9-b128-81c762a172ff
System.Guid.NewGuid().ToString("N") = 489ecfc61ee7403988efe8546806c6a2
System.Guid.NewGuid().ToString("D") = 119201d9-84d9-4126-b93f-be6576eedbfd
System.Guid.NewGuid().ToString("B") = {fd508d4b-cbaf-4f1c-894c-810169b1d20c}
System.Guid.NewGuid().ToString("P") = (eee1fe00-7e63-4632-a290-516bfc457f42)

تمام اين‌ها خيلي هم خوب! اما همان سناريوي مشخص ساختن يك فايل با GUID و يا بازيابي كلمه عبور فراموش شده را درنظر بگيريد. يكي از اصول امنيتي مهم، تعيين اعتبار ورودي كاربر است. چگونه بايد يك GUID را به صورت مؤثري تعيين اعتبار كرد و مطمئن شد كه كاربر از اين راه قصد تزريق اس كيوال را ندارد؟
دو روش براي انجام اينكار وجود دارد
الف) عبارت دريافت شده را به new Guid پاس كنيم. اگر ورودي غيرمعتبر باشد، يك exception توليد خواهد شد.
ب) استفاده از regular expressions جهت بررسي الگوي عبارت وارد شده

پياده سازي اين دو را در كلاس زير مي‌توان ملاحظه نمود:
using System;
using System.Text.RegularExpressions;

namespace sample
{
/// <summary>
/// بررسي اعتبار يك گوئيد
/// </summary>
public static class CValidGUID
{
/// <summary>
/// بررسي تعيين اعتبار ورودي
/// </summary>
/// <param name="guidString">ورودي</param>
/// <returns></returns>
public static bool IsGuid(this string guidString)
{
if (string.IsNullOrEmpty(guidString)) return false;

bool bResult;
try
{
Guid g = new Guid(guidString);
bResult = true;
}
catch
{
bResult = false;
}

return bResult;
}

/// <summary>
/// بررسي تعيين اعتبار ورودي
/// </summary>
/// <param name="input">ورودي</param>
/// <returns></returns>
public static bool IsValidGUID(this string input)
{
return !string.IsNullOrEmpty(input) &&
new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$").IsMatch(input);
}
}

}

سؤال: آيا متدهاي فوق ( extension methods ) درست كار مي‌كنند و واقعا نياز ما را برآورده خواهند ساخت؟ به همين منظور، آزمايش واحد آن‌ها را نيز تهيه خواهيم كرد:

using NUnit.Framework;
using sample;

namespace TestLibrary
{
[TestFixture]
public class TestCValidGUID
{

/*******************************************************************************/
[Test]
public void TestIsGuid1()
{
Assert.IsTrue("81276701-9dd7-42e9-b128-81c762a172ff".IsGuid());
}

[Test]
public void TestIsGuid2()
{
Assert.IsTrue("489ecfc61ee7403988efe8546806c6a2".IsGuid());
}

[Test]
public void TestIsGuid3()
{
Assert.IsTrue("{fd508d4b-cbaf-4f1c-894c-810169b1d20c}".IsGuid());
}

[Test]
public void TestIsGuid4()
{
Assert.IsTrue("(eee1fe00-7e63-4632-a290-516bfc457f42)".IsGuid());
}

[Test]
public void TestIsGuid5()
{
Assert.IsFalse("81276701;9dd7;42e9-b128-81c762a172ff".IsGuid());
}


/*******************************************************************************/
[Test]
public void TestIsValidGUID1()
{
Assert.IsTrue("81276701-9dd7-42e9-b128-81c762a172ff".IsValidGUID());
}

[Test]
public void TestIsValidGUID2()
{
Assert.IsTrue("489ecfc61ee7403988efe8546806c6a2".IsValidGUID());
}

[Test]
public void TestIsValidGUID3()
{
Assert.IsTrue("{fd508d4b-cbaf-4f1c-894c-810169b1d20c}".IsValidGUID());
}

[Test]
public void TestIsValidGUID4()
{
Assert.IsTrue("(eee1fe00-7e63-4632-a290-516bfc457f42)".IsValidGUID());
}

[Test]
public void TestIsValidGUID5()
{
Assert.IsFalse("81276701;9dd7;42e9-b128-81c762a172ff".IsValidGUID());
}
}

}

نتيجه اين آزمايش به صورت زير است:



همانطور كه ملاحظه مي‌كنيد حالت دوم يعني استفاده از عبارات باقاعده دو حالت را نمي‌تواند بررسي كند (مطابق الگوي بكار گرفته شده كه البته قابل اصلاح است)، اما روش معمولي استفاده از new Guid ، تمام فرمت‌هاي توليد شده توسط دات نت را پوشش مي‌دهد.