সার্ভার-সাইড পাসকি প্রমাণীকরণ

ওভারভিউ

এখানে পাসকি প্রমাণীকরণের সাথে জড়িত মূল পদক্ষেপগুলির একটি উচ্চ-স্তরের ওভারভিউ রয়েছে:

পাসকি প্রমাণীকরণ প্রবাহ

  • একটি পাসকি দিয়ে প্রমাণীকরণের জন্য প্রয়োজনীয় চ্যালেঞ্জ এবং অন্যান্য বিকল্পগুলি সংজ্ঞায়িত করুন। সেগুলি ক্লায়েন্টের কাছে পাঠান, যাতে আপনি সেগুলিকে আপনার পাসকি প্রমাণীকরণ কলে পাঠাতে পারেন ( ওয়েবে navigator.credentials.get )৷ ব্যবহারকারী পাসকি প্রমাণীকরণ নিশ্চিত করার পরে, পাসকি প্রমাণীকরণ কলটি সমাধান করা হয় এবং একটি শংসাপত্র প্রদান করে ( PublicKeyCredential )। শংসাপত্রে একটি প্রমাণীকরণ দাবি রয়েছে।
  • প্রমাণীকরণ দাবী যাচাই করুন.
  • প্রমাণীকরণ দাবি বৈধ হলে, ব্যবহারকারীকে প্রমাণীকরণ করুন।

নিম্নলিখিত বিভাগগুলি প্রতিটি ধাপের সুনির্দিষ্ট বিষয়গুলিতে ডুব দেয়।

চ্যালেঞ্জ তৈরি করুন

অনুশীলনে, একটি চ্যালেঞ্জ হল র্যান্ডম বাইটের একটি অ্যারে, যা একটি ArrayBuffer অবজেক্ট হিসাবে উপস্থাপিত হয়।

// Example challenge, base64URL-encoded
weMLPOSx1VfSnMV6uPwDKbjGdKRMaUDGxeDEUTT5VN8

চ্যালেঞ্জটি তার উদ্দেশ্য পূরণ করে তা নিশ্চিত করতে, আপনাকে অবশ্যই:

  1. নিশ্চিত করুন যে একই চ্যালেঞ্জ একবারের বেশি ব্যবহার করা হয় না। প্রতিটি সাইন-ইন প্রচেষ্টায় একটি নতুন চ্যালেঞ্জ তৈরি করুন৷ প্রতিটি সাইন-ইন প্রচেষ্টার পরে চ্যালেঞ্জটি বাতিল করুন, তা সফল হোক বা ব্যর্থ হোক। একটি নির্দিষ্ট সময়ের পরে চ্যালেঞ্জটিও বাতিল করুন। একবারের বেশি প্রতিক্রিয়ায় একই চ্যালেঞ্জ গ্রহণ করবেন না।
  2. নিশ্চিত করুন চ্যালেঞ্জটি ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত । একটি চ্যালেঞ্জ অনুমান করা কার্যত অসম্ভব হওয়া উচিত একটি ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত চ্যালেঞ্জ সার্ভার-সাইড তৈরি করতে, আপনার বিশ্বাস করা একটি FIDO সার্ভার-সাইড লাইব্রেরির উপর নির্ভর করা ভাল। আপনি যদি পরিবর্তে আপনার নিজের চ্যালেঞ্জ তৈরি করেন, আপনার টেক স্ট্যাকে উপলব্ধ অন্তর্নির্মিত ক্রিপ্টোগ্রাফিক কার্যকারিতা ব্যবহার করুন, বা ক্রিপ্টোগ্রাফিক ব্যবহারের ক্ষেত্রে ডিজাইন করা লাইব্রেরিগুলি সন্ধান করুন৷ উদাহরণের মধ্যে রয়েছে Node.js-এ iso-crypto , অথবা Python-এর গোপনীয়তাস্পেসিফিকেশন অনুসারে, নিরাপদ বলে বিবেচনা করার জন্য চ্যালেঞ্জটি কমপক্ষে 16 বাইট দীর্ঘ হতে হবে।

একবার আপনি একটি চ্যালেঞ্জ তৈরি করলে, পরে যাচাই করতে ব্যবহারকারীর সেশনে এটি সংরক্ষণ করুন।

শংসাপত্রের অনুরোধের বিকল্পগুলি তৈরি করুন

একটি publicKeyCredentialRequestOptions অবজেক্ট হিসাবে শংসাপত্রের অনুরোধের বিকল্পগুলি তৈরি করুন৷

এটি করতে, আপনার FIDO সার্ভার-সাইড লাইব্রেরির উপর নির্ভর করুন। এটি সাধারণত একটি ইউটিলিটি ফাংশন অফার করবে যা আপনার জন্য এই বিকল্পগুলি তৈরি করতে পারে। SimpleWebAuthn অফার করে, উদাহরণস্বরূপ, generateAuthenticationOptions .

