Tại sao việc kiểm thử đặt lại mật khẩu bị xem nhẹ
Đặt lại mật khẩu là một trong những luồng bị tấn công nhiều nhất trong bất kỳ ứng dụng nào — và một cách nghịch lý, lại là một trong những luồng ít được kiểm thử nhất. Lý do rất đơn giản: lập trình viên dùng chính địa chỉ email của mình trong quá trình phát triển. Sau lần chạy thử thứ ba hoặc thứ tư, hộp thư của bạn bị chôn vùi trong các thông điệp "Đặt lại mật khẩu của bạn" trông giống hệt nhau. Các dòng tiêu đề gộp chồng lên nhau, bạn không còn theo dõi được liên kết nào thuộc lần chạy nào, và cuối cùng việc kiểm thử trở nên quá phiền phức để làm một cách kỹ lưỡng. Bạn bắt đầu dựa vào giả định rằng nó hoạt động vì lần trước nó đã hoạt động. Đó chính xác là kiểu tự mãn khiến những lỗi nghiêm trọng lọt vào môi trường sản xuất.
Rủi ro rất cao. Đặt lại mật khẩu là cơ chế chính mà người dùng dùng để khôi phục tài khoản — và cũng là cơ chế mà kẻ tấn công dùng để cố chiếm đoạt chúng. Một token có lỗi không hết hạn, một liên kết có thể tái sử dụng, hoặc một điểm cuối (endpoint) đặt lại không giới hạn tần suất có thể biến một vụ rò rỉ thông tin đăng nhập nhỏ thành một cuộc chiếm đoạt tài khoản toàn diện. Theo dữ liệu được lập chỉ mục bởi Have I Been Pwned, hàng tỷ thông tin đăng nhập từ các vụ rò rỉ cũ đang lưu hành một cách chủ động, và kẻ tấn công thường xuyên thử đặt lại mật khẩu đối với các tài khoản chúng phát hiện. Nếu luồng đặt lại của bạn có điểm yếu, chúng sẽ tìm ra.
Những gì bạn thực sự cần kiểm thử trong một luồng đặt lại mật khẩu
Một bài kiểm thử sơ bộ (smoke test) cơ bản kiểu "nó có gửi email không" là chưa đủ. OWASP Authentication Cheat Sheet phác thảo một bộ yêu cầu toàn diện cho việc đặt lại mật khẩu an toàn, và mỗi yêu cầu đều đáng được kiểm thử riêng. Đây là danh sách đầy đủ những gì bạn thực sự nên xác minh:
- Việc gửi email — email đặt lại có đến không, và nó có đến kịp thời không? Một email đặt lại mất 10 phút sẽ khiến người dùng bối rối và làm tăng số phiếu hỗ trợ.
- Tính đúng đắn của liên kết — liên kết trong email có dẫn đến đúng trang với đúng token trong URL hoặc nội dung không?
- Hết hạn token — nếu bạn chờ 25 giờ rồi nhấp vào liên kết, ứng dụng có từ chối token đã hết hạn một cách đúng đắn không? Hãy kiểm thử điều này một cách tường minh, không phải trên lý thuyết.
- Thực thi dùng một lần — bạn có thể nhấp cùng một liên kết đặt lại hai lần không? Sau khi đổi mật khẩu thành công, token phải bị vô hiệu hóa. Đây là một yêu cầu bắt buộc theo OWASP, và nó thường bị bỏ qua.
- Vô hiệu hóa khi yêu cầu lại — nếu một người dùng yêu cầu đặt lại, rồi yêu cầu đặt lại lần nữa hai phút sau, token đầu tiên có bị vô hiệu hóa không? Cả hai token cùng hợp lệ đồng thời là một lỗ hổng bảo mật.
- Xử lý tài khoản SSO — điều gì xảy ra khi một người dùng đã đăng ký qua Google, GitHub hoặc một nhà cung cấp OAuth khác yêu cầu đặt lại mật khẩu? Luồng này thường bị hỏng vì tài khoản không có mật khẩu cục bộ nào để đặt lại.
- Thực thi HTTPS — liên kết đặt lại có dùng HTTPS không? Một liên kết đặt lại qua HTTP thuần làm lộ token trước sự chặn bắt trên mạng.
- Chất lượng thông báo lỗi — khi một liên kết hết hạn, ứng dụng có hiển thị một thông báo rõ ràng, hữu ích, hay một lỗi 500 chung chung? Trải nghiệm người dùng ở đây rất quan trọng.
- Giới hạn tần suất — điều gì xảy ra nếu ai đó gửi 10 yêu cầu đặt lại cho cùng một địa chỉ trong một phút? Nên có một giới hạn hợp lý để ngăn việc liệt kê (enumeration) và lạm dụng.
- Ngăn liệt kê email — phản hồi có khác nhau tùy theo việc địa chỉ email có tồn tại trong hệ thống hay không? Một phản hồi khác biệt là một sự rò rỉ thông tin cho phép kẻ tấn công liệt kê các tài khoản hợp lệ.
Cách tiếp cận email tạm thời — hướng dẫn từng bước
Giải pháp gọn gàng nhất cho tất cả những thách thức kiểm thử này là một địa chỉ email tạm thời mới cho mỗi lần chạy thử. Đây chính xác là cách nó hoạt động trong thực tế.
Mở một hộp thư tạm thời, sao chép địa chỉ hiển thị ở trên cùng, và vào ứng dụng của bạn. Đăng ký một tài khoản thử nghiệm mới bằng địa chỉ đó. Đi tới trang đăng nhập và nhấp vào "Quên mật khẩu". Nhập địa chỉ và gửi yêu cầu. Chuyển trở lại hộp thư tạm thời — email đặt lại đến theo thời gian thực, thường trong vài giây. Bạn có thể xem toàn bộ email, kiểm tra dòng tiêu đề và chi tiết người gửi, nhấp vào liên kết, xác minh rằng nó dẫn đến đúng trang, đặt một mật khẩu mới, và xác nhận việc đăng nhập hoạt động. Tổng thời gian từ đầu đến cuối: dưới hai phút. Khi bạn cần kiểm thử một kịch bản thứ hai, hãy mở một tab trình duyệt mới — bạn có được một hộp thư hoàn toàn độc lập với một địa chỉ khác. Không dọn dẹp, không lẫn lộn phân luồng, không có rủi ro vô tình nhấp nhầm liên kết từ một lần chạy trước.
Một ví dụ hướng dẫn thực tế: kiểm thử trước một bản phát hành
Tôi đang chuẩn bị một ứng dụng SaaS cho một bản phát hành nhỏ bao gồm việc cập nhật thư viện xác thực. Luồng đặt lại mật khẩu không được thay đổi một cách tường minh, nhưng các bản cập nhật thư viện xác thực có thói quen âm thầm làm hỏng việc tạo token email. Đây là toàn bộ trình tự tôi đã chạy qua.
Tôi mở năm tab trình duyệt, mỗi tab với một hộp thư tạm thời độc lập. Tab một: luồng lý tưởng — đăng ký, yêu cầu đặt lại, dùng liên kết trong vòng hai phút, xác nhận đăng nhập. Tab hai: token hết hạn — đăng ký, yêu cầu đặt lại, chờ email đến, để riêng ra trong 25 giờ (tôi quay lại vào ngày hôm sau), rồi thử liên kết. Ứng dụng đã từ chối nó một cách đúng đắn. Tab ba: đặt lại kép — đăng ký, yêu cầu đặt lại, ngay lập tức yêu cầu đặt lại lần nữa, rồi thử cả hai liên kết. Liên kết đầu tiên lẽ ra phải bị vô hiệu hóa; và đúng là vậy. Tab bốn: tái sử dụng liên kết đã dùng — đăng ký, yêu cầu đặt lại, dùng liên kết để đổi mật khẩu thành công, rồi thử cùng liên kết đó lần thứ hai. Bị từ chối một cách đúng đắn. Tab năm: giới hạn tần suất — kích hoạt các yêu cầu đặt lại một cách nhanh chóng để xác minh bộ giới hạn tần suất đang hoạt động.
Mỗi kịch bản dùng một hộp thư sạch, độc lập. Không có sự mơ hồ nào về việc email nào thuộc bài kiểm thử nào. Bản cập nhật thư viện xác thực không làm hỏng gì cả, và tôi đã có bằng chứng được ghi chép. Toàn bộ lần chạy thử mất khoảng 30 phút bao gồm cả lần kiểm tra token hết hạn qua đêm.
Kiểm thử các trường hợp biên với nhiều hộp thư tạm thời cùng lúc
Mỗi tab trình duyệt trên một dịch vụ email tạm thời là một hộp thư độc lập với địa chỉ duy nhất của riêng nó. Điều này làm cho việc kiểm thử song song trở nên đơn giản. Mở ba tab và bạn có ba địa chỉ duy nhất. Đăng ký ba tài khoản thử nghiệm, kích hoạt đặt lại mật khẩu cho cả ba cùng lúc, và xác minh rằng mỗi tài khoản chỉ nhận token của riêng nó — không phải của người khác. Bài kiểm thử nhiễm chéo này bắt được một lỗi đặc biệt khó chịu, nơi một hệ thống đặt lại được triển khai kém gửi tất cả các token đến địa chỉ được đăng ký đầu tiên, hoặc đến một địa chỉ được mã hóa cứng (hardcode) trong một môi trường bị cấu hình sai.
Bạn cũng có thể kiểm thử điều gì xảy ra khi một người dùng yêu cầu đặt lại trong khi đã đăng nhập, hoặc điều gì xảy ra khi một yêu cầu đặt lại được thực hiện cho một địa chỉ email không tồn tại trong hệ thống. Mỗi trường hợp biên này đều có hộp thư sạch của riêng nó, trạng thái sạch của riêng nó, và tạo ra những kết quả rõ ràng, không mơ hồ.
Danh sách kiểm tra bảo mật token
Token đặt lại mật khẩu là một trong những bề mặt tấn công phổ biến nhất trong các ứng dụng web. OWASP nói rất rõ về những gì một cách triển khai an toàn đòi hỏi, và tiêu chuẩn cao hơn nhiều đội nhóm nhận ra. Mọi mục trong danh sách này đều phải kiểm chứng được thông qua các bài kiểm thử của bạn:
- Tối thiểu 32 ký tự, ngẫu nhiên về mặt mật mã — các token ngắn hoặc dễ đoán có thể bị phá bằng brute-force. Hãy dùng bộ tạo số ngẫu nhiên an toàn về mặt mật mã của nền tảng của bạn, không phải Math.random() hay các thứ tương đương.
- Hết hạn trong vòng 24 giờ, lý tưởng là 1 giờ — một token không bao giờ hết hạn là một bề mặt tấn công dai dẳng. Một giờ là mức tối đa được khuyến nghị cho hầu hết các ứng dụng.
- Chỉ dùng một lần — token phải bị vô hiệu hóa ngay khoảnh khắc nó được sử dụng. Một token đặt lại có thể tái sử dụng là một lỗ hổng nghiêm trọng.
- Bị vô hiệu hóa khi có một yêu cầu đặt lại mới — nếu người dùng yêu cầu đặt lại lần nữa, tất cả các token còn hiệu lực trước đó cho tài khoản đó phải bị hủy bỏ.
- Giới hạn tần suất theo từng địa chỉ email — ngăn chặn việc liệt kê và lạm dụng tự động bằng cách giới hạn số yêu cầu đặt lại có thể được thực hiện cho mỗi địa chỉ trong mỗi khoảng thời gian.
- Không bao giờ ghi log ở dạng văn bản thuần — nếu hạ tầng ghi log của bạn thu thập các tham số yêu cầu, hãy đảm bảo các token đặt lại được loại trừ hoặc băm (hash) trước khi ghi log.
Bản thân email đặt lại nên trông như thế nào
Nội dung và cách trình bày của email đặt lại quan trọng hơn hầu hết các đội nhóm nhận thức được. Một email đặt lại được soạn tốt thì đơn giản và chức năng: một dòng tiêu đề rõ ràng ("Đặt lại mật khẩu của bạn"), một nút hoặc liên kết nổi bật duy nhất, một tuyên bố hết hạn rõ ràng ("Liên kết này hết hạn trong 1 giờ"), và một ghi chú rằng nếu người dùng không yêu cầu điều này, họ có thể bỏ qua email một cách an toàn. Không có lời quảng cáo tiếp thị, không có biểu tượng mạng xã hội, không có chân trang bản tin. Một email giao dịch nên trông giống email giao dịch.
Chi tiết người gửi cũng quan trọng. Tên người gửi nên phản ánh thương hiệu của bạn một cách rõ ràng, và địa chỉ người gửi nên được xác thực đúng cách. Một email đến với tên người gửi không khớp hoặc rơi vào thư mục spam do cấu hình xác thực kém sẽ gây ra sự bối rối thực sự cho người dùng và gánh nặng hỗ trợ. Hãy kiểm tra cấu hình SPF, DKIM và DMARC của tên miền của bạn bằng một công cụ như MXToolbox, và đọc kỹ hướng dẫn xác thực email nếu bất kỳ thuật ngữ nào trong số đó còn xa lạ với bạn.
Đặc tả kỹ thuật của email — cái gì là và không phải là một email hợp lệ, việc gửi hoạt động từ đầu đến cuối ra sao — được ghi chép trong RFC 5321. Đây là tài liệu đọc khá nặng, nhưng các phần tổng quan là bối cảnh hữu ích để hiểu hạ tầng email của bạn thực sự đang làm gì khi nó gửi đi một email đặt lại.
Tại sao hộp thư thật của bạn là công cụ sai cho việc này
Dùng địa chỉ email cá nhân hoặc công việc của bạn cho các tài khoản thử nghiệm tạo ra một loạt vấn đề vượt ngoài sự bất tiện. Địa chỉ của bạn rốt cuộc nằm trong cơ sở dữ liệu của chính ứng dụng của bạn dưới dạng một bản ghi thử nghiệm. Nó có thể xuất hiện trong log ứng dụng, trong lịch sử đã gửi của máy chủ thư của bạn, trong các bản xuất môi trường staging, và đôi khi trong các bản trích xuất cơ sở dữ liệu được chia sẻ với các nhà thầu hoặc đội QA bên ngoài. Môi trường staging thường có các biện pháp kiểm soát truy cập lỏng lẻo hơn môi trường sản xuất. Electronic Frontier Foundation ủng hộ việc tối thiểu hóa dữ liệu như một nguyên tắc riêng tư nền tảng — giữ địa chỉ thật của bạn nằm ngoài các hệ thống phát triển và thử nghiệm là một sự áp dụng trực tiếp của nguyên tắc đó. Một hộp thư tạm thời hết hạn một cách tự nhiên, không bao giờ gắn với danh tính của bạn, và không để lại dấu vết.
Đưa việc đặt lại mật khẩu vào bộ kiểm thử hồi quy của bạn
Đặt lại mật khẩu là kiểu luồng âm thầm hỏng khi các thư viện xác thực được cập nhật, khi các nhà cung cấp email được xoay vòng, hoặc khi các khóa API được làm mới. Nó hiếm khi có các bài kiểm thử tự động chuyên biệt vì hầu hết các đội nhóm coi nó như một bài kiểm thử tích hợp chỉ-giao-diện khó tự động hóa. Lập luận đó có thể hiểu được nhưng nguy hiểm.
Ở mức tối thiểu, hãy cân nhắc thêm một bài kiểm thử đầu-cuối cơ bản trong môi trường staging hoặc CI của bạn: tạo một tài khoản thử nghiệm bằng lập trình với một địa chỉ được tạo ra, kích hoạt một yêu cầu đặt lại, chặn bắt hoặc kiểm tra email đi ra trực tiếp từ API của dịch vụ thư của bạn, trích xuất token, thử sử dụng, và xác minh trạng thái kết quả. Việc này không cần phải cầu kỳ. Ngay cả một lần kiểm tra tự động duy nhất xác nhận rằng luồng đặt lại còn hoạt động sau mỗi lần triển khai cũng sẽ bắt được lớp hồi quy phổ biến nhất: những thay đổi phụ thuộc xác thực âm thầm làm hỏng việc tạo token.
Tài nguyên bổ sung cho việc xác thực an toàn
Để có một góc nhìn rộng hơn về lý do tại sao việc xử lý mật khẩu an toàn quan trọng trong thực tế, Troy Hunt trình bày phân tích các vụ rò rỉ thực tế một cách dễ tiếp cận và có chứng cứ vững chắc. Những bài viết của ông về nhồi thông tin đăng nhập (credential stuffing) và chiếm đoạt tài khoản liên quan trực tiếp đến lý do tại sao luồng đặt lại xứng đáng được chú ý nghiêm túc. OWASP Authentication Cheat Sheet vẫn là tài liệu tham khảo đơn lẻ toàn diện nhất cho mọi thứ mà hệ thống xác thực của bạn nên làm. Giữa hai tài nguyên đó và một thực hành kiểm thử có kỷ luật dùng hộp thư mới cho mỗi lần chạy, bạn có được nền tảng cho một hệ thống xác thực sẽ đứng vững dưới sự soi xét của thực tế.