یک عدد جونیور علاقه مند به حوزه امنیت :)
رمزنگاری کاربردی - قسمت سوم : Message authentication codes
در فصل سوم از کتاب اقای دیوید وونگ به بحث Message authentication codes میرسیم ، حتما قبل ادامه این مقاله رو از سر مقدماتی بخونید و قسمت قبل که مربوط به Hash بوده رو هم بخونید
به صورت ساده اگر شما همون hash رو با یه کدی ترکیب کنید به message authentication code (MAC) میرسید
🔸3.1 Stateless cookies, a motivating example for MACs
کتاب درمورد بحث cookie صحبت میکنه، صفحات وب قبل امدن cookie ها وضعیتشون stateless بوده و شما اگر صفحه ای رو عوض میکردید یا رفرش میکردید دیگه شمارو نمیشناخت، cookie امده و این مسئله رو حل کرد، امدن قابلیت cookie رو اضافه کردن به مرورگر و از اون به بعد شما هروقت هر صفحه وبی رو مشاهده میکردید، هرچقد رفرش میکردید یا میرفتید صفحات دیگه، مرورگر با هربار درخواست شما این cookie رو هم به سایت میفرستاد (درواقع سایت یه شناسه به مرورگر میداد که میگفت اگر تو هر بار توی درخواستت اینو به من بدی من میفهمم که تویی و این یعنی cookie)
حالا امدن گفتن ما که یوزر پس رو cleartext ذخیره نمیکنیم توی مرورگر، بجاش وب سرور میاد یه string رندوم میسازه و میزاره توی cookie و میده به مرورگر (که بهش میگن session cookie) و اگر این string فاش بشه، عملا اطلاعاتی از یوزر لو نمیره ! (هرچند میشه باهاش impersonate کرد) و اگر وب سرور اون رشته رو حذف کنه ،دیگه اون session هیچ رسمیتی نداره !
این سناریو تا جایی جواب میده که ما یک سرور داشته باشیم و اگر تعداد سرور هامون زیاد بشه، مشکل داریم! باید روی همشون این random string رو ذخیره کنیم و خب مسئله میشه ، اگر ما بیاییم یوزر نیم رو بزاریم توی cookie و این cookie لو بره ، احتمال impersonate کردن بجای هر یوزر دلخواه وجود داره
بجاش میتونیم بیاییم از یوزرنیم هش بگیریم طبق شکل 3.1 (سمت چپی)، بنظرتون این روش چه مشکلی داره؟ به هش ها میشه حمله کرد (pre compute/recompute) و میشه راحت فهمید چه یوزری بوده ! از طرف دیگه یه مهاجم راحت میتونه بیاد از هر یوزری ک دلش میخواد hash بگیره و اونو بزاره توی ریکوئست طبق شکل 3.2 (شکل راستی)
راه بدی نیست ولی راه بهتریم هست، یه primitive دیگه داریم به اسم MAC که عین hash عمل میکنه، میاد پیام رو میگیره و یه کلیدم میگیره و یه خروجی بهمون میده ، نقطه قوت این MAC ما توی Key یا کلیدیه ک میگیره و این تا وقتی که فاش نشه، امنیت ما برقراره
به خروجی ایم که تولید میکنه میگن authentication tag و به طور قطع شما اگر اون کلید و پیام رو هزاران بار بهش بدی بازم مثل hash میاد و یه authentication tag بهت میده !
برای اینکه مطمئن شیم یه کاربر نمیتونه cookie رو تغییر بده (tamper یعنی تغییر دادن) ، میاییم بجای hash از MAC استفاده میکنیم، یعنی بار اولی که یوزر لاگین میکنه ما میاییم key و username رو باهم میدیم به MAC و از authentication tag خروجی میگیریم و توی cookie میزاریم و چون مهاجم یا حتی خود کاربر نمیدونه key چی هست، نمیتونه کوکی دلخواه تولید کنه و یا forge کنه
درواقع MAC مثل یه Private Hash یا الگریتم خصوصی هش میمونه، چرا؟ چون فقط شما key رو دارید !
یه جورایی شما اگر با همون الگریتم هش بیایید و یه متن رو با یه key یا متن خاصی هش بگیرید، عین همین MAC میشه دیگه :)
🔸3.2 An example in code
ما دونفر بخوان به هم پیام بدن و فقط نگران integrity پیام باشن، باید هم از hash استفاده کنن هم از MAC، یعنی شما و مخاطبتون از یه کلید (key) به همراه MAC استفاده کنید؛ درواقع یه ورژن از MAC هست به اسم hash-based message authentication code (HMAC) که انی کارو میکنه :
این کد به زبان RUST نوشته شده و همونطور که میبینید توی قسمتای اولیه یه کلید میگیره و authentication tag برمیگردونه
تو کد پایین وقتی طرف یام رو دریافت میکنه ، authentication tag هم داره و میتونه پیام خودشو با اون key میتونه رمز کنه و یه authentication tag تولید کنه و بفرسته برای اون طرف
ما اینجا replay attack داریم که جلوتر بهش میرسیم، فقط خواستم بگم که همچین عالی هم نیست این روش
🔸3.3.1 Forgery of authentication tag
برای اینکه MAC ما امن باشه باید authentication tag forgery نداشته باشیم، یعنی نشه یه پیام جدید ساخت که authentication tag اش با پیام های دیگه یکی باشه (مثل هش که نباید دو ورودی مختلف منجر به یه digest بشه)
فرمول MAC به صورت ساده اینه : t = MAC(k, m) که t میشه همون authentication tag خروجی، تابع MAC که یه سری کار ها میکنه که مثل الگریتم های هش نامشخصه، و یه k داریم که key هست و m که message ماست؛ پس بدون دونستن k نباید کسی بتونه t رو تولید بکنه، حتی اگر m رو داشته باشه !
یعنی الگریتم های MAC باید انقد مقاوم باشن که اگر شما امدی بهش یه لیست message ورودی دادی ولی key رو ثابت نگه داشتی، اگر authentication tag خروجی رو بررسی کردی، نفهمی key چی بوده !
🔸 پس تا وقتی که key مخفی باشه، هیچ مهاجمی نمیتونه کاری بکنه، و از طرفی key ما هم باید به اندازه کافی random بشه (فصل 8 بهش میرسیم) هم به اندازه کافی بلند باشه ( معمولا 16 بایت)
🔸 و حواستون باشه که MAC هم مثل Hash نیاز به serialize شدن داره و اگر اون رو رعایت نکنید ، حملات جعل یا forgery راحت تر اتفاق می افته !
🔸3.3.2 Lengths of authentication tag
یه حمله دیگه که به MAC میکنن ، مثل Hash ، بحث collision هست، برای Hash اگر ما میتونستیم دوتا ورودی متفاوت پیدا کنیم که digest شون یکی بود ما به collision میخوردیم، برای MAC هم همینطوره و اگر ما بتونیم دوتا message پیدا کنیم ، و با کلید یکسان اونارو توی MAC بفرستیم و یک authentication tag در خروجی ببینیم، پس به collision خوردیم.
اگر طول خروجی الگریتم ها کوچیک باشه، حمله تاریخ تولد با شانس بالایی قابل رخ دادن هستا! یعنی اگر خروجی MAC ای که تولید میکنیم 64 بیتی باشه ، به احتمال دو به توان 32 میتونیم توش collision پیدا کنیم، هرچند در عمل زیاد قابلیت سوء استفاده نداره همچین collision ای ولی برای خیلی ها شاید collision resistence مهم باشه، پس باید authentication tag 128 بیت استفاده کنیم که این حملات رخ ندن
از طرفی حملات pre compute/recompute یا حملات offline روی MAC تاثیر گذار نیست، چرا؟ چون ما یه key داریم که منحصر به فرده و باید طرف این key رو هم دخیل کنه توی محاسبه تک تک اون authentication tag هایی که تولید میکنه (اگر از سری مقدماتی یادتون باشه ما برای ذخیره پسورد میگفتیم استفاده از salt و pepper باعث میشه طرف نتونه حمله کنه به هش ما، اینم همونه) پس اگر ما طولو 128 بیت انتخاب کنیم، مهاجم باید به احتمال 2 به توان 64 تا درخواست بزنه، اونم در حالتی که online query میزنه ( چون key رو نداره و هربار باید از ما بپرسه)؛ و احتمال موفقیت این حمله 50% هست، پس اگر میخواید مهاجم شکست بخوره همون 256 رو انتخاب کنید.
🔸3.3.3 Replay attacks
فرض کنید دونفر میخوان باهم پیام ردوبدل بکنن، و برای جلوگیری از tamper میان و از MAC استفاده میکنن، و فرض کنید اونا از دوتا کلید متفاون استفاده میکنن، هرکی کلید خودشو داره، و توی این سناریو اگر یه مهاجم بیاد، به راحتی میتونه پیام هر کدوم رو Replay کنه (حملات replay به صورت کلی یعنی باز ارسال، یعنی همون چیزی که یه نفر ارسال کرده رو شما بتونید مجدد ارسال کنی، حالا به چه دردی میخوره؟ شما اگر دسترسی فرستنده اصلی رو قطع کنی و خودت پیام رو بفرستی کار تمومه دیگه :) )
یکی از راه هایی که میشه جلوی این رو گرفت استفاده از یه عددیه (counter) که افزایش پیدا کنه، و توی مکالمه بعدی زیاد تر بشه :
توی عمل این عدد با طول ثابت 64 بیت توصیه میشه و به هر فرد این امکان رو میده که 2به توان 64 تا پیام بتونه ارسال کنه، ولی خب بعد این رقم، دیگه این عدد ما تکراری میشه :)))
راهکار اینه ک بجای استفاده از counter بیاییم و توافق کنیم که بعد چندین مکالمه و پیام، key رو عوض کنیم
نکته : عدد های ما (counter) نمیتونن مقادیر مختلف رو هندل کنن (variable length نیستن) و ممکنه دسخوش حمله بشن
🔸3.3.4 Verifying authentication tags in constant time
🔸حملات timing attack :
اقای وونگ میگه که در طی بررسی برنامه هایی ک از مکانیزم authentication tag استفاده میکردن یه اسیب پذیری جالبی پیدا کرده، وقتی شما میخوای حمله بکنی و تست کنی که ایا یک authentication tag درست هست یانه ، میایی به روش online از سیستم میپرسی، مثلا میگی اقا این authentication tag رو ببین، درسته؟ و سیستم یه زمان صرف میکنه که بگه درست هست یا نه، حالا اصول اینطوریه که هر بار شما این authentication tag رو میفرستی به سمت مقصد، چه کامل اشتباه باشه، چه کامل درست، چه نصفش درست، باید یک زمان معین طول بکشه که جواب به شما برگرده، به این زمان معین میگن constant time، حالا اگر شما دیدی وقتی ده بار authentication tag فرستادی و 1 میلی ثانیه طول کشید، ولی یکبار که فرستادی 2 میلی ثانیه طول کشید، اون موقع میفهمی که شاید یه قسمتش درست بوده، شاید یه فرقی داشته
این حملات اسمشون timing attack یا حملات زمانی هستن و به این صورت کار میکنن که سیستم در صورت دیدن بایت صحیح، زمان بیشتری طول میکشه که جواب بده و اگر بایت ها اشتباه باشه سریع جواب میده !
این مشکل نباید پیش بیاد و باید سیستم هم به authentication tag کاملا صحیح 1 میلی ثانیه جواب بده، هم به اشتباهش 1 میلی ثانیه هم به نصف صحیحش 1 میلی ثانیه ! اصلا نباید فرقی بکنه براش؛ توی شکل پایین این مسئله به صورت ساده بیان شده:
🔸3.4 MAC in the real world
دقت کنید که اکثر حملات و یا نکاتی که توی کتاب بررسی میکنه، کلی هستند و فقط مختص یه primitive خاص نیستن ! یعنی این timing attack توی الگریتم های رمزنگاری متقارن و نامتقارنم هست، توی هش هم هست و...
کاربرد های MAC در دنیای واقعی :
1- Message authentication :
از MAC توی خیلی از جاها استفاده میشه که مطمئن بشیم که پیام های ردوبدل شده بین دو کاربر یا کامپیوتر تغییر پیدا نمیکنه (tamper نمیشه) و توی فصل بعدی یعنی فصل 4 به این مسئله میپردازیم
2- Deriving keys :
یکی از کاربرد های MAC مفید بدونشون برای تولید random strings یا عبارت های رندوم هست، ما با استفاده از همین عبارات رندوم تعداد بسیاری کلید تولید میکنیم و در رمزنگاری بهره میبریم (توی فصل 8 بهش میرسیم)، ما جلوتر به HMAC-based key derivation function (HKDF) میرسیم که دقیقا همین کارو میکنه
یکی از اهداف (و شاید آرزو) ما در رمزنگاری اینه ک بتونیم یه string رندوم تولید کنیم، در تئوری میشه ولی در دنیای واقعی واقعا این ها random نیستن؛ اگر ما میتونستیم این کارو بکنیم برای MAC کارمون خیلی راحت بود ولی خب وقتی نمیتونیم باید بیاییم یه چیزی خیلی نزدیک بهش تولید کنیم، پس میاییم pseudorandom functions (PRFs) یا توابع شبه رندوم تولید میکنیم، این توابع هم یه جورایی random حساب میشن، درواقع شما با هر تغییر key ای که بدی یه خروجی متفاوت (خیلی متفاوت)دریافت میکنی و این اون هدفیه که ما دوست داشتیم بهش برسیم و تا حدیم میرسیم؛ HMAC و انواع MAC ها درواقع شبه رندوم هستند و با انتخاب کلیدی که انجام میدید نتیجه رندوم میگیرید
3- Integrity of cookies :
یادتونه بحث stateless cookie که در اول مقاله گفتم؟ برای اینکه بتونیم یوزر رو ردیابی کنیم ، بهترین گزینه اینه که یه random string بهشون اختصاص بدیم (از metadata خودشون مثلا) و در کنارش authentication tag اون متادیتا رو هم بزاریم که نتونن تغییرش بدن !
4- Hash tables :
ما توی زبان برنامه نویسی یه مفهوم داریم به اسم hash table (این لینکو بخونید) که توی زبان های دیگه به اسم های hashmaps, dictionaries, associated arrays و.. ازش استفاده میشه
مشکل اینجاست که توی این ها مهاجم میتونه ورودی رو طوری بسازه که چندین کلید اگر هش بشن به یک اندیس یا ایندکس(index) توی اون hash table میرسن
و با این کارش بجای اینکه بزاره که زبان برنامه نویسی چندین ایتم رو در چندین جای مختلف که مال خود اون ایتم هاست قرار بده، همروی توی یک جا قرار میده و این منجر به حمله منع سرویس (denial-of-service (DoS)) میشه
آمدن از SipHash استفاده کردن که مختص همین hash table keys هاست (ورودی کوچکی میگیره)، توی این SipHash ما از یه random key استفاده میشه که برای مهاجم کارو سخت میکنه که بتونه خروجی رو حدس بزنه پس در نهایت احتمال حملرو خیلی کاهش میده
این مشکل توی خیلی از زبان های برنامه نویسی از جمله Python-Rust-Ruby و... اتفاق می افتاده (طبیعتا الان دیگه نیست به لطف SipHash که یک نوع MAC بهینه و کوچیک شدس) و این مشکل توی جاهای بزرگی هم رخ داده، مثل هسته سیستم عامل لینوکس
5- TOTP :
چون MAC قابلیت تولید مقادیر random خوبی داره، ازش توی TOTOP یا time-based one-time password استفاده میشه که توی فصل 11 بهش پرداخته میشه
قبل از شروع با این علائم آشنا بشید :
علامت ⊕ که یعنی XOR
علامت || که یعنی concatenate یا بهم چسبوندن، در امتداد هم قرار دادن
وقتی میگیم 1 || 2 یعنی 2 رو بزار پشت سر 1 که میشه 12
(علامت || رو با or اشتباه نگیرید ، اون توی برنامه نویسیه)
🔸3.5.1 HMAC, a hash-based MAC
کلا MAC دو نوع داره : HMAC و KMAC
نوع اولش که از MAC در کنار hash استفاده میشه ، که بهش میگن hash-based MAC که در سری مقدماتی به صورت خلاصه بررسیش کردیم
استفاده از hash برای ساخت MAC خیلی خوبه چون hash هم سریعه هم راحت پیاده سازی میشه تو کد و سخت افزار ها راحت اجراش میکنن، حالا چطوری این کارو میکنن ؟
قدم اول میان از یه کلید اصلی (main key) دوتا کلید میسازن :
k1 = k ⊕ ipad و k2 = k ⊕ opad
و k همون کلید اصلی ماست ، k1 و k2 دوتا کلید خروجی ما هستند و ipad یه بلوک دادست که با مقدار 0x36 هگز پر میشه، opad هم به همین ترتیب ولی ما مقدار 0x5C هگز
قدم دوم پیام ما به کلید اول میچسبه (CONCATENATE یا || میشه) و ازش هش گرفته میشه
در قدم سوم این خروجی خودش به عنوان message با کلید دوم concatenate ldai و ازشون هش گرفته میشه
و authentication tag خروجی ما تولید میشه
🔸نکته: HMAC سایزش منعطفه ، یعنی سایز authentication tag توسط هشی که استفاده میشه تعیین میشه (یعنی شما اگر HMAC-SHA256 انتخاب کنی، سایز authentication tag ات میشه 256 بیت، اگر HMAC-SHA512 انتخاب کنی سایز میشه 512 بیت و به همین ترتیب)
🔸نکته : truncate کردن رو انجام ندید، اقای وونگ میگه خیلیا سایز رو تا 128 میارن پایین که بد نیست ولی خیلیا میارنش تا 64 بیت که خیلی خطریه (ایشون میگه توجیه نکنید که ما فضای کمی توی کد داریم، اینطوری امنیت به خطر میفته!)
🔸نکته : امنیت HMAC کاملا وابسته به امنیت الگریتم هشینگی هست که استفاده میکنید
🔸3.5.2 KMAC, a MAC based on cSHAKE
قبل ادامه حتما یه بار SHAKE-CSHAKE رو از مقاله قبل مرور کنید
ما یه نوع دیگه از MAC داریم به نام KMAC که از cSHAKE استفاده میکنه، درواقع KMAC میاد اون MAC key رو با ورودی یا همون پیام و خروجی درخواستی میزاره توی الگریتم cSHAKE و بهمون خروجی اون هشی که میخوایم رو میده
🔸نکته : تازه یه string دلخواه دیگه هم میتونه ازتون بگیره که در صورت استفاده به امنیت خیلی کمک میکنه
🔸نکته : این KMAC همون cSHAKE عه، فرق خاصی نکرده :)
🔸 3.6 SHA-2 and length-extension attacks
من طبق کتاب اقای وونگ باید اینو توضیح بدم اینجا ولی توی سری حملات مجدد ما توضیح و اجراشو داریم، ولی این یه تیکه رو از دست ندید :
قبل تشریح حمله Hash Length Extention Attack بزارید این سناریو رو بررسی کنیم:
فرض کنید یه وب سرور هست که میاد از مقدار یوزر نیم هش sha256 میگیره و به عنوان cookie برای شما میفرسته، شما اگر از این امر مطلع باشی میتونی یا بیایی یوزر نیم رو عوض کنی و ازش هش بگیری و اون کوکی رو بزاری توی درخواست،یا در مثال فوق بیاییم یه پارامتر دیگه هم اضافه کنیم به جلوی یوزرنیم که سطح دسترسی مارو میتونه ادمین کنه ! و بعد هش بگیریم !
حالا فرض کنید ما میاییم یه کلیدی هم برای وب سرور در نظر میگیریم که طرف نمیدونه و با این روش hash میگیریم (درواقع HMAC)
اگر بخواد این حملرو انجام بده، چون key یا کلید رو نداره نمیتونه :)
🔸نکته : اینکه بدونید الگریتمی ک طرف باهاش هش میگیره چیه، بد نیست ولی خوبم نیست، درواقع هرچی سخت تر بفهمید چیه بهتره، در عمل هم، بعضی مواقع نمیشه حتی فهمید که الگریتم چیه ولی خیلی از مواقع یا این داکیومنت شدس یا با ابزار میشه تشخیص داد
فرض کنید که input1 ما یه استرینگه : user=bob و اگر ما digest رو بخوایم بشکافیم، فقط هش user=bob هست و یه مقدار padding
(الان اینجا اینو درک کنید که وقتی user=bob رو میدیم به الگریتم هشینگ، فقط همین user=bob رو برمیگردونه و padding اش چیز خاصی نیست)
حالا
یک مهاجم میتونه بیاد یه مقدار دیگه، مثلا &admin=true رو به انتهای digest قبلی اضافه کنه و هش کاملا درست کار میکنه !!!
علمی ترش اینطوریه که ما اینبار input1 || padding || input2 داریم، یعنی انگار الگریتم هش ما هنوز کارش تموم نشده ، و ما یه مقدار دیگه اضافه میکنیم و هش میگیره ، میتونیم مقدار سومم اضافه کنیم و هش بگیره :)
من مختصر به زبان خودم توضیحش بدم : وقتی شما ورودی اول یا user=bob رو میدید به الگریتم هش و یه digest میده، این digest دقیقااااا همون user=bob هست، فرض کنید تو مثال وب سرور هستیم و اینم شما میدونید که اگر بعد user=bob یه عبارت &admin=true اضافه کنید، راحت ادمین میشید ! حالا چیکار میکنید؟ میایید digest رو میدید به الگریتم هش و در ادامش &admin=true رو اضافه میکنید، یعنی میشه :
digest&admin=true
که اینجا digest ما چون میشد user=bob
حالا دومین digest ما دقیقا این میشه : user=bob&admin=true
توضیح مجدد : وب سرور میگه تو user=bob هستی و اینم hash ات (دقت کنید که با الگریتم sha2 داره میگیره و از کلید هم استفاده میکنه)
حالا ما که میخوایم جعلش کنیم، میاییم و از این مقدار هش میگیریم :
عبارتی که خودش برای ما داده به عنوان cookie درواقع یه digest هست از مقدار user=bob و key ، ما میاییم یه padding میزنیم پشتش و &admin=true مد نظرمونو میزاریم پشتش و یه هش میگیریم !
🔸نکته : توی این حمله ما از مقدار digest اول شاید اگاه نباشیم، ولی میتونیم ازش سوء استفاده کنیم
🔸نکته : padding قسمت اول میتونه توی بعضی از پروتوکل ها تا حدی امنیت رو به ارمغان بیاره ولی خب یکم باهاش بازی کنیم میتونیم بایپسش کنیم
🔸نکته : حتما دقت کنید که با SHA2 از دوتا کلید استفاده کنید (یه کلیدش در سناریو بالا نقض شد) ، که دقیقا همون چیزیه که HMAC بهمون میده ، ولی اگر خواستید خیلی امن تر باشه برید سراغ KMAC که از SHA3 استفاده میکنه
فصل سوم تموم شد، فصل بعدی سراغ مبحث Authenticated encryption میریم
امید وارم براتون مفید بوده باشه
منتظر سوالات، انتقاد و پیشنهاداتتون هستم
یاعلی ;)
مطلبی دیگر از این انتشارات
رمزنگاری کاربردی - قسمت اول : مقدمات
مطلبی دیگر از این انتشارات
رمزنگاری کاربردی - قسمت دوم : Hash Function
مطلبی دیگر از این انتشارات
رمزنگاری کاربردی - قسمت پنجم : Key exchanges