An ninh mạng & Bảo mậtĐộ khó: N2 / N1 · Nâng cao⏱ 9 phút đọc

Thiết kế xác thực với Amazon Cognito: 6 điểm mấu chốt về Passkey, MFA và Session (Cognito認証設計)

概Điểm nhấn trọng tâm của bài viết (Overview)

Phân tích 6 giới hạn kiến trúc khi thiết kế luồng xác thực trên AWS Cognito: từ idle timeout, khóa màn hình, SMS MFA, xác thực OTP đặt lại mật khẩu đến Passkey và Admin API.

Thiết kế xác thực với Amazon Cognito: 6 điểm mấu chốt về Passkey, MFA và Session (Cognito認証設計)

✍️ Tác giả nguồn: gerrard15 (thuộc tổ chức claves) trên diễn đàn công nghệ Qiita Nhật Bản.
🔗 Tiêu đề gốc: Amazon Cognitoの認証設計で押さえたい6つの注意点|パスキー・MFA・セッション管理
🌐 Bài gốc: Qiita · 29d62c11c26945570e82, đăng và cập nhật ngày 16/10/2026, đọc ngày 17/10/2026.
📝 Biên soạn: Lược thuật kết hợp phân tích kiến trúc, đối chiếu tài liệu AWS & OWASP, và góc nhìn thiết kế yêu cầu dành cho Kỹ sư cầu nối (BrSE) và lập trình viên Full-stack. Câu đàm thoại tiếng Nhật là ví dụ luyện tập tự soạn, không phải trích dẫn nguyên văn.


📌 Điểm nhấn cốt lõi (Key Takeaways)

  • Cognito không tự quản lý Idle Timeout: Thời hạn của JSON Web Token (JWT) là cố định; việc đăng xuất khi người dùng không thao tác phải do ứng dụng tự theo dõi mốc thời gian và từ chối tại tầng API nghiệp vụ.
  • Xác thực lại (Re-authentication) dễ phát sinh tin nhắn rác: Luồng AdminInitiateAuth chuẩn sẽ kích hoạt gửi SMS nếu người dùng bật SMS_MFA, không thể chỉ dùng để đối chiếu mật khẩu mà bỏ qua bước gửi mã.
  • Không thể xác minh độc lập mã đặt lại mật khẩu: API ConfirmForgotPassword bắt buộc gửi đồng thời mã xác nhận kèm mật khẩu mới. Nếu muốn xác thực mã trước khi cho phép nhập mật khẩu mới, phải tự xây luồng OTP riêng.
  • Passkey trong luồng USER_AUTH chuẩn vẫn đòi USERNAME: Muốn lược bỏ ô nhập tên đăng nhập, ứng dụng bắt buộc phải lưu trữ danh tính từ phiên trước (qua Cookie/Local Storage).
  • Đổi mật khẩu sau Passkey cần phân biệt quyền hạn: Người dùng đã có mật khẩu không thể dùng ChangePassword mà bỏ qua mật khẩu cũ; nếu dùng AdminSetUserPassword phía backend thì toàn bộ trách nhiệm phân quyền phải do ứng dụng đảm nhận.

1. Ranh giới giữa Amazon Cognito và Ứng dụng nghiệp vụ

Khi phát triển các ứng dụng Web doanh nghiệp sử dụng Amazon Cognito User Pools, nhiều nhóm dự án thường mặc định rằng: “Chỉ cần chọn dịch vụ xác thực được quản lý của AWS là toàn bộ yêu cầu bảo mật từ đăng nhập, khóa màn hình đến thu hồi phiên sẽ tự động được đáp ứng.”

Tuy nhiên, trong bài chia sẻ của tác giả gerrard15, thực tế triển khai cho thấy có sự phân định rất rõ ràng giữa chuẩn OAuth2/OIDC mà Cognito cung cấp và trạng thái phiên (Session State) mà ứng dụng phải tự quản trị. Nếu BrSE không nắm rõ ranh giới này ngay từ giai đoạn định nghĩa yêu cầu (要件定義), nhóm phát triển rất dễ rơi vào bẫy trễ tiến độ khi phát hiện ra các luồng nghiệp vụ đặc thù không được Cognito hỗ trợ sẵn.

Bản dịch: Chúng ta hãy bóc tách rõ ràng giữa phạm vi mà quy cách chuẩn của Cognito có thể đáp ứng và những yêu cầu đòi hỏi phải quản lý trạng thái ở phía ứng dụng.


