Blog này đã nhiều tháng lặp lại một điều: mật khẩu của bạn không nên rời khỏi
trình duyệt. Trình tạo
mật khẩu lấy tính ngẫu nhiên từ crypto.getRandomValues() ngay trên máy
của bạn, còn trình kiểm tra phân tích những gì bạn gõ mà không
gửi đi đâu cả.
Và hôm nay chúng tôi công bố một API tạo mật khẩu trên máy chủ.
Mâu thuẫn là rõ ràng và chúng tôi sẽ không che đậy: mỗi phản hồi của API đều
mang một lời cảnh báo nói đúng điều đó. Nhưng có những chỗ không có trình duyệt
— một script tạo một trăm tài khoản, một máy chủ sinh thông tin đăng nhập tạm
thời, một trợ lý bị ai đó hỏi xin mật khẩu và tự bịa ra — và ở đó, lựa chọn thay
thế không phải là «trình duyệt»: đó là một random() chọn sai hoặc một chuỗi gõ
bằng tay. API này sinh ra cho việc đó.
Toàn bộ lệnh gọi
Không khóa, không đăng ký, không tiêu đề. Chỉ có vậy:
curl -X POST https://api.password.es/v1/generate
Nó trả về một mật khẩu 16 ký tự cùng phần phân tích. Chạy trong lúc viết đoạn
này: x*7$9,BUy9PPvOI?, lấy từ bảng chữ cái 89 ký tự, 103,6 bit entropy và
thời gian bẻ khóa 2,5 × 10¹¹ năm. Mức 4 trên 4.
Không con số nào trong đó là mới. Chúng đến từ đúng bộ máy vẽ thanh đo ở trang chủ: cùng công thức bit, cùng mô hình tấn công — 10¹² lần đoán mỗi giây, ngoại tuyến, hàm băm nhanh — và cùng thang mức độ với trình kiểm tra. Nếu trang web và API cho ra những con số khác nhau cho cùng một mật khẩu thì một trong hai đang nói dối.
Nó trả về gì, từng trường một
Phản hồi mang passwords — luôn là một mảng, kể cả khi bạn chỉ xin một — và một
khối analysis gồm length, pool, bits, log10_guesses,
crack_time_log10_seconds, crack_time, level, level_scale và ceiling.
Hai trong số đó đáng một đoạn riêng. level_scale cho biết mức độ đến từ đâu
(hiện luôn là "time"), để nếu sau này trang web và API lệch nhau thì điều đó
hiện ra ngay ở trường dữ liệu thay vì phải suy ra bằng cách so sánh các con số.
Còn ceiling cho biết con số là chính xác hay chỉ là mức trần: trong
generate nó luôn là false, vì mật khẩu do máy chủ tạo ra và nó biết dùng
bảng chữ cái nào; ở trình kiểm tra trên web thì không phải lúc nào cũng vậy, vì
ở đó mật khẩu là do bạn mang tới.
Sau đó là notice, lời cảnh báo, và một khối _meta với gói dịch vụ, ngôn ngữ
đã áp dụng, các giới hạn và một liên kết về tài liệu.
Các tùy chọn
Tất cả đều không bắt buộc, và tên để nguyên tiếng Anh vì người tích hợp một API
sẽ gõ y hệt tên trường: length (4–64, mặc định 16), count (1–20), lower,
upper, digits, symbols, exclude_ambiguous, no_repeats và lang.
curl -X POST https://api.password.es/v1/generate -H 'content-type: application/json' -d '{"length":20,"exclude_ambiguous":true}'
Hai chi tiết mà tài liệu có nói và nên biết trước khi tích hợp.
exclude_ambiguous bỏ sáu ký tự chứ không phải bảy: nó loại 0 O 1 I l | o,
nhưng dấu gạch đứng vốn không có trong bộ ký hiệu ngay từ đầu, nên bảng chữ cái
giảm từ 89 xuống 83 chứ không phải 82. Vì vậy phản hồi ghi pool là 83. Và
no_repeats không phải là bảo đảm tuyệt đối: nó thử lại mười lần, y như
trình tạo trên web, và ở 64 ký tự thì một cặp lặp liền kề vẫn lọt qua khoảng
0,1 % số lần. Nói ra như vậy hữu ích hơn là hứa điều ngược lại.
Ngôn ngữ
Mặc định nó trả lời bằng tiếng Anh, đúng như người tích hợp mà không nói gì sẽ
mong đợi. Đổi bằng ?lang= trên URL, bằng "lang" trong thân yêu cầu hoặc bằng
tiêu đề Accept-Language, và nếu mâu thuẫn thì cái đầu tiên thắng. Một ngôn ngữ
không tồn tại không phải là lỗi: nó rơi về tiếng Anh.
Ở đây có một sự bất đối xứng không thể đoán ra, và vì thế API tự khai báo:
thông điệp có tiếng Anh và tiếng Tây Ban Nha; còn liên kết thì có đủ mười tám
ngôn ngữ của trang. Xin tiếng Đức sẽ cho bạn liên kết tiếng Đức và thông điệp
vẫn bằng tiếng Anh. Không cần phải đoán: _meta.lang mang
{ "messages", "links" } cho biết mỗi nửa đã được áp dụng gì.
Máy chủ MCP
MCP là giao thức để một trợ lý — Claude, ChatGPT — dùng công cụ bên ngoài.
https://api.password.es/mcp là một máy chủ MCP không đăng ký và không khóa, và
nó công bố đúng một công cụ: generate_password, với các tham số như trên.
Đây là phần chúng tôi quan tâm nhất, và không phải vì lý do kỹ thuật: một trợ
lý không có chỗ nào để lấy mật khẩu sẽ tự bịa ra, và thứ ra đời từ đó không hề
ngẫu nhiên, nó là thứ mà mô hình cho là trông giống mật khẩu. Khi nối máy chủ
này, trợ lý thôi ứng biến và trả về một mật khẩu tạo bằng
crypto.getRandomValues(), kèm phân tích và kèm lời cảnh báo nằm trong mô tả
công cụ lẫn trong từng kết quả. Chính lời cảnh báo đó là thứ trợ lý cuối cùng
đọc lại cho người đã hỏi xin mật khẩu, và đó là lý do nó có mặt.
Nếu bạn đến từ các máy chủ MCP khác, một điều thôi: máy chủ này không có trạng
thái. POST được trả lời bằng JSON và không mở luồng nào, Mcp-Session-Id
không được phát ra cũng không được chờ đợi, một thông báo nhận về 202 không có
thân, và GET trả về 405. Đặc tả 2025-06-18 cho phép điều đó một cách rõ ràng,
nhưng ai chờ phiên làm việc và SSE sẽ gỡ lỗi trong mù mờ nếu không ai nói.
Những gì nó không làm
/v1/check trả về 501. Đó không phải sơ suất hay một endpoint làm dở: nó cố
ý như vậy, và chính phản hồi giải thích vì sao. Trả về đúng những con số của
trình kiểm tra đòi hỏi đúng bộ máy nhận dạng mẫu đang chạy trên web, và bộ máy
đó tốn từ 11 ms đến 3,6 giây CPU mỗi yêu cầu tùy vào thứ được gửi lên. Với một
dịch vụ không đăng ký và không khóa, khoảng chênh đó là một quyết định sản phẩm
— đặt trần độ dài ở đâu — và vẫn chưa được chốt. Trong lúc chờ, mã 501 mang theo
checker_url trỏ tới trình kiểm tra trên web bằng đúng ngôn ngữ bạn yêu cầu,
nơi làm đúng việc này mà không gửi gì đi.
Tài khoản cũng chưa có, khóa cũng không, gói cước cũng không. Và vì chúng không tồn tại, trong toàn bộ API không có lấy một liên kết đăng ký nào: kể cả trong lỗi vượt giới hạn, chỗ mà ai cũng đặt một cái.
Các giới hạn
Một cái, và đó là cái thực sự được áp dụng: 60 yêu cầu mỗi phút cho mỗi IP.
Không cần tin trang này: con số đi kèm trong từng phản hồi, ở
_meta.limits.burst. Vượt quá thì nhận 429 kèm Retry-After và các tiêu đề
RateLimit-*.
Trong _meta bạn còn thấy khối quota với các giá trị null. Đó là chỗ dành
sẵn cho khi nào có tài khoản, và nó để trống một cách có chủ ý: một giới hạn
được công bố mà không được thi hành còn tệ hơn là không công bố gì, vì người
tích hợp sẽ tôn trọng nó và lập trình theo một con số chẳng ai đếm.
Và dù vậy, hãy cân nhắc hai lần
Một API tạo mật khẩu, xét cho cùng, là một phản mẫu: mật khẩu đi qua mạng và đi qua một cái máy không phải của bạn. Việc chúng tôi không lưu gì không làm thay đổi hình dạng của vấn đề, chỉ thay đổi phần của chúng tôi trong đó — và bạn đã biết một lời hứa không kiểm chứng được thì đáng giá bao nhiêu.
Vì thế lời cảnh báo không chỉ sống trên trang này: nó đi theo từng phản hồi, bằng ngôn ngữ bạn yêu cầu. Và vì thế đoạn văn này nằm ở đây chứ không giấu ở cuối tài liệu. Với một mật khẩu bạn sẽ tự dùng, trình tạo của trang này chạy hoàn toàn trong trình duyệt của bạn và không gửi gì đi. API là cho việc khác: cho những gì diễn ra khi không có ai ngồi trước màn hình.
Chi tiết nằm ở hai trang. API là gì kể nó dành cho ai, hôm nay cái
gì phản hồi và cái gì không. Tài liệu tham khảo có đủ chín tham
số, mọi trường được giải thích từng cái một, các mã lỗi và các giới hạn; đó là
trang bạn mở cạnh trình soạn thảo. Còn nếu bạn muốn máy đọc nó, đã có
openapi.json.
Nguồn: chính API đó, đã kiểm chứng trên môi trường thật ngày 31 tháng 8 năm
2026 — POST /v1/generate, POST /v1/check (501), POST /mcp và
GET /openapi.json · các con số phân tích đến từ cùng bộ máy với trình tạo và
trình kiểm tra của password.es · mô hình tấn công là 10¹² lần đoán mỗi giây,
ngoại tuyến và với hàm băm nhanh, giống phần còn lại của trang · máy chủ MCP
triển khai đặc tả 2025-06-18 với truyền tải Streamable HTTP không trạng thái.
Ảnh của Stanislav Kondratiev · Pexels