نمای کلی
در اینجا نمای کلی سطح بالایی از مراحل کلیدی مربوط به اصالتسنجی گذرکلید ارائه شده است:

- چالش و گزینههای دیگر موردنیاز برای اصالتسنجی با گذرکلید را تعریف کنید. آنها را به کارخواه ارسال کنید تا بتوانید آنها را به تماس اصالتسنجی گذرکلیدتان (
navigator.credentials.getدر وب) منتقل کنید. پساز اینکه کاربر اصالتسنجی با گذرکلید را تأیید کرد، فراخوانی اصالتسنجی با گذرکلید حلوفصل میشود و اطلاعات اعتباری (PublicKeyCredential) را برمیگرداند. اطلاعات اعتباری حاوی ادعای اصالتسنجی است.
- ادعای اصالتسنجی را درستیسنجی کنید.
- اگر ادعای اصالتسنجی معتبر باشد، کاربر را اصالتسنجی کنید.
بخشهای زیر جزئیات هر مرحله را شرح میدهد.
چالش را ایجاد کنید
در عمل، چالش آرایهای از بایتهای تصادفی است که بهصورت شیء ArrayBuffer نشان داده میشود.
// Example challenge, base64URL-encoded
weMLPOSx1VfSnMV6uPwDKbjGdKRMaUDGxeDEUTT5VN8
برای اطمینان از اینکه چالش به هدف خود میرسد، باید:
- مطمئن شوید که هر چالش هرگز بیشاز یک بار استفاده نشود. در هر تلاش برای ورود به سیستم، چالش جدیدی ایجاد کنید. پساز هر تلاش برای ورود به سیستم، چه موفقیتآمیز باشد و چه ناموفق، چالش را دور بیندازید. همچنین چالش را پساز مدتزمان مشخصی کنار بگذارید. هرگز چالش یکسانی را بیشاز یک بار در پاسخ نپذیرید.
- مطمئن شوید چالش ازنظر رمزنگاری ایمن باشد. حدس زدن چالش باید عملاً غیرممکن باشد. برای ایجاد یک چالش امن ازنظر رمزنگاری در سمت سرور، بهتر است به یک کتابخانه سمت سرور FIDO که به آن اعتماد دارید تکیه کنید. اگر بهجای آن چالشهای خودتان را ایجاد میکنید، از عملکرد رمزنگاری داخلی موجود در پشته فناوریتان استفاده کنید یا بهدنبال کتابخانههایی باشید که برای موارد استفاده رمزنگاری طراحی شدهاند. نمونهها شامل iso-crypto در Node.js، یا secrets در Python است. طبق مشخصات، چالش باید حداقل ۱۶ بایت طول داشته باشد تا ایمن درنظر گرفته شود.
پساز ایجاد چالش، آن را در جلسه کاربر ذخیره کنید تا بعداً آن را درستیسنجی کنید.
ایجاد گزینههای درخواست اطلاعات کاربری
گزینههای درخواست اطلاعات کاربری را بهعنوان شیء publicKeyCredentialRequestOptions ایجاد کنید.
برای انجام این کار، به کتابخانه سمت سرور FIDO خود تکیه کنید. معمولاً تابع سودمندیای ارائه میدهد که میتواند این گزینهها را برای شما ایجاد کند. برای مثال، SimpleWebAuthn generateAuthenticationOptions را ارائه میدهد.
publicKeyCredentialRequestOptions باید حاوی همه اطلاعات لازم برای اصالتسنجی با گذرکلید باشد. این اطلاعات را به تابع موجود در کتابخانه سمت سرور FIDO که مسئول ایجاد شیء publicKeyCredentialRequestOptions است ارسال کنید.
برخیاز فیلدهای publicKeyCredentialRequestOptions میتوانند ثابت باشند. سایر موارد باید بهصورت پویا در سرور تعریف شوند:
rpId: انتظار دارید اعتبارنامه با کدام شناسه طرف محدود مرتبط شود، برای مثالexample.com. اصالتسنجی فقط درصورتی موفقیتآمیز خواهد بود که شناسه RP که در اینجا ارائه میدهید با شناسه RP منسوب به اعتبارنامه مطابقت داشته باشد. برای پر کردن شناسه RP، از همان مقداری استفاده کنید که درطول ثبت اطلاعات اعتباری درpublicKeyCredentialCreationOptionsبرای شناسه RP تنظیم کردهاید.challenge: بخشی از دادهها که ارائهدهنده گذرکلید آن را امضا میکند تا ثابت کند کاربر در زمان درخواست اصالتسنجی، گذرکلید را دراختیار دارد. جزئیات را در ایجاد چالش مرور کنید.allowCredentials: آرایهای از اعتبارنامههای قابلقبول برای این اصالتسنجی. آرایه خالی را ارسال کنید تا کاربر بتواند گذرکلید دردسترس را از فهرستی که مرورگر نشان میدهد انتخاب کند. برای جزئیات، واکشی چالش از سرور RP و بررسی عمیق اعتبارنامههای قابلشناسایی را مرور کنید.userVerification: نشان میدهد که درستیسنجی کاربر بااستفاده از قفل صفحه دستگاه «الزامی»، «ترجیحی»، یا «غیرتوصیهشده» است. واکشی چالش از سرور RP را مرور کنید.timeout: مدت زمانی (به میلیثانیه) که کاربر میتواند برای تکمیل اصالتسنجی صرف کند. این زمان باید بهطور منطقی طولانی و کوتاهتر از طول عمرchallengeباشد. مقدار پیشفرض توصیهشده ۵ دقیقه است، اما میتوانید آن را افزایش دهید — تا ۱۰ دقیقه، که هنوز در محدوده توصیهشده است. اگر انتظار دارید کاربران از گردش کار ترکیبی استفاده کنند، که معمولاً کمی بیشتر طول میکشد، زمانهای طولانیتر برای اتمام مهلت منطقی است. اگر مهلت عملیات بهپایان برسد،NotAllowedErrorایجاد خواهد شد.
وقتی publicKeyCredentialRequestOptions را ایجاد کردید، آن را برای کارخواه ارسال کنید.
challenge رمزگشایی در سمت کارخواه انجام میشود.نمونه کد: ایجاد گزینههای درخواست اطلاعات کاربری
در مثالهایمان از کتابخانه SimpleWebAuthn استفاده میکنیم. در اینجا، ایجاد گزینههای درخواست اطلاعات کاربری را به تابع generateAuthenticationOptions آن واگذار میکنیم.
import {
generateAuthenticationOptions,
} 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 را برمیگرداند.
navigator.credentials.get PublicKeyCredential را برمیگرداند.response یک AuthenticatorAssertionResponse است. این پاسخ ارائهدهنده گذرکلید به دستورالعمل کارخواه برای ایجاد آنچه برای امتحان کردن و اصالتسنجی با گذرکلید در RP لازم است را نشان میدهد. این اطلاعات شامل موارد زیر میشود:
response.authenticatorDataوresponse.clientDataJSON، مثل مرحله ثبت گذرکلید.response.signatureکه حاوی امضایی روی این مقادیر است.
شیء PublicKeyCredential را به سرور ارسال کنید.
در سرور، کارهای زیر را انجام دهید:
- اطلاعاتی را که برای درستیسنجی ادعا و اصالتسنجی کاربر نیاز دارید جمعآوری کنید:
- چالش موردانتظاری را که هنگام تولید گزینههای اصالتسنجی در جلسه ذخیره کردهاید دریافت کنید.
- مبدأ و شناسه RP موردانتظار را دریافت کنید.
- در پایگاه دادهتان پیدا کنید که کاربر کیست. درخصوص اطلاعات اعتباری قابلکشف، نمیدانید کاربری که درخواست اصالتسنجی میکند کیست. برای اطلاع از این موضوع، دو گزینه دارید:
- گزینه ۱: از
response.userHandleدر شیءPublicKeyCredentialاستفاده کنید. در جدول کاربران، بهدنبالpasskey_user_idکه باuserHandleمطابقت دارد بگردید. - گزینه ۲: از اطلاعات اعتباری
idموجود در شیءPublicKeyCredentialاستفاده کنید. در جدول اعتبارنامه کلید عمومی، بهدنبال اعتبارنامهidبگردید که با اعتبارنامهidموجود در شیءPublicKeyCredentialمطابقت داشته باشد. سپس بااستفاده از کلید خارجیpasskey_user_id، کاربر مربوطه را در جدول کاربران پیدا کنید.
- گزینه ۱: از
- در پایگاه دادهتان، اطلاعات اطلاعات کاربری کلید عمومی را که با ادعای اصالتسنجی دریافتیتان مطابقت دارد پیدا کنید. برای انجام این کار، در جدول اعتبارنامههای کلید عمومی، بهدنبال اعتبارنامه
idبگردید که با اعتبارنامهidموجود در شیءPublicKeyCredentialمطابقت داشته باشد.
ادعای اصالتسنجی را تأیید کنید. این مرحله درستیسنجی را به کتابخانه سمت سرور FIDO خود بسپارید که معمولاً یک تابع سودمندی برای این منظور ارائه میدهد. برای مثال، SimpleWebAuthn
verifyAuthenticationResponseرا ارائه میدهد. در پیوست: درستیسنجی پاسخ اصالتسنجی با آنچه در پشت صحنه اتفاق میافتد آشنا شوید.چالش را حذف کنید، چه درستیسنجی موفقیتآمیز باشد چه نباشد، تا از حملات بازپخش جلوگیری شود.
کاربر را به سیستم وارد کنید. اگر درستیسنجی موفقیتآمیز بود، اطلاعات جلسه را بهروز کنید تا کاربر بهعنوان واردشده به سیستم علامتگذاری شود. همچنین ممکن است بخواهید شیء
userرا به کارخواه برگردانید تا بخش کاربری بتواند از اطلاعات مرتبط با کاربر جدیدی که به سیستم وارد شده است استفاده کند.
کد نمونه: درستیسنجی و ورود به سیستم کاربر
در مثالهایمان از کتابخانه SimpleWebAuthn استفاده میکنیم. در اینجا، درستیسنجی پاسخ اصالتسنجی را به تابع verifyAuthenticationResponse آن واگذار میکنیم.
import {
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.');
}
// Prepare credential information for verification
const credential = {
id: cred.id,
publicKey: isoBase64URL.toBuffer(cred.publicKey),
transports: cred.transports,
};
// Verify the credential
const { verified, authenticationInfo } = await verifyAuthenticationResponse({
response,
expectedChallenge,
expectedOrigin,
expectedRPID,
credential,
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 });
}
});
پیوست: درستیسنجی پاسخ اصالتسنجی
درستیسنجی پاسخ اصالتسنجی شامل بررسیهای زیر است:
- مطمئن شوید شناسه طرف متکی با سایت شما مطابقت داشته باشد.
- مطمئن شوید که مبدأ درخواست با مبدأ ورود به سیستم سایت شما مطابقت داشته باشد. برای برنامههای Android، درستیسنجی مبدأ را مرور کنید.
- بررسی کنید که دستگاه توانسته باشد چالشی را که به آن دادهاید ارائه دهد.
- درستیسنجی کنید که کاربر درطول اصالتسنجی، الزامات شما را بهعنوان RP رعایت کرده باشد. اگر به درستیسنجی کاربر نیاز دارید، مطمئن شوید که پرچم
uv(کاربر درستیسنجیشده) درauthenticatorDataرویtrueتنظیم شده باشد. بررسی کنید که پرچمup(حضور کاربر) درauthenticatorDataرویtrueتنظیم شده باشد، زیرا حضور کاربر برای گذرکلیدها همیشه الزامی است. - امضا را درستیسنجی کنید. برای درستیسنجی امضا، به موارد زیر نیاز دارید:
- امضا، که همان چالش امضاشده است:
response.signature - کلید عمومی، برای درستیسنجی امضا.
- دادههای امضاشده اصلی. این دادهای است که امضای آن باید درستیسنجی شود.
- الگوریتم رمزنگاری که برای ایجاد امضا استفاده شده است.
- امضا، که همان چالش امضاشده است:
برای کسب اطلاعات بیشتر درباره این مراحل، کد منبع SimpleWebAuthn برای verifyAuthenticationResponse را بررسی کنید یا فهرست کامل درستیسنجیها را در مشخصات بررسی کنید.