2. Phân tích 6 điểm mấu chốt trong thiết kế xác thực

① Đăng xuất khi không thao tác (無操作ログアウト) và Thu hồi token tức thì

  • Vấn đề thực tế: Khách hàng yêu cầu “Sau 15 phút người dùng không thao tác, hệ thống phải tự động đăng xuất và vô hiệu hóa quyền truy cập”.
  • Giới hạn của Cognito: Access Token và ID Token của Cognito là stateless JWT có thời hạn cố định (expiration). Kể cả khi gọi API thu hồi Refresh Token (GlobalSignOut hoặc RevokeToken), các Access Token đã phát hành vẫn có hiệu lực cho đến khi hết hạn tự nhiên nếu API backend chỉ kiểm tra chữ ký số (signature) mà không kiểm tra trạng thái thu hồi.
  • Giải pháp kiến trúc: Tuân thủ hướng dẫn OWASP Session Management Cheat Sheet (Idle Timeout): Ứng dụng phải lưu mốc thời gian thao tác gần nhất (Last Activity Timestamp) ở backend session/Redis và kiểm tra trước mỗi request nghiệp vụ.

② Khóa màn hình và bảo toàn dữ liệu đang nhập (画面ロック・作業保持)

  • Vấn đề: Khi rời bàn làm việc, màn hình bị khóa nhưng người dùng không muốn mất form dữ liệu đang điền dở.
  • Giải pháp: Khóa màn hình là trạng thái UI kết hợp với từ chối request API ở backend. Tuyệt đối không chỉ che màn hình bằng modal ở frontend, vì kẻ tấn công vẫn có thể dùng token trong DevTools để gọi API. Khi mở khóa, ứng dụng dùng Cognito để xác thực lại đúng tài khoản ban đầu rồi mới khôi phục phiên làm việc.

③ Xác nhận lại mật khẩu mà không gửi lại SMS (SMSなしのパスワード再確認)

  • Vấn đề: Khi thực hiện thao tác nhạy cảm (như chuyển tiền hoặc đổi thông tin cá nhân), ứng dụng muốn yêu cầu người dùng nhập lại mật khẩu để xác thực.
  • Giới hạn: Nếu người dùng đã kích hoạt SMS_MFA, việc gọi luồng chuẩn AdminInitiateAuth sẽ tự động kích hoạt gửi tin nhắn SMS chứa mã OTP. Không thể chặn SMS chỉ bằng cách không hiển thị màn hình nhập mã OTP trên frontend!
  • Giải pháp: Nếu dùng Custom SMS Sender Lambda Trigger, trigger này sẽ can thiệp vào toàn bộ SMS của User Pool (gồm cả đăng ký và quên mật khẩu). Vì vậy, cần cân nhắc dùng WebAuthn/Passkey hoặc đánh giá xem thao tác đó có thực sự cần bước xác thực thứ hai hay chỉ cần kiểm tra session hợp lệ.

④ Kiểm tra mã OTP đặt lại mật khẩu trước khi cho nhập mật khẩu mới

  • Vấn đề: Thiết kế UI thường chia làm 2 bước: Màn hình 1 nhập mã OTP gửi qua email/SMS; sau khi mã đúng mới chuyển sang Màn hình 2 cho nhập mật khẩu mới.
  • Giới hạn của Cognito: API ConfirmForgotPassword là atomic API: đòi hỏi phải gửi đồng thời cả ConfirmationCode VÀ Password. Cognito không cung cấp API chỉ để “thử kiểm tra xem mã có đúng hay không”.
  • Giải pháp: Hoặc điều chỉnh UI để gửi cả hai thông tin ở bước cuối cùng, hoặc nếu bắt buộc phải tách bước thì backend phải tự sinh mã OTP nội bộ, xác minh độc lập, rồi mới gọi AdminSetUserPassword.
[Luồng chuẩn Cognito]
Màn hình 1: Nhập OTP (chưa kiểm tra) 
       ↓ 
Màn hình 2: Nhập Mật khẩu mới 
       ↓
Gửi đồng thời cả hai qua ConfirmForgotPassword -> Thành công / Thất bại