publicKeyCredentialRequestOptions পাসকি প্রমাণীকরণের জন্য প্রয়োজনীয় সমস্ত তথ্য থাকা উচিত। আপনার FIDO সার্ভার-সাইড লাইব্রেরির ফাংশনে এই তথ্যটি পাঠান যা publicKeyCredentialRequestOptions অবজেক্ট তৈরির জন্য দায়ী।

কিছু publicKeyCredentialRequestOptions ' ক্ষেত্র ধ্রুবক হতে পারে। অন্যদের গতিশীলভাবে সার্ভারে সংজ্ঞায়িত করা উচিত:

  • rpId : আপনি কোন RP আইডির সাথে শংসাপত্র যুক্ত হবে বলে আশা করেন, উদাহরণস্বরূপ example.com । প্রমাণীকরণ তখনই সফল হবে যদি আপনি এখানে যে RP ID প্রদান করেন সেটি শংসাপত্রের সাথে যুক্ত RP ID-এর সাথে মিলে যায়। RP আইডি পপুলেট করতে, শংসাপত্র নিবন্ধনের সময় আপনি publicKeyCredentialCreationOptions এ যে RP আইডি সেট করেছেন সেই মানটি ব্যবহার করুন।
  • challenge : ডেটার একটি অংশ যা পাসকি প্রদানকারী প্রমাণীকরণের অনুরোধের সময় ব্যবহারকারীর কাছে পাসকি ধারণ করে তা প্রমাণ করতে স্বাক্ষর করবে। চ্যালেঞ্জ তৈরি করুন -এ বিবরণ পর্যালোচনা করুন।
  • allowCredentials : এই প্রমাণীকরণের জন্য গ্রহণযোগ্য শংসাপত্রের একটি অ্যারে। ব্যবহারকারীকে ব্রাউজার দ্বারা দেখানো একটি তালিকা থেকে একটি উপলব্ধ পাসকি নির্বাচন করতে দেওয়ার জন্য একটি খালি অ্যারে পাস করুন৷ বিশদ বিবরণের জন্য RP সার্ভার এবং আবিষ্কারযোগ্য শংসাপত্রগুলি থেকে একটি চ্যালেঞ্জ নিয়ে আসা পর্যালোচনা করুন।
  • userVerification : ডিভাইসের স্ক্রিন লক ব্যবহার করে ব্যবহারকারীর যাচাইকরণ "প্রয়োজনীয়", "পছন্দের" বা "নিরুৎসাহিত" কিনা তা নির্দেশ করে। রিভিউ RP সার্ভার থেকে একটি চ্যালেঞ্জ আনুন
  • timeout : ব্যবহারকারী কতক্ষণ (মিলিসেকেন্ডে) প্রমাণীকরণ সম্পূর্ণ করতে নিতে পারে। এটি যুক্তিসঙ্গতভাবে উদার হওয়া উচিত এবং challenge জীবনকালের চেয়ে ছোট হওয়া উচিত। প্রস্তাবিত ডিফল্ট মান হল 5 মিনিট , কিন্তু আপনি এটি বাড়াতে পারেন — 10 মিনিট পর্যন্ত, যা এখনও প্রস্তাবিত সীমার মধ্যে রয়েছে৷ আপনি যদি ব্যবহারকারীদের হাইব্রিড ওয়ার্কফ্লো ব্যবহার করার আশা করেন তবে দীর্ঘ সময় শেষ হওয়া অর্থপূর্ণ হয়, যা সাধারণত একটু বেশি সময় নেয়। অপারেশন সময় শেষ হলে, একটি NotAllowedError নিক্ষেপ করা হবে।

একবার আপনি publicKeyCredentialRequestOptions তৈরি করলে, এটি ক্লায়েন্টের কাছে পাঠান।

সার্ভার দ্বারা পাঠানো publicKeyCredentialCreationOptions
সার্ভার দ্বারা পাঠানো বিকল্প. challenge ডিকোডিং ক্লায়েন্ট-সাইড ঘটে।

উদাহরণ কোড: শংসাপত্রের অনুরোধের বিকল্পগুলি তৈরি করুন

আমরা আমাদের উদাহরণে SimpleWebAuthn লাইব্রেরি ব্যবহার করছি। এখানে, আমরা ক্রেডেনশিয়াল রিকোয়েস্ট অপশন তৈরি করার কাজটি এর generateAuthenticationOptions ফাংশনে হস্তান্তর করি।

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} from '@simplewebauthn/server';

