paint-brush
প্রমাণীকরণের জন্য কখনই UUID-এর উপর নির্ভর করবেন না: প্রজন্মের দুর্বলতা এবং সর্বোত্তম অনুশীলনদ্বারা@mochalov
2,088 পড়া
2,088 পড়া

প্রমাণীকরণের জন্য কখনই UUID-এর উপর নির্ভর করবেন না: প্রজন্মের দুর্বলতা এবং সর্বোত্তম অনুশীলন

দ্বারা Ivan Mochalov8m2024/05/01
Read on Terminal Reader

অতিদীর্ঘ; পড়তে

প্রমাণীকরণ, দুর্বলতা উন্মোচন এবং নিরাপদ বাস্তবায়ন কৌশলগুলির জন্য UUID ব্যবহার করার ঝুঁকি এবং সর্বোত্তম অনুশীলন।
featured image - প্রমাণীকরণের জন্য কখনই UUID-এর উপর নির্ভর করবেন না: প্রজন্মের দুর্বলতা এবং সর্বোত্তম অনুশীলন
Ivan Mochalov HackerNoon profile picture
0-item

প্রমাণীকরণের জন্য UUID

আজকাল খুব কমই এমন একজন ব্যক্তি আছেন যিনি গভীর হতাশার মধ্যে "পাসওয়ার্ড পুনরুদ্ধার করুন" বোতামটি ক্লিক করেননি। এমনকি যদি মনে হয় যে পাসওয়ার্ডটি নিঃসন্দেহে সঠিক ছিল, তবে এটি পুনরুদ্ধার করার পরবর্তী পদক্ষেপটি বেশিরভাগই একটি ইমেল থেকে একটি লিঙ্কে গিয়ে নতুন পাসওয়ার্ড প্রবেশ করার মাধ্যমে সহজে যায় (আসুন কাউকে বোকা বানাবেন না; আপনি এটি টাইপ করেছেন বলে এটি খুব কমই নতুন। আপত্তিকর বোতাম টিপানোর আগে ধাপ 1 এ ইতিমধ্যে তিনবার)।


ইমেল লিঙ্কগুলির পিছনে যুক্তি, তবে, এটির প্রজন্মকে অনিরাপদ রেখে ব্যবহারকারীর অ্যাকাউন্টগুলিতে অননুমোদিত অ্যাক্সেস সম্পর্কিত দুর্বলতার বন্যা উন্মোচন করার বিষয়ে খুব ভালভাবে যাচাই করার মতো বিষয়। দুর্ভাগ্যবশত, এখানে একটি UUID-ভিত্তিক পুনরুদ্ধার URL কাঠামোর একটি উদাহরণ রয়েছে যা সম্ভবত অনেকের সম্মুখীন হয়েছে, যা তবুও নিরাপত্তা নির্দেশিকা অনুসরণ করে না:


 https://.../recover/d17ff6da-f5bf-11ee-9ce2-35a784c01695


যদি এই ধরনের একটি লিঙ্ক ব্যবহার করা হয়, তাহলে এর সাধারণ অর্থ হল যে কেউ আপনার পাসওয়ার্ড পেতে পারে এবং এটি তত সহজ। এই নিবন্ধটির লক্ষ্য হল UUID প্রজন্মের পদ্ধতির গভীরে প্রবেশ করা এবং তাদের প্রয়োগের জন্য অনিরাপদ পদ্ধতি নির্বাচন করা।

UUID কি

UUID হল একটি 128-বিট লেবেল যা সাধারণত দুটি মূল্যবান বৈশিষ্ট্য সহ ছদ্ম-র্যান্ডম শনাক্তকারী তৈরিতে ব্যবহৃত হয়: এটি যথেষ্ট জটিল এবং যথেষ্ট অনন্য। বেশিরভাগ ক্ষেত্রে, এগুলি হল আইডির জন্য মূল প্রয়োজনীয়তা যা ব্যাকএন্ড ছেড়ে দেওয়া এবং ব্যবহারকারীকে স্পষ্টভাবে ফ্রন্টএন্ডে দেখানো হয় বা সাধারণত পর্যবেক্ষণ করার ক্ষমতা সহ API এর মাধ্যমে পাঠানো হয়। এটি আইডি = 123 (জটিলতা) এর তুলনায় অনুমান করা বা পাশবিক শক্তিকে কঠিন করে তোলে এবং যখন তৈরি করা আইডি পূর্বে ব্যবহার করা হয়েছে, যেমন, 0 থেকে 1000 (অনন্যতা) এর মধ্যে একটি এলোমেলো সংখ্যার সাথে ডুপ্লিকেট করা হলে সংঘর্ষ প্রতিরোধ করে।


