Đăng nhập một lần đã là chuẩn ổn định hơn hai mươi năm, vậy mà SAML vẫn là dòng chi phí làm đình trệ một thương vụ hoặc buộc bạn gọi cho bộ phận bán hàng. Vì sao xác thực doanh nghiệp trở thành món bán thêm cao cấp, ai đã dựng nên thế giới đó và ai đã cảnh báo chúng ta, con hào phòng thủ thực sự rộng đến đâu và được làm bằng gì, cùng một lực đang lặng lẽ rút cạn một nửa con hào ấy.
Giám sát là đoạn mã chỉ chạy trong lúc thảm họa, đúng khoảnh khắc bạn không thể để nó hỏng. Vì vậy chúng tôi dựng một bộ khung kiểm thử, đẩy một tín hiệu thật cho từng quy tắc cảnh báo và chỉ coi là đạt khi thông báo thực sự tới được relay thu nhận. Lần chạy đầu tiên thấy cảnh báo tỷ lệ lỗi chủ lực đã chết hẳn, rồi tệ hơn: một thiết lập lưu giữ âm thầm đóng dấu hết hạn tức thì lên log, khiến cả một lớp cảnh báo đánh giá trên số không.
Một trăm megabyte ảnh chụp màn hình được chuyển sang Cloudflare R2, phía sau một cơ sở CDN mà ứng dụng tự suy ra khi chạy từ tên máy chủ của nó. Trong trình duyệt mọi thứ đều chạy tốt, nhưng lưu lượng máy chủ gốc không thay đổi: các trang mà trình thu thập nhìn thấy được kết xuất trước trên 127.0.0.1, nơi tên máy chủ chẳng nói lên điều gì. Giải pháp: một chuỗi giữ chỗ mà HTML mang ra khỏi bản build và edge điền vào theo từng yêu cầu, do một hệ thống tệp chỉ đọc.
Cấp phát tài khoản tức thời được bật theo mặc định, nên một nhà cung cấp danh tính liên kết có thể tạo tài khoản trong tenant của khách hàng chỉ bằng cách khẳng định rằng ai đó tồn tại. Tắt giá trị mặc định đó là một thay đổi một từ. Tắt nó mà không âm thầm thay đổi hành vi của mọi kết nối đã lưu, những kết nối chưa bao giờ ghi cột này, mới là vấn đề thực sự, và mẹo giải quyết nó cũng giải thích vì sao một giá trị mặc định là một phần của bề mặt API.
Một backend-for-frontend lấy các token OAuth ra khỏi trình duyệt và chỉ để lại cho nó một cookie mờ đục. Phục vụ nhiều tenant từ một BFF chủ yếu là bài toán đấu nối, cho đến khi back-channel logout xuất hiện: một POST từ máy chủ đến máy chủ, không cookie, không phiên, không trình duyệt, nơi thứ duy nhất định danh tenant là một claim nằm bên trong một token mà bạn còn chưa xác minh.
Chúng tôi đã thêm vào cụm Kubernetes một admission webhook kiểm tra chữ ký, để không image nào chưa ký có thể chạy. Vài giờ sau, một Deployment có 2.242 ReplicaSet, cứ ba giây lại sinh ra một cái mới, và không có pod nào sẵn sàng. Log của webhook cho thấy nó chấp nhận mọi yêu cầu. Thiệt hại đến từ đúng một từ trong "mutating admission webhook" mà chúng tôi đã thôi để mắt tới.
Một người dùng quay lại nhìn chữ "Đang tải…" đúng mười giây trước khi trang đăng nhập hiện ra, lần nào cũng vậy. Con số tròn trịa chính là dấu hiệu: thư viện SSO của chúng tôi mở một iframe silent-renew ẩn nhắm vào một auth host phục vụ X-Frame-Options: DENY và frame-ancestors 'none', nên iframe không bao giờ tải xong, không bao giờ báo lỗi, và thư viện chờ hết trọn thời gian timeout. Phòng thủ clickjacking và OIDC silent renew, mỗi thứ đều đúng, treo tại đường nối giữa chúng.
Chúng tôi mã hóa mọi địa chỉ email khi lưu trữ, và ô tìm kiếm quản trị lặng lẽ ngừng tự động gợi ý: không lỗi, không log. Blind index khôi phục tra cứu khớp chính xác trên ciphertext, nhưng nhiệm vụ của HMAC là phá hủy thứ tự mà tìm kiếm theo tiền tố cần. Đây là thiết kế lấy lại tính năng gợi ý (đánh chỉ mục mọi tiền tố như một giá trị riêng) và ba cái bẫy dạy chúng tôi rằng searchable encryption là bài toán hệ thống, không phải bài toán mật mã.
Chúng tôi chuyển việc ký JWT từ RS256 sang ES256, và mọi token chúng tôi phát hành đều hợp lệ về mặt mật mã, nhưng vẫn bị từ chối. Ba lần, vì ba lý do khác nhau: DER ở nơi JWS muốn raw R‖S, phần đệm số nguyên làm hỏng một token trên mỗi 128, và một phương ngữ base64 thay đổi ngay dưới chân chúng tôi. Đây là cách đọc các byte bị làm hỏng và gọi tên tầng thủ phạm chỉ từ độ dài chữ ký.
Hai tài liệu mà một dịch vụ đọc để xác thực một trong các JWT của chúng tôi — tài liệu discovery và JWKS — là những thứ được yêu cầu nhiều nhất và ít bí mật nhất mà chúng tôi phục vụ, thế mà trước đây mỗi lần tải chúng đều phải chạy suốt quãng đường về tận máy chủ gốc. Cache chúng trên Cloudflare là phần dễ.
Gói LITE của Duende có giá $5,750/yr nhưng giới hạn ở 2 ứng dụng client và hoàn toàn không có SAML. Ngay khi bạn cần SSO cho doanh nghiệp hoặc thêm ứng dụng thứ ba, bạn phải lên gói STANDARD với giá $12,500. Cùng nhìn xem Duende thực sự tốn của một sản phẩm thật bao nhiêu, và khoản thuế tính theo số lượng client mà không ai cảnh báo bạn.
Một khóa riêng tư duy nhất ký mọi token mà máy chủ xác thực của chúng tôi phát hành, và nó đã di chuyển hai lần trong đời: sinh ra bên trong tiến trình, bị lưu đày vào một két an toàn mà nó không thể rời khỏi, rồi tái sinh thành một loại khóa hoàn toàn khác — nhỏ hơn, nhanh hơn. Mỗi lần di chuyển đều diễn ra trên một issuer đang chạy với các token đang lưu hành.
SP của bạn đã xác thực chữ ký của assertion, ghim chứng chỉ, và cho người dùng đăng nhập. Mọi kiểm tra đều qua. Vậy mà tôi vẫn replay được một phiên đăng nhập tôi bắt được từ một giờ trước. Mẹo ở đây không phải là phá chữ ký. Mà là xóa đi một trường mà chữ ký chưa bao giờ bao phủ.
Chúng tôi yêu cầu yếu tố thứ hai ở trang login nhưng không bao giờ kiểm tra lại nó ở token endpoint. Thế là một mật khẩu hợp lệ cộng với một return URL được sửa bằng tay đi thẳng qua mặt MFA. Bản sửa lỗi, và quy tắc nó dạy chúng tôi: MFA là một thuộc tính của session mà bạn cấp phát, không phải một bước trong màn hình login.
SCIM là một API CRUD chuẩn hóa mà động từ quan trọng nhất của nó là động từ tắt tài khoản của nhân viên đã nghỉ việc. Các nhà cung cấp định giá nó ở mức $125 mỗi tháng cho mỗi kết nối, hoặc nhét nó vào gói enterprise cạnh SSO. Đây là chi phí thực sự để vận hành một kết nối SCIM, và lý do vì sao thu tiền cho việc thu hồi tài khoản chính là thu tiền cho bảo mật.
Azure Table Storage và DynamoDB không có delete marker, nên một dòng bị xóa chỉ đơn giản biến mất, đồng nghĩa bản sao lưu tăng dần của bạn không bao giờ nhìn thấy thao tác xóa. Khôi phục bản sao lưu đó và người dùng bạn đã cho nghỉ việc quay trở lại, cùng với mật khẩu bạn đã thay bỏ. Đây là lý do vì sao thao tác xóa phải là công dân hạng nhất, và một bảng duy nhất làm được điều đó.
Rate limiter và bộ đếm khóa tài khoản của chúng tôi khóa theo IP của client. Đứng sau nginx, IP đó đến từ một header mà client tự đặt được, nên chỉ cần xoay vòng một header là mỗi request có một IP mới tinh và mọi cơ chế kiểm soát theo IP đổ sập. Đây là màn spoof một dòng, và cách sửa lắt nhắt hơn vẻ ngoài của nó trên Kubernetes.
Azure Table Storage cho bạn một partition key, một row key, và gần như không gì khác. Không join, không secondary index nào đáng kể, không phép increment. Vậy mà chúng tôi vẫn chạy một hệ thống auth multi-tenant trên nó. Đây là những pattern giúp nó hoạt động, và những gì bạn phải đánh đổi so với SQL.
Một endpoint đặt lại mật khẩu không cần xác thực mà không có rate limit theo từng email không chỉ là vấn đề của riêng bạn. Chĩa nó vào hộp thư của người khác và bạn đã biến biểu mẫu đặt lại của họ thành một khẩu đại bác spam, tính hóa đơn lên uy tín người gửi của bạn. Cách khắc phục, mà không làm lộ email nào có tài khoản.
Hầu hết các nhà cung cấp dịch vụ xác thực rào SSO, SCIM, MFA và audit log sau gói Enterprise. Chúng tôi đưa tất cả vào gói $0. Đây là bài toán kinh tế đơn vị khiến việc cho không tính năng vẫn sống khỏe: bức tường chưa bao giờ là tính năng, mà là người dùng hoạt động và hỗ trợ.
Đăng nhập một lần đồng nghĩa với việc tin tưởng nhà cung cấp danh tính. Chúng tôi đã tin nó thêm một điều quá mức: chúng tôi phân giải người dùng liên kết quay lại dựa trên email trong assertion, nên bất kỳ kết nối nào cũng có thể khẳng định địa chỉ của người khác và đáp vào tài khoản của họ. Cách khắc phục không phải là kiểm tra nhiều hơn. Đó là đổi khóa nối danh tính từ email sang một subject riêng theo từng nhà cung cấp mà bên khẳng định không thể giả mạo.
Azure Container Apps hứa hẹn giúp chúng tôi ngừng trả tiền cho một cụm xác thực nhàn rỗi. Chúng tôi tính toán chi phí di chuyển rồi hủy bỏ nó, không phải vì giá hay cold start, mà vì serverless thiếu đúng con số duy nhất mà một backplane xác thực có bầu cử leader thực sự cần.
"Thuế tính năng" dưới dạng một hóa đơn thật. Ba kịch bản của người mua (thương vụ enterprise đầu tiên, một số ít khách hàng, SSO trở thành điều bắt buộc) được tính theo bảng giá công khai của từng nhà cung cấp. Cùng khoản phụ phí âm thầm mà không ai cảnh báo: hết client OAuth giữa chừng tích hợp.
Một cluster ba bản sao liên tục bất đồng với chính nó. Nguyên nhân nằm trong chính mã của chúng tôi: thủ tục phát hiện peer có một khối catch vô hiệu hóa việc phát hiện khi multicast thất bại, và trên Kubernetes được quản lý thì multicast luôn thất bại. Đây là lý do chúng tôi xóa bỏ hoàn toàn gossip và thay vào đó bầu một leader bằng blob lease, và vì sao một danh sách thành viên bạn có thể mở trong Storage Explorer tốt hơn một danh sách bạn phải suy luận ra.
Đăng nhập chỉ tốn một cuối tuần. Hóa đơn đến sau đó: xác minh chữ ký, hủy cấp phép SCIM, MFA, nhật ký kiểm toán, xoay vòng khóa, trách nhiệm pháp lý khi bị xâm phạm, và trực ca cho một hệ thống tier zero. Một bài phân tích tự-xây-hay-mua thành thật, bao gồm cả khi nào tự xây mới là đúng.
OIDC hay SAML? Bạn hiếm khi được quyền chọn. Mỗi giao thức là gì, khi nào mỗi cái thắng thế, những cái bẫy chữ ký XML khiến SAML trở nên nguy hiểm khi tự viết tay, và vì sao một sản phẩm B2B rốt cuộc lại cần cả hai.
Hệ thống hỗ trợ của Authagonal: nhắn tin thời gian thực không cần tải lại, dịch AI hai chiều minh bạch, được ghim từ tin nhắn đầu tiên của khách hàng, và một cuộc trò chuyện vẫn được dịch trọn vẹn ngay cả qua email. Đơn giản, đẹp mắt và thực sự đủ sức.
Hai tenant của chúng tôi âm thầm chính là cùng một tenant: chung cơ sở dữ liệu, chung khóa ký token, chỉ khác tên. Nguyên nhân là một hàm trợ giúp một dòng làm sạch slug bằng cách bỏ đi dấu gạch nối, biến một định danh duy nhất thành một định danh không còn duy nhất. Đây là câu chuyện về việc một phép biến đổi chuỗi gây mất mát thông tin đã trở thành một cuộc chiếm quyền liên tenant như thế nào, và tại sao chúng tôi tìm ra nó trước khi bạn tìm ra.
Chúng tôi bán dịch vụ xác thực, nên bài kiểm thử quan trọng nhất của chúng tôi là một bài tự nó trở thành khách hàng: một lần chạy Playwright duy nhất đăng ký một tenant, cấu hình mọi tính năng, trỏ một ứng dụng thật vào nó, đăng nhập một người dùng thật với đúng các claim, rồi sao lưu, khôi phục và tự xóa chính mình. Cách kiểm thử đầu cuối toàn diện đã mang lại cho chúng tôi sự tự tin để đưa vào vận hành.
Bảng điều khiển nhân viên của chúng tôi xác thực qua chính Authagonal, với Entra SSO và SCIM lo việc provisioning. Trở thành khách hàng của chính mình làm lộ ra những trường hợp biên mà không bộ test nào nghĩ tới việc viết. Đây là những trường hợp quan trọng.
Terraform state của bạn là một tấm bản đồ đầy đủ về toàn bộ hạ tầng cloud cộng với một đống bí mật, và theo mặc định nó nằm sau một endpoint công cộng. Đây là cách chúng tôi khởi tạo remote state từ con số không, đặt nó sau một private endpoint, và truy cập nó qua một connector zero-trust thay vì trả khoản thuế VPN gateway, mà không bao giờ tự khóa mình ra ngoài.
"Ai cũng phải đặt lại mật khẩu" là một lựa chọn di chuyển, không phải định luật vật lý. Cách mang theo các hash mật khẩu hiện có của người dùng qua một lần đổi nhà cung cấp danh tính để cuộc chuyển đổi trở nên vô hình, và một trường hợp duy nhất bạn thực sự không thể.
Ngành xác thực tính tiền theo quyền sử dụng, chứ không theo chi phí. Họ khóa SSO, MFA và việc cấp phát sau các gói và phí theo từng kết nối, cho những tính năng mà việc cung cấp gần như không tốn kém gì. Đây là ranh giới mà các trang giá mong bạn không bao giờ vạch ra.
Chuyển từ Duende IdentityServer tự lưu trữ sang xác thực được quản lý: thực sự cái gì được di chuyển từ cấu hình SQL và ASP.NET Identity, cùng chi phí vận hành mà bạn thôi không phải gánh.
Một góc nhìn thực tế về những gì một cuộc di chuyển khỏi Auth0 thực sự đòi hỏi: cái gì dễ, cái gì rắc rối, và một thứ duy nhất mà Auth0 thật sự sẽ không đưa cho bạn.