router.post('/signinRequest', csrfCheck, async (req, res) => {

  // Ensure you nest calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // Use the generateAuthenticationOptions function from SimpleWebAuthn
    const options = await generateAuthenticationOptions({
      rpID: process.env.HOSTNAME,
      allowCredentials: [],
    });
    // Save the challenge in the user session
    req.session.challenge = options.challenge;

    return res.json(options);
  } catch (e) {
    console.error(e);
    return res.status(400).json({ error: e.message });
  }
});

ব্যবহারকারী যাচাই করুন এবং সাইন ইন করুন

যখন navigator.credentials.get ক্লায়েন্টে সফলভাবে সমাধান করে, তখন এটি একটি PublicKeyCredential অবজেক্ট ফেরত দেয়।

সার্ভার দ্বারা পাঠানো PublicKeyCredential অবজেক্ট
navigator.credentials.get একটি PublicKeyCredential ফেরত দেয়।

response হল একটি AuthenticatorAssertionResponse । এটি RP-এ একটি পাসকি দিয়ে চেষ্টা এবং প্রমাণীকরণের জন্য যা প্রয়োজন তা তৈরি করার জন্য ক্লায়েন্টের নির্দেশে পাসকি প্রদানকারীর প্রতিক্রিয়া উপস্থাপন করে। এতে রয়েছে:

  • response.authenticatorData এবং response.clientDataJSON , যেমন পাসকি রেজিস্ট্রেশন ধাপে।
  • response.signature যা এই মানগুলির উপর একটি স্বাক্ষর ধারণ করে।

সার্ভারে PublicKeyCredential অবজেক্ট পাঠান।

সার্ভারে, নিম্নলিখিতগুলি করুন:

ডাটাবেস স্কিমা
প্রস্তাবিত ডাটাবেস স্কিমা। সার্ভার-সাইড পাসকি রেজিস্ট্রেশনে এই ডিজাইন সম্পর্কে আরও জানুন।
  • দাবীটি যাচাই করতে এবং ব্যবহারকারীকে প্রমাণীকরণ করতে আপনার প্রয়োজনীয় তথ্য সংগ্রহ করুন:
    • আপনি প্রমাণীকরণ বিকল্পগুলি তৈরি করার সময় সেশনে সঞ্চিত প্রত্যাশিত চ্যালেঞ্জটি পান৷
    • প্রত্যাশিত মূল এবং আরপি আইডি পান।
    • ব্যবহারকারী কে আপনার ডাটাবেসে খুঁজুন. আবিষ্কারযোগ্য শংসাপত্রের ক্ষেত্রে, আপনি জানেন না যে ব্যবহারকারী কে একটি প্রমাণীকরণ অনুরোধ করছে। খুঁজে বের করতে, আপনার দুটি বিকল্প আছে:
      • বিকল্প 1: PublicKeyCredential অবজেক্টে response.userHandle ব্যবহার করুন। ব্যবহারকারীর টেবিলে, userHandle সাথে মেলে এমন passkey_user_id সন্ধান করুন।
      • বিকল্প 2: PublicKeyCredential অবজেক্টে উপস্থিত শংসাপত্র id ব্যবহার করুন। পাবলিক কী শংসাপত্রের সারণীতে, পাবলিক কী PublicKeyCredential অবজেক্টে উপস্থিত শংসাপত্র id সাথে মেলে এমন শংসাপত্র id সন্ধান করুন৷ তারপর আপনার ব্যবহারকারীদের টেবিলে বিদেশী কী passkey_user_id ব্যবহার করে সংশ্লিষ্ট ব্যবহারকারীর সন্ধান করুন।
    • আপনার ডাটাবেসে সার্বজনীন কী শংসাপত্রের তথ্য খুঁজুন যা আপনি প্রাপ্ত প্রমাণীকরণ দাবির সাথে মেলে। এটি করতে, পাবলিক কী শংসাপত্রের টেবিলে, PublicKeyCredential অবজেক্টে উপস্থিত শংসাপত্র id সাথে মেলে এমন শংসাপত্র id সন্ধান করুন৷
  • প্রমাণীকরণ দাবী যাচাই করুন. এই যাচাইকরণের ধাপটি আপনার FIDO সার্ভার-সাইড লাইব্রেরিতে হস্তান্তর করুন, যা সাধারণত এই উদ্দেশ্যে একটি ইউটিলিটি ফাংশন অফার করবে। SimpleWebAuthn অফার করে, উদাহরণস্বরূপ, verifyAuthenticationResponseপরিশিষ্টে হুডের নিচে কী ঘটছে তা জানুন: প্রমাণীকরণ প্রতিক্রিয়া যাচাইকরণ

  • রিপ্লে আক্রমণ প্রতিরোধ করতে , যাচাইকরণ সফল হোক বা না হোক চ্যালেঞ্জটি মুছুন

  • ব্যবহারকারী সাইন ইন করুন. যাচাইকরণ সফল হলে, ব্যবহারকারীকে সাইন-ইন করা হিসেবে চিহ্নিত করতে সেশনের তথ্য আপডেট করুন। আপনি ক্লায়েন্টকে একটি user বস্তু ফেরত দিতে চাইতে পারেন, যাতে ফ্রন্টএন্ড নতুন সাইন-ইন করা ব্যবহারকারীর সাথে সম্পর্কিত তথ্য ব্যবহার করতে পারে।