"যথেষ্ট" অংশগুলি আসলে আসে, প্রথমত, ইউনিভার্সলি ইউনিক আইডেন্টিফায়ারের কিছু সংস্করণ, এটিকে নকলের জন্য ছোটখাটো সম্ভাবনার জন্য উন্মুক্ত করে দেয়, যা অবশ্য অতিরিক্ত তুলনামূলক যুক্তি দ্বারা সহজে প্রশমিত হয় এবং খুব কমই নিয়ন্ত্রিত অবস্থার কারণে হুমকির কারণ হয় না। এর ঘটনা। এবং দ্বিতীয়ত, বিভিন্ন UUID সংস্করণের জটিলতা সম্পর্কে নিবন্ধে বর্ণনা করা হয়েছে, সাধারণভাবে আরও কোণার ক্ষেত্রে ছাড়া এটি বেশ ভাল বলে ধরে নেওয়া হয়।

ব্যাকএন্ডে বাস্তবায়ন

ডাটাবেস টেবিলের প্রাথমিক কীগুলি UUID-এর মতো জটিল এবং অনন্য হওয়ার একই নীতির উপর নির্ভর করে। অনেক প্রোগ্রামিং ল্যাঙ্গুয়েজ এবং ডাটাবেস ম্যানেজমেন্ট সিস্টেমে এর প্রজন্মের জন্য বিল্ট-ইন পদ্ধতির ব্যাপক গ্রহণের সাথে, UUID প্রায়শই সংরক্ষিত ডেটা এন্ট্রি সনাক্ত করতে এবং সাধারণভাবে টেবিলে যোগদানের ক্ষেত্র হিসাবে প্রথম পছন্দ হিসাবে আসে এবং সাবটেবলগুলি স্বাভাবিককরণের মাধ্যমে বিভক্ত হয়। নির্দিষ্ট ক্রিয়াকলাপের প্রতিক্রিয়া হিসাবে API-এর মাধ্যমে ডেটাবেস থেকে আসা ব্যবহারকারী আইডিগুলি প্রেরণ করাও সাধারণ অভ্যাস যা অতিরিক্ত অস্থায়ী আইডি তৈরি ছাড়াই ডেটা প্রবাহকে একীভূত করার প্রক্রিয়া তৈরি করে এবং সেগুলিকে প্রোডাকশন ডেটা স্টোরেজের সাথে লিঙ্ক করে।


পাসওয়ার্ড রিসেট উদাহরণের ক্ষেত্রে, আর্কিটেকচারে সম্ভবত এমন একটি ক্রিয়াকলাপের জন্য দায়ী একটি টেবিল অন্তর্ভুক্ত থাকে যা ব্যবহারকারীর বোতামে ক্লিক করার সময় উত্পন্ন UUID সহ ডেটার সারি সন্নিবেশ করায়। এটি ব্যবহারকারীর সাথে যুক্ত ঠিকানায় তাদের user_id দ্বারা একটি ইমেল পাঠিয়ে এবং রিসেট লিঙ্কটি খোলার পরে কোন ব্যবহারকারীর সনাক্তকারীর উপর ভিত্তি করে পাসওয়ার্ড রিসেট করতে হবে তা পরীক্ষা করে পুনরুদ্ধার প্রক্রিয়া শুরু করে। যাইহোক, ব্যবহারকারীদের কাছে দৃশ্যমান এই ধরনের শনাক্তকারীদের জন্য নিরাপত্তা নির্দেশিকা রয়েছে এবং UUID-এর কিছু বাস্তবায়ন সাফল্যের বিভিন্ন মাত্রার সাথে তাদের পূরণ করে।

