احتمالاً برای شما هم پیش آمده که درباره مفاهیمی که به ظاهر شبیه به هم هستند، دچار سردرگمی شوید. در دنیای نرمافزار از این دست موارد کم نیستند و بسیار مهم است که تعاریف درستی از آن ها داشته باشیم تا بر اساس این تعاریف، تصمیم درستی بگیریم. در این پست، تلاش میکنم تا سه مورد از این مفاهیم را به زبان ساده و به همراه کد به شما آموزش دهم.
آیا تا به حال به مکالمه ما با یکدیگر دقت کردهاید؟ تفکر ما توسط دهان به صدا تبدیل میشود. صدا در فاصله بین ما و شنونده انتقال میابد و در آخر، شنونده صدای دریافتی را دوباره به مفهوم تبدیل کرده و درباره آن فکر میکند. این فرآیند Decode و Encode است. درواقع به فرآیند تبدیل اطلاعات به فرمتی که برای انتقال و یا ذخیرهسازی مناسب است، Encoding و به فرآیند دریافت این پیام و تبدیل آن به فرمت اولیه، Decoding گفته میشود. دلیل این تبدیل این است که بتوانیم اطلاعات را راحتتر جابهجا و یا ذخیرهسازی کنیم چرا که فرمت اصلی اطلاعات، برای این اهداف مناسب نیست. از دیگر مثالهای این مورد، میتوان به تبدیل شدن دیتاهای باینری کامپیوتر به سیگنالهای الکتریکی برای جابهجایی در سیمها اشاره کرد.
به قطعه کد زیر توجه کنید:
string text = "abc" byte[] bytes = Encoding.UTF8.GetBytes(text);
متغییر text دارای مقدار متنی است ولی ما آن را با استفاده از متد GetBytes که برای Encoding استفاده میشود به آرایهای از بایتها تبدیل کردیم. مقادیر داخل متغییر bytes به صورت زیر است:
شاید از خودتان سوال کنید که این اعداد بر چه اساسی مشخص شدهاند. چرا باید a به 97 تبدیل شود؟ برای پاسخ به این سوال لازم است نگاهی به جدول پایین بیاندازیم.
این جدول که به آن encoding scheme گفته میشود، بیان میکند که یک کاراکتر معادل چه مقدار بایتی در سیستم کامپیوتری میباشد. این جدول UTF-8 است، به این معنی که از 8 بیت برای نمایش کاراکترها استفاده میکند. با 8 بیت میتوان 256 کاراکتر را نمایش داد. ما همچنین UTF-16 و UTF-32 را هم داریم که تعداد بیشتری کاراکتر را پوشش میدهند. طبق جدول بالا که برای UTF-8 است، کاراکتر a به عدد 97 وصل شده است. این بدان معناست که برای نمایش کاراکتر a باید آن را به صورت نمایش بایتی 97 مشخص کنیم.
a : 97 : 0000000001100001
حالا که متن a را به 0 و 1 ها تبدیل کردیم میتوانیم آن را در سیمها جابهجا کنیم. پس از این جابهجایی، گیرنده اطلاعات باید فرآیند Decode کردن را انجام دهد. لازم به ذکر است که برای این کار، باز هم به جدول encoding scheme نیاز است. اطلاعات باید با همان جدولی که Encode شدهاند Decode شوند در غیر این صورت برداشت غلطی از دیتا خواهد شد. به تصویر زیر دقت کنید:
همانطور که مشاهده میکنید، با استفاده از تابع GetString مقدار بایتی را به متن اولیه تبدیل کردیم. به این فرآیند Decode گفته میشود.
ما گاها نیاز داریم که اطلاعاتی را از نقطهای به نقطهای دیگر جابهجا کنیم اما این بار، مسئله ما نه فقط انتقال بلکه حفظ امنیت و عدم تغییر آن است. ما میخواهیم اطلاعات ما خوانده نشود، تغییر نکند و درست همانطور که ارسال شده، دریافت گردد. برای این منظور، از Encryption و Decryption استفاده میکنیم.
در این فرآیند، دو عنصر اساسی وجود دارد. کلید و الگوریتم. متن اولیه به همراه یک کلید، به الگوریتمی داده میشود و الگوریتم به وسیله آن کلید و محاسبات ریاضی، متن جدیدی به عنوان خروجی تحویل میدهد. طرف دریافت کننده هم با استفاده از یک الگوریتم و کلید، متن تغییر یافته را دریافت و آن را به حالت اولیه برمیگرداند. از آنجایی که کسی در میانه راه کلید و الگوریتم را ندارد، پس نمیتواند متن را خوانده و اطلاعات آن را بدست آورد. Encryption و Decryption به دو دسته تقسیم میگردد. Symmetric encryption و Asymmetric encryption. در Symmetric encryption از یک کلید برای Encryption و Decryption استفاده میشود. فرستنده و گیرنده هردو یک کلید را دارند و به همین دلیل بهتر است که خیلی مراقب این کلید بود چون در صورتی که در دست شخص دیگری باشد، امکان خواندن اطلاعات وجود دارد.
در حالت دوم که Asymmetric encryption نام دارد، از دو کلید استفاده میگردد. یک کلید عمومی و یک کلید شخصی. کلید عمومی در دست همه قرار دارد و از آن برای encryption اطلاعات استفاده میشود. ارسال کننده با کلید عمومیِ شما، اطلاعات را encrypt میکند ولی فقط با استفاده از کلید شخصی شما میتوان اطلاعات را Decrypt کرده و از آن استفاده کرد. درواقع این کلیدها به طریقی با هم ارتباط دارند و برای هم ساخته شدهاند تا بتوانند تکمیل کننده کار دیگری باشند.
در دو مورد قبل، ما دیدیم که دیتای اولیه به حالتی دیگر تبدیل میشد ولی ما این قابلیت را داشتیم که اطلاعات را از حالت تبدیل شده، به حالت اول برگردانیم. درواقع همه موارد قبل، دو طرفه بودند. اما اگر بخواهیم که این فرآیند یک طرفه باشد چه؟ یعنی نخواهیم دیتای تبدیل شده، به حالت اولیه برگردد. در این صورت باید از Hashing استفاده کنیم. ابتدا به توضیح کاملتر Hashing میپردازیم و بعد توضیح میدهیم که در چه مواردی به یک طرفه بودن فرآیند نیاز داریم.
در فرآیند Hashing ما اطلاعات را به وسیله الگوریتم Hash به رشتهای به طول ثابت تبدیل میکنیم. اصلا مهم نیست که اندازه ورودی شما چقدر باشد. خروجی همیشه طول یکسان خواهد داشت. این تبدیل همانطور که گفته شد، یک طرفه است و شما نمیتوانید دیتای اولیه را بازیابی کنید. از Hashing میتوان برای صحتسنجی دیتا استفاده کرد. میتوانیم مقدار Hash یک دیتا را بگیریم و آن را با مقدار Hash قبلی همان دیتا مقایسه کنیم. درصورتی که این دو مقدار باهم متفاوت باشند، یعنی داده ما دچار تغییر شده چرا که Hash یک داده، همیشه یکسان است. مثلا در نرمافزارهای وب، رمز عبور کاربران به صورت Hash شده ذخیره میگردد. این بدان جهت است که هم رمز عبورها قابل مشاهده و بازیابی نباشد و هم این که اصلا نیازی به ذخیره واقعی آنها نیست. به عنوان مثال، وقتی کاربر نام کاربری و رمز عبور خود را در فرم ورود وارد میکند، رمز وارد شده به تابع Hash داده میشود. در صورتی که مقدار این Hash با مقدار موجود در دیتابیس متفاوت باشد، یعنی کاربر رمز عبور خود را به درستی وارد نکرده است چرا که باید Hash های یکسانی تولید میشد.
لینک کانال پادکست من: https://t.me/BookBriefByFard