The Copenhagen Book: Hướng dẫn triển khai xác thực cho ứng dụng web
(thecopenhagenbook.com)- Đây là bộ hướng dẫn cơ bản có thể tham khảo khi thiết kế triển khai xác thực trong ứng dụng web, nhằm lấp đầy những khoảng trống của các tài liệu xác thực trực tuyến
- Là tài liệu miễn phí, mã nguồn mở và được cung cấp theo cách cộng đồng cùng duy trì
- Một số nội dung có thể mang tính quan điểm hoặc chưa hoàn chỉnh, vì vậy phù hợp hơn khi dùng làm tài liệu bổ trợ thay vì tiêu chuẩn duy nhất
- Khuyến nghị sử dụng cùng với OWASP Cheat Sheet Series, một tài liệu tham khảo về bảo mật xác thực
- Nếu có đề xuất hoặc lo ngại, bạn có thể mở issue mới để gửi phản hồi và tham gia cải thiện tài liệu
Mục đích và tính chất của tài liệu
- The Copenhagen Book cung cấp các hướng dẫn chung có thể tham khảo khi triển khai xác thực (auth) trong ứng dụng web
- Cách vận hành như sau
- Được cung cấp miễn phí
- Là mã nguồn mở
- Được cộng đồng duy trì
- Nội dung đôi khi có thể mang tính quan điểm hoặc chưa hoàn thiện
Tài liệu nên tham khảo cùng và cách tham gia
- Khi triển khai xác thực, nên tham khảo cùng OWASP Cheat Sheet Series
- Nếu có đề xuất hoặc lo ngại, bạn có thể mở issue mới
1 bình luận
Ý kiến trên Hacker News
Nếu tôi không nhầm thì bài này có vẻ do tác giả của Lucia, một thư viện xác thực phổ biến cho TypeScript, viết
Gần đây, thư viện Lucia đã công bố rằng họ cho rằng thư viện này không còn là một cách tiện dụng để triển khai xác thực nữa, nên sẽ ngừng thư viện và thay thế bằng một loạt bài hướng dẫn dạng văn bản
Bản xem trước hướng dẫn ban đầu đọc khá tốt và cũng rất hợp với The Copenhagen Book
https://github.com/lucia-auth/lucia
https://github.com/lucia-auth/lucia/discussions/1707
https://lucia-next.pages.dev/
Thật hiếm khi thấy họ nhận ra dấu hiệu độ phức tạp sắp bùng nổ, thừa nhận rằng thư viện không còn hữu ích ngay cả với chính mình, rồi khiêm tốn rời khỏi con đường tiêu chuẩn của tình trạng phình to thư viện thường gặp
Thực ra 99% người dùng chỉ cần đăng nhập/đăng xuất, và thứ này cực kỳ hữu ích khi được cung cấp dưới dạng thư viện
Nếu phải cung cấp passkey Web 8.0 bằng socket đường cong elliptic WASM thì có thể tự làm hoặc dùng Auth0, nhưng ném đặc tả OAuth và danh sách cạm bẫy cho người đang làm một ứng dụng nấu ăn CRUD rồi bảo họ tự xây xác thực thì thật kỳ lạ
Người đó nên tập trung vào ý tưởng thực sự thay vì phát minh lại hệ thống ống nước, và nhiều người cuối cùng có thể triển khai sai đến mức thực tế là không có xác thực
Phần này sai: “địa chỉ email không phân biệt chữ hoa chữ thường”
https://thecopenhagenbook.com/email-verification
Theo tiêu chuẩn email, địa chỉ email có phân biệt chữ hoa chữ thường
Nếu chuyển email thành chữ thường trong quá trình gửi, thư có thể đến nhầm người, và điều đó nguy hiểm trong xác thực
Nhiều nhà cung cấp email nổi tiếng chọn không phân biệt chữ hoa chữ thường, nhưng nếu là một trang xử lý xác thực nói chung thì nên khuyến nghị trường hợp tổng quát
https://stackoverflow.com/questions/9807909/are-email-addres...
Nói thêm, không phải lúc nào cũng rõ dạng chữ hoa/chữ thường đối ứng của một ký tự là gì, và điều đó cũng có thể thay đổi theo thời gian. Ví dụ chữ ß viết hoa trong tiếng Đức được thêm vào Unicode năm 2008, nên trong lập trình nói chung, nếu có thể thì tốt hơn nên tránh phân biệt chữ hoa chữ thường
Tiêu chuẩn nói một đằng nhưng triển khai lại hoạt động một nẻo, và trong trường hợp này nếu chỉ bám vào câu chữ của tiêu chuẩn thì sẽ gây vấn đề trong thế giới thực
Như vậy email sẽ được gửi đến địa chỉ với cách viết hoa thường như lần đầu nhập, còn đăng nhập thì có thể thực hiện bất kể hoa thường
Nhược điểm là không thể có hai người dùng riêng biệt với email chỉ khác nhau về chữ hoa/chữ thường, nhưng tôi cho rằng như vậy cũng ổn
Email không phân biệt chữ hoa chữ thường, nhưng tên người dùng được tạo từ email lại phân biệt chữ hoa chữ thường, nên nếu tạo tài khoản bằng email có chữ hoa, bạn phải nhập đúng y như vậy mới đăng nhập được, còn nhập email theo kiểu không phân biệt hoa thường thì không đăng nhập được
Sau đó nếu xóa luôn khác biệt ngôn ngữ và văn hóa thì hàng chục tỷ dòng code sẽ biến mất, tôi đã nghe thấy tiếng kho lưu trữ nhỏ lại rồi
Ví dụ một bản ghi người dùng có JohnDoe@example.com, bản ghi khác có johndoe@example.com, trong khi rõ ràng là cùng một người
Hơn nữa, vì các giá trị đến từ các hệ thống khác nhau nên còn rắc rối hơn
Tôi cho rằng ma trận rủi ro của email không phân biệt chữ hoa chữ thường tốt hơn nhiều so với email có phân biệt chữ hoa chữ thường. Nói cách khác, chuyển mọi email thành chữ thường là đúng, và The Copenhagen Book cũng đúng ở điểm này
Rất hay. Tôi luôn không hài lòng với việc 90% tài liệu bảo mật dường như được viết theo cách mà nếu không phải chuyên gia bảo mật thì không thể hiểu nổi. Đặc biệt là tài liệu mật mã học
Nhưng hầu như mọi trang ở đây đều rõ ràng, ngắn gọn, đi thẳng vào trọng tâm và có thể áp dụng ngay, nên tôi rất thích
Tuy nhiên trang về đường cong elliptic vẫn khó hiểu như các tài liệu mật mã học khác
Bản thân nguyên lý hoạt động là toán học, nên tôi nghĩ nó vốn dĩ khó nhìn thấu
Sau khi hiểu trường hữu hạn, đường cong elliptic, và về cơ bản là các nhóm số nguyên, tôi đã có thể nắm được nhiều phần của mật mã học, và phần lớn thường là một dạng nào đó của bài toán logarit rời rạc
Sẽ tốt hơn nếu có thêm các tài liệu thay thế về chủ đề tương tự. Ví dụ như OWASP Cheatsheet, nhưng là tài liệu theo góc nhìn thực tiễn hơn
Tôi tôn trọng nó, nhưng hơi hoài nghi về tài liệu này
Cái tên khá hoành tráng. Gọi tài liệu như thể do các nhà nghiên cứu đại học ở Copenhagen viết là một chiêu marketing rất hay
Đúng là Lucia là một thư viện tương đối phổ biến, nhưng điều đó không có nghĩa là nó quảng bá best practice, hay tác giả nên được xem là người có thẩm quyền trong lĩnh vực quan trọng này
Tôi cũng không thích một số phần trong thiết kế của Lucia. Khi token của người dùng sắp hết hạn, nó gợi ý kéo dài thời gian sống của token hiện tại thay vì tạo token bảo mật mới
Việc token về cơ bản sống mãi và có thể tiếp tục bị lạm dụng trông rất không an toàn, và vi phạm best practice bảo mật về vòng đời token có giới hạn
Cả Lucia và “Copenhagen Book” đều khuyến nghị cách này: https://thecopenhagenbook.com/sessions#session-lifetime
Đây là cách làm phổ biến, thường được gọi là rolling session
Bản thân token ngay từ đầu nên là bất biến, nên không thể thay đổi vòng đời của nó; vì vậy việc làm mới token vốn là cấp một token mới, chứ không phải sửa token ban đầu
Ở trường hợp thứ nhất, kẻ tấn công đánh cắp token và dùng mãi mãi. Ở trường hợp thứ hai, nếu kẻ tấn công đánh cắp token rồi nhận token mới ngay trước khi hết hạn, họ vẫn có thể tiếp tục mạo danh người dùng
Nếu người dùng bị đá ra ngoài thì có thể họ sẽ nhận ra điều gì đó, nhưng khả năng thấp; và để có trải nghiệm người dùng tốt thì dù sao cũng cần một khoảng gia hạn. Nếu không, người dùng hợp lệ cũng sẽ gặp vấn đề với các yêu cầu song song
Có thể dùng token thứ hai, tức refresh token, nhưng như vậy chỉ chuyển rủi ro sang token đó. Lúc này bạn phải lo refresh token bị đánh cắp và bị lạm dụng mãi mãi
Refresh token hữu ích ở chỗ không cần chạm cơ sở dữ liệu ở mọi yêu cầu. Thông thường, session token có vòng đời ngắn có thể được xác thực không cần cơ sở dữ liệu, chẳng hạn như JWT đã ký
Nhưng khi bị đánh cắp thì không thể vô hiệu hóa và vẫn hợp lệ cho đến khi hết hạn, nên để giảm thiệt hại thì thời gian hết hạn phải ngắn
Ngược lại, refresh token sẽ truy vấn cơ sở dữ liệu. Bản thân token thứ hai không tăng thêm bảo mật; chính việc truy vấn cơ sở dữ liệu mới tăng bảo mật. Vì có thể xóa nó khỏi cơ sở dữ liệu để vô hiệu hóa
Lucia, ít nhất theo các ví dụ, luôn truy vấn cơ sở dữ liệu nên có thể vô hiệu hóa token bất cứ lúc nào
Để giảm rủi ro, nên cho phép người dùng xem và kết thúc các phiên đang hoạt động. Nếu có thể, nên hiển thị cả thời gian, vị trí và thông tin thiết bị. Ví dụ: “Đăng nhập lúc 12 giờ sáng hôm qua từ iPhone ở Copenhagen”
Cũng có thể thông báo cho người dùng khi có đăng nhập từ vị trí hoặc thiết bị mới
Rốt cuộc, không có cách nào triển khai phiên tồn tại lâu dài mà hoàn toàn an toàn
Tôi nghĩ đó là một nhận xét hay, nhưng không thể nói tôi là chuyên gia
Có hai điều trong OAuth mà mọi người đều bỏ lỡ, và chúng không dễ thấy
Tôi rất vui vì có người đã chỉ ra một trong số đó. Khi dùng token, bắt buộc phải dùng transaction như một cơ chế khóa phân tán
Với refresh token, tầm quan trọng này tăng gấp đôi, gấp bốn. Nếu dùng cùng một token từ hai lần trở lên, người dùng đó sẽ bị đăng xuất
Không quan trọng hệ thống chạy trên một máy hay N máy. Nếu có từ hai yêu cầu trở lên kèm refresh token đang diễn ra đồng thời — điều này xảy ra rất thường xuyên — nó sẽ đăng xuất người dùng và thường kết thúc bằng lỗi 500
Refresh token là dùng một lần
Điều còn lại mà các lập trình viên và framework xác thực bỏ lỡ là tham số “state”
Nhìn vào hướng phát triển chung của xác thực hiện nay, có vẻ nó đang tiến gần đến sự bất ổn do mơ hồ, chứ không phải bảo mật nhờ mơ hồ
Khi cả trạng thái ứng dụng cũng bị cuốn vào, bạn phải điều chỉnh logic ứng dụng theo xác thực, và ứng dụng phải kiểm tra xem có ai đó đã đánh cắp refresh token hay không
Chỉ transaction thôi không giải quyết được. Ví dụ, nếu mở nhanh hai tab, bạn sẽ đánh vào máy chủ hai lần bằng refresh token ban đầu
Tài liệu đó ưu tiên refresh token rotation, nhưng cũng đề cập đến khó khăn hiển nhiên trong môi trường cluster: https://datatracker.ietf.org/doc/html/rfc6819#section-5.2.2....
Nhiều yêu cầu luôn xảy ra đồng thời. Ví dụ, trình duyệt khởi động với nhiều tab, hoặc ứng dụng smartphone khởi động và gửi nhiều yêu cầu cùng lúc là chuyện phổ biến
Mong rằng nhiều trang web hơn sẽ đưa ra lựa chọn “hãy để phiên không hết hạn cho đến khi tôi đăng xuất, tôi hiểu rủi ro”
Dạo này nút “remember me” chẳng có tác dụng gì
Cứ bị ngắt quãng cả ngày vì phiên hết hạn, thật là bực. Tôi đặc biệt ghét GitHub: vì chỉ dùng khoảng mỗi tuần một lần nên phiên luôn hết hạn, lại còn bắt buộc xác thực 2 bước, lần nào cũng phải lấy điện thoại ra nhập số
Trải nghiệm người dùng đã tệ, nhưng dù không có bằng chứng, tôi nghĩ việc liên tục bắt đăng nhập như vậy khiến người dùng mệt mỏi và kém chú ý hơn
Nếu phải đăng nhập vào một trang nhiều lần mỗi tuần, đến lần đăng nhập thứ 60 thì một trang phishing chen vào sẽ dễ hơn. Ngược lại, với một tài khoản hầu như không bao giờ phải đăng nhập, nếu đột nhiên bị yêu cầu mật khẩu thì sẽ thấy bất thường và cảnh giác hơn
Cuối cùng, các công ty cần hiểu rằng mỗi người có mức chấp nhận rủi ro và tư thế bảo mật khác nhau, rồi cung cấp lựa chọn
Nói thêm, việc GitHub thường xuyên hết hạn phiên và bắt xác thực 2 bước quá khó chịu nên tôi đã chuyển sang Gitea và tắt hết hạn phiên. 90% lý do chuyển là vì chuyện đó
Vì chỉ có thể truy cập từ mạng của tôi nên tôi cảm thấy bảo mật thậm chí còn tốt hơn. Việc một công ty mất khách hàng vì mô hình bảo mật thiếu linh hoạt là hoàn toàn có thể xảy ra
Đại khái là nếu phiên được dùng trong vòng 30 ngày thì cho phiên tiếp tục thêm 30 ngày nữa
https://thecopenhagenbook.com/sessions#session-lifetime
Tệ hơn nữa là phiên có vẻ kéo dài hơn một tuần khoảng 1–2 phút, nên tuần này tôi bị đá ra ngay sau khi bắt đầu công việc từ tuần trước
Vài tuần trước tôi đăng nhập để ghi chú ngay trước một cuộc họp định kỳ, và trong nhiều tuần liên tiếp tôi bị buộc đăng nhập giữa chừng cuộc họp đó
Có rất nhiều cài đặt có thể vô hiệu hóa kiểu nút “remember me” đó
Cá nhân tôi không gặp vấn đề gì với việc duy trì đăng nhập trên các trang web, bao gồm cả GitHub
Gần đây tôi mới biết đến giao thức SRP, và khá ngạc nhiên là nó không được dùng hay nhắc đến rộng rãi hơn
Đây là một giao thức tương đối đơn giản, có thể thực hiện chứng minh không tiết lộ thông tin và đồng thời tạo token phiên giữa máy chủ và client
https://en.wikipedia.org/wiki/Secure_Remote_Password_protoco...
Tôi thích tài liệu này. Nhiều lời khuyên về bảo mật khó hiểu và đôi khi nghe vô lý. Giống như luật sư khuyên đừng làm gì cả
Hướng dẫn này hay ở chỗ ngắn gọn một cách sảng khoái, dễ theo dõi, dễ hiểu và có lời khuyên thẳng thắn
Tôi sẽ tiếp tục xem bình luận để biết có ý kiến phản biện hay điểm cần lưu ý nào không, nhưng có lẽ tôi sẽ dùng nó để đối chiếu với dự án xác thực của mình
Có một điều tôi mong muốn là có mục JWT. Nếu ý kiến của tác giả là “đừng dùng” thì chỉ cần nói vậy cũng được
Một trong những điều tôi thường phê bình các nhóm phụ trách bảo mật là họ dành nhiều thời gian nói những gì không được làm, thay vì chủ động cung cấp công cụ hay cách thức để mọi người làm hiệu quả những việc họ muốn làm
Email có thể được xem là hệ thống an toàn hơn, nên không nhất thiết lúc nào cũng truy cập được ngay lập tức hoặc ở mọi nơi
Tôi thắc mắc “auth” ở đây nghĩa là xác thực (authn) hay phân quyền (authz)
Nhìn thì có vẻ là xác thực, nhưng sẽ tốt hơn nếu làm rõ
Một lời phàn nàn ngắn hơi liên quan: trình duyệt nhúng đang phá hỏng web
Trình duyệt nhúng khiến việc dùng OAuth xã hội trở nên bất khả thi. Trong một số trường hợp là bất khả thi theo đúng nghĩa đen, còn trong các trường hợp khác thì gần như bất khả thi
Khi nhấn một liên kết trong Instagram, mặc định nó mở trong trình duyệt của Instagram; nếu liên kết đó có “Sign in with Google” thì Google sẽ chặn “trình duyệt không an toàn” như Instagram, nên nó không hoạt động
Ngay cả “Sign in with Facebook” cũng gặp vấn đề, dù Meta sở hữu cả Instagram lẫn Facebook. Trình duyệt nhúng của Facebook cũng có vấn đề tương tự
Việc nhấn liên kết ở nơi như Slack, rồi trả lời tin nhắn, sau đó quay lại trình duyệt và mong trang đó vẫn còn, nhưng Slack đã nuốt nó vào trong trình duyệt của chính mình khiến nó chẳng ở đâu cả, hầu như luôn vô dụng
May là tôi vừa kiểm tra và thấy Slack có cách tắt trình duyệt nhúng