পুরানো সংস্করণ

UUID জেনারেশনের ভার্সন 1 তার 128 বিটগুলিকে ডিভাইস তৈরিকারী আইডেন্টিফায়ারের 48-বিট MAC অ্যাড্রেস, একটি 60-বিট টাইমস্ট্যাম্প, 14-বিট মান বৃদ্ধির জন্য সংরক্ষিত, এবং 6 সংস্করণ করার জন্য বিভক্ত করে। এইভাবে অনন্যতার গ্যারান্টি কোড লজিকের নিয়ম থেকে হার্ডওয়্যার নির্মাতাদের কাছে স্থানান্তরিত হয় যারা উৎপাদনে প্রতিটি নতুন মেশিনের জন্য সঠিকভাবে মান নির্ধারণ করে। শুধুমাত্র 60+14 বিটগুলিকে ব্যবহারযোগ্য পরিবর্তনযোগ্য পেলোড উপস্থাপন করার জন্য শনাক্তকারীর অখণ্ডতার অবনতি ঘটে, বিশেষ করে এর পিছনে যেমন স্বচ্ছ যুক্তি রয়েছে। চলুন ফলস্বরূপ উত্পন্ন UUID v1 সংখ্যার একটি ক্রম দেখে নেওয়া যাক:


 from uuid import uuid1 for _ in range(8):    print(uuid1())
 d17ff6da-f5bf-11ee-9ce2-35a784c01695 d17ff6db-f5bf-11ee-9ce2-35a784c01695 d17ff6dc-f5bf-11ee-9ce2-35a784c01695 d17ff6dd-f5bf-11ee-9ce2-35a784c01695 d17ff6de-f5bf-11ee-9ce2-35a784c01695 d17ff6df-f5bf-11ee-9ce2-35a784c01695 d17ff6e0-f5bf-11ee-9ce2-35a784c01695 d17ff6e1-f5bf-11ee-9ce2-35a784c01695



দেখা যায়, "-f5bf-11ee-9ce2-35a784c01695" অংশটি সব সময় একই থাকে। পরিবর্তনযোগ্য অংশটি হল সিকোয়েন্স 3514824410 - 3514824417-এর একটি 16-বিট হেক্সাডেসিমেল উপস্থাপনা। এটি একটি অতিসাধারণ উদাহরণ কারণ উত্পাদন মান সাধারণত সময়ের মধ্যে আরও উল্লেখযোগ্য ফাঁক দিয়ে তৈরি হয়, তাই টাইমস্ট্যাম্প-সম্পর্কিত অংশটিও পরিবর্তিত হয়। 60-বিট টাইমস্ট্যাম্প অংশের মানে হল যে শনাক্তকারীর একটি আরও উল্লেখযোগ্য অংশ আইডিগুলির একটি বড় নমুনার উপর দৃশ্যত পরিবর্তিত হয়৷ মূল পয়েন্টটি একই থাকে: UUIDv1 সহজেই অনুমান করা যায়, যদিও এটি এলোমেলোভাবে দেখায় প্রাথমিকভাবে।