⑤ Đăng nhập Passkey không cần nhập Username

  • Vấn đề: Người dùng muốn trải nghiệm đăng nhập không mật khẩu (Passkey/FIDO2) bằng cách chạm vân tay mà không cần gõ trước địa chỉ email hoặc tên đăng nhập.
  • Giới hạn: Trong luồng chuẩn USER_AUTH của Cognito, API InitiateAuth bắt buộc phải có tham số USERNAME.
  • Giải pháp: Để mang lại trải nghiệm không cần gõ, ứng dụng client phải lưu trữ username/email đã từng đăng nhập vào Secure Cookie hoặc Local Storage để tự động đính kèm vào payload gửi lên API.

⑥ Đổi mật khẩu sau khi xác thực bằng Passkey

  • Vấn đề: Người dùng đăng nhập bằng Passkey, sau đó vào trang cá nhân để thiết lập hoặc cập nhật mật khẩu.
  • Giới hạn: API ChangePassword bắt buộc người dùng phải cung cấp PreviousPassword. Nếu người dùng quên mật khẩu cũ hoặc tài khoản thuần Passkey, API này sẽ từ chối.
  • Giải pháp: Backend phải sử dụng API quản trị AdminSetUserPassword với cờ Permanent: true. Khi dùng API này, backend hoàn toàn chịu trách nhiệm xác minh danh tính và quyền hạn của người gọi (theo khuyến nghị OWASP Re-authentication).

3. Kiến trúc luồng xác thực tổng thể

Dưới đây là sơ đồ ranh giới trách nhiệm giữa Ứng dụng và AWS Cognito do BrSE.vn tổng hợp:

Sơ đồ kiến trúc xác thực Cognito và ứng dụng

Sơ đồ do BrSE.vn tự vẽ, phát hành theo CC0 1.0 Universal. Tệp nằm trong repository; đây không phải screenshot hoặc tài nguyên lấy từ Qiita.


4. Minh họa mã nguồn: Kiểm soát thu hồi Token tại Backend

Ví dụ TypeScript (Node.js/Express) minh họa cơ chế kiểm tra thời gian không thao tác (Idle Timeout) và danh sách thu hồi (Revocation Cache) trước khi ủy quyền cho Access Token:

import { Request, Response, NextFunction } from 'express';

interface UserSessionState {
  userId: string;
  lastActiveAt: number;     // Epoch timestamp in milliseconds
  isRevoked: boolean;
}

// Giả lập kho lưu trữ trạng thái phiên (trong thực tế dùng Redis hoặc DynamoDB)
const sessionStore = new Map<string, UserSessionState>();
const IDLE_TIMEOUT_MS = 15 * 60 * 1000; // 15 phút

export function enforceIdleTimeoutAndRevocation(
  req: Request,
  res: Response,
  next: NextFunction
): void {
  const sessionId = req.headers['x-session-id'] as string;
  if (!sessionId) {
    res.status(401).json({ error: 'SESSION_MISSING', message: 'Thiếu định danh phiên' });
    return;
  }

  const session = sessionStore.get(sessionId);
  const now = Date.now();

  if (!session) {
    res.status(401).json({ error: 'SESSION_NOT_FOUND', message: 'Phiên không tồn tại' });
    return;
  }

  // 1. Kiểm tra trạng thái đã bị thu hồi từ trước
  if (session.isRevoked) {
    res.status(401).json({ error: 'TOKEN_REVOKED', message: 'Phiên làm việc đã bị hủy bỏ' });
    return;
  }

  // 2. Kiểm tra thời gian không thao tác (Idle Timeout)
  if (now - session.lastActiveAt > IDLE_TIMEOUT_MS) {
    session.isRevoked = true; // Đánh dấu thu hồi
    res.status(401).json({ error: 'IDLE_TIMEOUT', message: 'Đã hết thời gian thao tác, vui lòng đăng nhập lại' });
    return;
  }

  // Cập nhật mốc thời gian thao tác mới nhất
  session.lastActiveAt = now;
  next();
}

Lưu ý: Mã nguồn được thiết kế chạy cục bộ nhằm mục đích minh họa nguyên lý xác thực; không kết nối trực tiếp cơ sở dữ liệu production.