উদাহরণ কোড: ব্যবহারকারী যাচাই করুন এবং সাইন ইন করুন

আমরা আমাদের উদাহরণে SimpleWebAuthn লাইব্রেরি ব্যবহার করছি। এখানে, আমরা প্রমাণীকরণ প্রতিক্রিয়ার যাচাইকরণ তার verifyAuthenticationResponse ফাংশনে হস্তান্তর করি।

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';

router.post('/signinResponse', csrfCheck, async (req, res) => {
  const response = req.body;
  const expectedChallenge = req.session.challenge;
  const expectedOrigin = getOrigin(req.get('User-Agent'));
  const expectedRPID = process.env.HOSTNAME;

  // Ensure you nest verification function calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // Find the credential stored to the database by the credential ID
    const cred = Credentials.findById(response.id);
    if (!cred) {
      throw new Error('Credential not found.');
    }
    // Find the user - Here alternatively we could look up the user directly
    // in the Users table via userHandle
    const user = Users.findByPasskeyUserId(cred.passkey_user_id);
    if (!user) {
      throw new Error('User not found.');
    }
    // Base64URL decode some values
    const authenticator = {
      credentialPublicKey: isoBase64URL.toBuffer(cred.publicKey),
      credentialID: isoBase64URL.toBuffer(cred.id),
      transports: cred.transports,
    };

    // Verify the credential
    const { verified, authenticationInfo } = await verifyAuthenticationResponse({
      response,
      expectedChallenge,
      expectedOrigin,
      expectedRPID,
      authenticator,
      requireUserVerification: false,
    });

    if (!verified) {
      throw new Error('User verification failed.');
    }

    // Kill the challenge for this session.
    delete req.session.challenge;

    req.session.username = user.username;
    req.session['signed-in'] = 'yes';

    return res.json(user);
  } catch (e) {
    delete req.session.challenge;

    console.error(e);
    return res.status(400).json({ error: e.message });
  }
});

পরিশিষ্ট: প্রমাণীকরণ প্রতিক্রিয়া যাচাইকরণ

প্রমাণীকরণ প্রতিক্রিয়া যাচাই করার জন্য নিম্নলিখিত চেকগুলি রয়েছে:

  • নিশ্চিত করুন যে RP ID আপনার সাইটের সাথে মেলে।
  • নিশ্চিত করুন যে অনুরোধের উৎস আপনার সাইটের সাইন-ইন মূলের সাথে মেলে। অ্যান্ড্রয়েড অ্যাপ্লিকেশানগুলির জন্য, যাচাই মূল পর্যালোচনা করুন৷
  • ডিভাইসটি আপনার দেওয়া চ্যালেঞ্জ প্রদান করতে সক্ষম হয়েছে কিনা তা পরীক্ষা করুন।
  • যাচাই করুন যে প্রমাণীকরণের সময়, ব্যবহারকারী আপনার RP হিসাবে বাধ্যতামূলক প্রয়োজনীয়তাগুলি অনুসরণ করেছেন। আপনার যদি ব্যবহারকারীর যাচাইকরণের প্রয়োজন হয়, নিশ্চিত করুন যে authenticatorData uv (ব্যবহারকারী যাচাইকৃত) পতাকাটি trueauthenticatorData up (ব্যবহারকারীর উপস্থিত) পতাকাটি true কিনা তা পরীক্ষা করুন, যেহেতু পাসকিগুলির জন্য সর্বদা ব্যবহারকারীর উপস্থিতি প্রয়োজন
  • স্বাক্ষর যাচাই করুন। স্বাক্ষর যাচাই করতে, আপনার প্রয়োজন:
    • স্বাক্ষর, যা স্বাক্ষরিত চ্যালেঞ্জ: response.signature
    • সর্বজনীন কী, সহ স্বাক্ষর যাচাই করতে।
    • মূল স্বাক্ষরিত ডেটা। এটি সেই ডেটা যার স্বাক্ষর যাচাই করতে হবে।
    • ক্রিপ্টোগ্রাফিক অ্যালগরিদম যা স্বাক্ষর তৈরি করতে ব্যবহৃত হয়েছিল।

এই ধাপগুলি সম্পর্কে আরও জানতে, verifyAuthenticationResponse জন্য SimpleWebAuthn-এর সোর্স কোড দেখুন বা স্পেসিফিকেশনে যাচাইকরণের সম্পূর্ণ তালিকায় ডুব দিন।