8টি আইডির প্রদত্ত তালিকা থেকে শুধুমাত্র প্রথম এবং শেষ মান নিন। যেহেতু শনাক্তকারীগুলি কঠোরভাবে তৈরি করা হয়, ফলস্বরূপ, এটি স্পষ্ট যে প্রদত্ত দুটির মধ্যে শুধুমাত্র 6টি আইডি তৈরি করা হয়েছে (হেক্সাডেসিমেল পরিবর্তনযোগ্য অংশগুলি বিয়োগ করে), এবং তাদের মানগুলিও নিশ্চিতভাবে পাওয়া যেতে পারে। এই ধরনের যুক্তির এক্সট্রাপোলেশন হল তথাকথিত স্যান্ডউইচ আক্রমণের পিছনে অন্তর্নিহিত অংশ যা UUID-কে এই দুটি সীমানা মান জানা থেকে জোর করে। আক্রমণ প্রবাহ সহজবোধ্য: ব্যবহারকারী লক্ষ্য UUID জেনারেশন হওয়ার আগে UUID A তৈরি করে এবং UUID B ঠিক পরে। একটি স্ট্যাটিক 48-বিট MAC অংশের সাথে একই ডিভাইসটি তিনটি প্রজন্মের জন্য দায়ী বলে ধরে নিলে, এটি একটি ব্যবহারকারীকে A এবং B এর মধ্যে সম্ভাব্য আইডিগুলির একটি ক্রম সহ সেট করে, যেখানে লক্ষ্য UUID অবস্থিত। টার্গেট করার জন্য জেনারেট করা আইডিগুলির মধ্যে সময়ের নৈকট্যের উপর নির্ভর করে, পরিসরটি ব্রুট-ফোর্স পদ্ধতিতে অ্যাক্সেসযোগ্য ভলিউম হতে পারে: খালিগুলির মধ্যে বিদ্যমানগুলি খুঁজে পেতে প্রতিটি সম্ভাব্য UUID পরীক্ষা করুন।


পূর্বে বর্ণিত পাসওয়ার্ড পুনরুদ্ধার এন্ডপয়েন্ট সহ API অনুরোধগুলিতে, এটি বিদ্যমান URL-টি উল্লেখ করে একটি প্রতিক্রিয়া না পাওয়া পর্যন্ত ফলস্বরূপ UUID-এর সাথে শত শত বা হাজার হাজার অনুরোধ পাঠাতে অনুবাদ করে। পাসওয়ার্ড রিসেটের সাথে, এটি একটি সেটআপের দিকে নিয়ে যায় যেখানে ব্যবহারকারী দুটি অ্যাকাউন্টে পুনরুদ্ধার লিঙ্ক তৈরি করতে পারে যা তারা নিয়ন্ত্রণ করে যতটা সম্ভব ঘনিষ্ঠভাবে লক্ষ্য অ্যাকাউন্টে পুনরুদ্ধার বোতাম টিপুন যাতে তাদের অ্যাক্সেস নেই কিন্তু শুধুমাত্র ইমেল/লগইন জানে। পুনরুদ্ধার UUIDs A এবং B সহ নিয়ন্ত্রিত অ্যাকাউন্টগুলিতে চিঠিগুলি তখন পরিচিত হয় এবং লক্ষ্য অ্যাকাউন্টের জন্য পাসওয়ার্ড পুনরুদ্ধার করার লক্ষ্য লিঙ্কটি প্রকৃত রিসেট ইমেল অ্যাক্সেস না করেই জোরপূর্বক করা যেতে পারে।


ব্যবহারকারীর প্রমাণীকরণের জন্য শুধুমাত্র UUIDv1-এর উপর নির্ভর করার ধারণা থেকে দুর্বলতার উৎপত্তি। একটি পুনরুদ্ধার লিঙ্ক পাঠানোর মাধ্যমে যা পাসওয়ার্ড রিসেট করার অ্যাক্সেস মঞ্জুর করে, এইভাবে অনুমান করা হয় যে লিঙ্কটি অনুসরণ করে, একজন ব্যবহারকারীকে সেই হিসাবে প্রমাণীকরণ করা হয়েছে যার লিঙ্কটি পাওয়ার কথা ছিল। এটি সেই অংশ যেখানে UUIDv1 সরাসরি নৃশংস শক্তির সংস্পর্শে আসার কারণে প্রমাণীকরণের নিয়ম ব্যর্থ হয় ঠিক একইভাবে যদি কারও দরজা তাদের প্রতিবেশী দরজার চাবি কেমন দেখতে তা জেনে খোলা যায়।

ক্রিপ্টোগ্রাফিকভাবে অনিরাপদ ফাংশন