5. Kinh nghiệm thực chiến dành cho BrSE

  1. Làm rõ tài liệu SRS trước khi chốt kiến trúc: Khi khách hàng Nhật yêu cầu “Xác thực hai bước” hoặc “Khóa màn hình sau X phút”, BrSE cần hỏi rõ: Có chấp nhận gửi mã xác thực kèm mật khẩu ở bước cuối cùng không? hay Bắt buộc phải kiểm tra mã OTP độc lập trước khi mở form mật khẩu mới?
  2. Dự toán chi phí triển khai bổ sung: Nếu các yêu cầu không khớp với quy cách chuẩn của Cognito, nhóm phát triển sẽ phải tốn thêm công sức xây dựng dịch vụ quản lý session phụ trợ (Redis, Lambda Triggers, cơ chế chống CSRF). Cần đưa chi phí này vào bảng phân tích tác động (Impact Analysis) trước khi cam kết tiến độ.

Bản dịch: Nếu chúng ta có thể điều chỉnh luồng màn hình phù hợp với quy cách chuẩn của Cognito, dự án hoàn toàn có thể tiết kiệm được chi phí phát triển bổ sung.


📚 Sổ tay thuật ngữ IT & Hán Việt (重要IT用語集)

Thuật ngữ (Kanji) Furigana / Romaji Âm Hán Việt Ý nghĩa tiếng Việt Cấp độ
認証設計にんしょうせっけい にんしょうせっけい NHẬN CHỨNG THIẾT KẾ Thiết kế cơ chế xác thực Technical
要件定義ようけんていぎ ようけんていぎ YẾU KIỆN ĐỊNH NGHĨA Định nghĩa yêu cầu phần mềm Technical
無操作むそうさ むそうさ VÔ THAO TÁC Không có thao tác (Idle) N2
失効しっこう しっこう THẤT HIỆU Hết hiệu lực, thu hồi (Revoke/Invalidate) N1
保持ほじ ほじ BẢO TRÌ Giữ lại, duy trì (Retain/Preserve) N2
認可にんか にんか NHẬN KHẢ Phân quyền, cấp phép (Authorization) Technical
再認証さいにんしょう さいにんしょう TÁI NHẬN CHỨNG Xác thực lại (Re-authentication) Technical
制約せいやく せいやく CHẾ ƯỚC Ràng buộc, giới hạn quy cách N2
配送制御はいそうせいぎょ はいそうせいぎょ PHỐI TỐNG CHẾ NGỰ Điều phối gửi tin/thư (Delivery control) Technical
事前検証じぜんけんしょう じぜんけんしょう SỰ TIỀN KIỂM CHỨNG Kiểm tra, xác minh trước N2

📐 Ngữ pháp tiếng Nhật IT bổ trợ (文法解説)

  • 〜に収まる (N2): Nằm gọn trong…, vừa vặn trong phạm vi…
    • Cấu trúc: Danh từ + に収まる
    • Ngữ cảnh IT: 標準仕様ひょうじゅんしように収おさまる = Vừa vặn trong đặc tả tiêu chuẩn của dịch vụ (không cần custom thêm).
  • 〜を補う (N2): Bổ sung, bù đắp cho phần còn thiếu.
    • Cấu trúc: Danh từ + を補う
    • Ngữ cảnh IT: アプリ側がわで何なにを補おぎなうべきか = Cần bổ sung thêm xử lý gì ở phía ứng dụng.
  • 〜だけでなく〜も (N3): Không những… mà còn…
    • Cấu trúc: A だけでなく B も
    • Ngữ cảnh IT: 通常つうじょうログインだけでなく再設定さいせっていも = Không chỉ đăng nhập thông thường mà cả luồng đặt lại mật khẩu.
  • 〜を踏まえたうえで (N1): Dựa trên cơ sở…, sau khi đã cân nhắc thấu đáo…
    • Cấu trúc: Danh từ + を踏まえたうえで
    • Ngữ cảnh IT: コストを踏ふまえたうえで判断はんだんする = Đưa ra quyết định sau khi đã cân nhắc toàn diện chi phí.
Quay lại danh sách bài viết
架
Ban biên tập BrSE.vnKỹ sư Cầu nối & Chuyên gia Dự án Nhật - Việt

Đội ngũ biên tập BrSE.vn gồm các Bridge Software Engineer (BrSE) và Solution Architect với hơn 10 năm kinh nghiệm thực chiến trong các dự án hợp tác công nghệ Việt - Nhật. Chia sẻ kiến thức về kỹ thuật làm rõ yêu cầu (要件定義), tài liệu đặc tả hệ thống, lược dịch chuyên sâu từ Qiita và kỹ năng cầu nối song hành công nghệ - nghiệp vụ.