UUID-এর প্রথম সংস্করণটি মূলত আংশিকভাবে উত্তরাধিকার হিসাবে বিবেচিত হয় কারণ প্রজন্মের যুক্তি শুধুমাত্র একটি এলোমেলো মান হিসাবে শনাক্তকারী আকারের একটি ছোট অংশ ব্যবহার করে। অন্যান্য সংস্করণ, যেমন v4, সংস্করণ করার জন্য যতটা সম্ভব কম জায়গা রেখে এবং র্যান্ডম পেলোড হতে 122 বিট পর্যন্ত রেখে এই সমস্যাটি সমাধান করার চেষ্টা করুন। সাধারণভাবে, এটি একটি হুপিং 2^122 তে মোট সম্ভাব্য বৈচিত্র নিয়ে আসে, যা আপাতত শনাক্তকারীর স্বতন্ত্রতার প্রয়োজনীয়তা সম্পর্কিত "যথেষ্ট" অংশকে সন্তুষ্ট করে এবং এইভাবে নিরাপত্তা মান পূরণ করে বলে মনে করা হয়। যদি জেনারেশন ইমপ্লিমেন্টেশন কোনোভাবে র‍্যান্ডম অংশের জন্য অবশিষ্ট বিটগুলিকে উল্লেখযোগ্যভাবে হ্রাস করে তাহলে ব্রুট-ফোর্স দুর্বলতার জন্য ওপেনিং দেখা দিতে পারে। কিন্তু কোন উত্পাদন সরঞ্জাম বা লাইব্রেরি ছাড়া, এটা কি হওয়া উচিত?


আসুন একটু ক্রিপ্টোগ্রাফিতে লিপ্ত হই এবং জাভাস্ক্রিপ্টের UUID জেনারেশনের সাধারণ বাস্তবায়নকে ঘনিষ্ঠভাবে দেখে নেওয়া যাক। এখানে randomUUID() ফাংশনটি ছদ্ম-র্যান্ডম সংখ্যা তৈরির জন্য math.random মডিউলের উপর নির্ভর করে:

 Math.floor(Math.random()*0x10);


এবং র্যান্ডম ফাংশন নিজেই, সংক্ষেপে এটি এই নিবন্ধের বিষয়ের জন্য আগ্রহের অংশ মাত্র:

 hi = 36969 * (hi & 0xFFFF) + (hi >> 16); lo = 18273 * (lo & 0xFFFF) + (lo >> 16); return ((hi << 16) + (lo & 0xFFFF)) / Math.pow(2, 32);


ছদ্ম-এলোমেলো প্রজন্মের জন্য র্যান্ডম পর্যাপ্ত সংখ্যার ক্রম তৈরি করার জন্য এটির উপরে গাণিতিক ক্রিয়াকলাপ সম্পাদন করার জন্য একটি ভিত্তি হিসাবে বীজের মান প্রয়োজন। এই ধরনের ফাংশনগুলি শুধুমাত্র এটির উপর ভিত্তি করে তৈরি করা হয়, যার অর্থ হল যদি সেগুলিকে আগের মতো একই বীজ দিয়ে পুনরায় চালু করা হয়, আউটপুট ক্রমটি মিলতে চলেছে। প্রশ্নে জাভাস্ক্রিপ্ট ফাংশনের বীজের মান হাই এবং লো ভেরিয়েবল নিয়ে গঠিত, প্রতিটি একটি 32-বিট স্বাক্ষরবিহীন পূর্ণসংখ্যা (0 থেকে 4294967295 দশমিক)। ক্রিপ্টোগ্রাফিক উদ্দেশ্যে উভয়ের সংমিশ্রণ প্রয়োজন, এটি দুটি প্রাথমিক মানকে তাদের একাধিক জেনে নিশ্চিতভাবে বিপরীত করা অসম্ভবের কাছাকাছি করে তোলে, কারণ এটি বড় সংখ্যার সাথে পূর্ণসংখ্যার ফ্যাক্টরাইজেশনের জটিলতার উপর নির্ভর করে।


দুটি 32-বিট পূর্ণসংখ্যা একসাথে 2^64 সম্ভাব্য ক্ষেত্রে নিয়ে আসে হাই এবং লো ভেরিয়েবল অনুমান করার জন্য প্রাথমিক ফাংশন UUID তৈরি করে। যদি হাই এবং লো মানগুলি কোনোভাবে পরিচিত হয়, তাহলে প্রজন্মের ফাংশনটি নকল করতে এবং বীজের মূল্যের এক্সপোজারের কারণে এটি যে সমস্ত মান তৈরি করে এবং ভবিষ্যতে উৎপন্ন করবে তা জানতে কোন প্রচেষ্টা লাগে না। যাইহোক, নিরাপত্তার মানদণ্ডে 64 বিটগুলিকে বোঝার জন্য একটি পরিমাপযোগ্য সময়ের মধ্যে পাশবিক শক্তির প্রতি অসহিষ্ণু বলে মনে করা যেতে পারে। বরাবরের মতো, সমস্যাটি নির্দিষ্ট বাস্তবায়ন থেকে আসে। Math.random() 32-বিট ফলাফলে hi এবং lo প্রতিটি থেকে বিভিন্ন 16 বিট নেয়; যাইহোক, এর উপরে randomUUID() .floor() অপারেশনের কারণে আবার মান পরিবর্তন করে, এবং হঠাৎ করে একমাত্র অর্থপূর্ণ অংশটি এখন শুধুমাত্র হাই থেকে আসে। এটি কোনোভাবেই প্রজন্মকে প্রভাবিত করে না তবে ক্রিপ্টোগ্রাফি পদ্ধতিগুলিকে বিচ্ছিন্ন করে দেয় কারণ এটি সমগ্র প্রজন্মের ফাংশন বীজের জন্য শুধুমাত্র 2^32 সম্ভাব্য সংমিশ্রণ ছেড়ে দেয় (হাই এবং লো উভয়টিকেই বর্বর-জোর করার প্রয়োজন নেই কারণ লো সেট করা যেতে পারে। মান এবং আউটপুট প্রভাবিত করে না)।


ব্রুট-ফোর্স ফ্লো একটি একক আইডি অর্জন করে এবং সম্ভাব্য উচ্চ মান পরীক্ষা করে যা এটি তৈরি করতে পারে। কিছু অপ্টিমাইজেশান এবং গড় ল্যাপটপ হার্ডওয়্যার সহ, এটি মাত্র কয়েক মিনিট সময় নিতে পারে এবং স্যান্ডউইচ আক্রমণের মতো সার্ভারে প্রচুর অনুরোধ পাঠানোর প্রয়োজন হয় না বরং অফলাইনে সমস্ত ক্রিয়াকলাপ সম্পাদন করে৷ এই ধরনের পদ্ধতির ফলাফল পাসওয়ার্ড পুনরুদ্ধারের উদাহরণে সমস্ত তৈরি এবং ভবিষ্যতের রিসেট লিঙ্কগুলি পেতে ব্যাকএন্ডে ব্যবহৃত প্রজন্ম ফাংশন অবস্থার প্রতিলিপি তৈরি করে। দুর্বলতাকে উদীয়মান থেকে রোধ করার পদক্ষেপগুলি সোজা এবং ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত ফাংশনগুলির ব্যবহারের জন্য চিৎকার করে, যেমন crypto.randomUUID()

Takeaways

UUID একটি দুর্দান্ত ধারণা এবং এটি অনেক অ্যাপ্লিকেশন ক্ষেত্রে ডেটা ইঞ্জিনিয়ারদের জীবনকে অনেক সহজ করে তোলে। যাইহোক, এটি কখনই প্রমাণীকরণের ক্ষেত্রে ব্যবহার করা উচিত নয়, কারণ এই নিবন্ধে, এটির প্রজন্মের কৌশলগুলির কিছু ক্ষেত্রে ত্রুটিগুলি আলোকে আনা হয়েছে৷ এটি স্পষ্টতই সমস্ত UUID-এর নিরাপত্তাহীনতার ধারণাকে অনুবাদ করে না। মৌলিক পদ্ধতি, যদিও, নিরাপত্তার জন্য এগুলিকে ব্যবহার না করার জন্য লোকেদেরকে রাজি করানো, যা এই ধরনের উদ্দেশ্যে ব্যবহার করা বা কীভাবে তৈরি করা যায় না সে বিষয়ে ডকুমেন্টেশনে জটিল সীমা নির্ধারণের চেয়ে বেশি দক্ষ এবং ভাল, নিরাপদ।