Kiến trúc để hợp nhất nhiều tài khoản người dùng với nhau


176

Được rồi, tôi có một trang web nơi bạn có thể đăng ký cho mình và đăng nhập. Bạn cũng có thể đăng nhập bằng tài khoản facebook, twitter hoặc Linkedin của mình.

Điều quan trọng là người dùng chỉ có một tài khoản được đăng ký. Vì vậy, bằng cách nào đó, tôi muốn hợp nhất tài khoản của người dùng nếu họ sử dụng các phương thức khác nhau để đăng nhập. Giải pháp tốt nhất để giải quyết điều này là gì?

Chẳng hạn, người dùng đăng nhập bằng tài khoản Facebook của mình. Tôi sử dụng dữ liệu để đăng ký tài khoản cho anh ta tự động. Tôi có nên gửi e-mail với tên người dùng và mật khẩu của trang web của chúng tôi không? (Nếu điều này ổn với chính sách của Facebook). Tôi có nên cho họ một màn hình thứ hai để họ có thể điền tên người dùng và mật khẩu không? Nhưng đó không phải là ý tưởng đằng sau việc đăng nhập bằng tài khoản Facebook của bạn. Nó nên đơn giản hóa thủ tục của bạn để tham gia.

Cũng có thể người dùng đã đăng ký chính mình trên trang web của chúng tôi và lần sau anh ta đăng nhập bằng tài khoản twitter của mình. Làm cách nào tôi có thể hợp nhất 2 tài khoản này làm một? Cách tốt nhất là gì?

Vì vậy, về cơ bản câu hỏi của tôi là: Tôi có 4 cách khác nhau để người dùng trở thành thành viên của trang web của chúng tôi. Làm cách nào tôi có thể đảm bảo cả 4 cách này chỉ tạo một tài khoản nếu người dùng quyết định sử dụng nhiều cách? Luồng tốt nhất để đảm bảo rằng nó không trở thành rắc rối cho chính người dùng?


Biên tập:

3 năm sau khi tôi hỏi câu hỏi này, tôi tự đưa ra câu trả lời trong một loạt các bài viết: https://www.peternijssen.nl/social-network-authentication-setup/
https://www.peternijssen.nl/social- xác thực mạng-google /
https://www.peternijssen.nl/social-network-authentication-merging-accounts/
https://www.peternijssen.nl/social-network-authentication-twitter-facebook/


7
Tất nhiên, tốt nhất là cố gắng ngăn người dùng có nhiều tài khoản ngay từ đầu bằng cách cho phép nhiều phương thức đăng nhập như Stack Overflow, nhưng ngay cả SO cũng có khả năng cho người kiểm duyệt hợp nhất tài khoản. Tôi đang cung cấp tiền thưởng cho bất cứ ai có thể mô tả một kiến ​​trúc để cho phép điều này. Phải có một giải pháp tốt hơn là "cập nhật bài đăng UserID = 2 trong đó UserID = 1"
David Boike

email và điện thoại có thể được sử dụng làm chìa khóa hợp nhất, những người khác?
Wuaner

Xin chào, liên kết mà bạn đã cung cấp dường như không hoạt động. Nó chỉ đi đến trang chủ của trang web
cảnh giác

Cập nhật các liên kết. Bài viết đã 6 tuổi mặc dù.
PT

Câu trả lời:


120

Tôi đang phải đối mặt với nhiệm vụ chính xác tại thời điểm này. Thiết kế tôi làm ra khá đơn giản, nhưng nó hoạt động tốt.

Ý tưởng cốt lõi là các mô hình cho một danh tính trang web địa phương và danh tính trang web của bên thứ ba được giữ tách biệt, nhưng sau đó được liên kết. Vì vậy, mỗi người dùng đăng nhập vào trang web có một danh tính cục bộ ánh xạ tới bất kỳ số lượng nhận dạng trang web của bên thứ ba nào.

Một bản ghi nhận dạng cục bộ chứa tối thiểu thông tin - nó thậm chí có thể là một trường duy nhất - chỉ là một khóa chính. (Đối với ứng dụng của tôi, tôi không quan tâm đến email, tên hoặc ngày sinh của người dùng - Tôi chỉ muốn biết họ là người đã đăng nhập vào tài khoản này suốt.)

Danh tính của bên thứ ba chứa thông tin chỉ liên quan đến xác thực với bên thứ ba. Đối với OAuth, điều này thường có nghĩa là số nhận dạng người dùng (như id, email hoặc tên người dùng) và số nhận dạng dịch vụ (cho biết trang web hoặc dịch vụ nào được xác thực). Trong các phần khác của ứng dụng, bên ngoài cơ sở dữ liệu, số nhận dạng dịch vụ đó được ghép nối với một phương thức để truy xuất định danh người dùng có liên quan từ dịch vụ đó và đó là cách xác thực được thực hiện. Đối với OpenID, chúng tôi sử dụng cùng một cách tiếp cận, ngoại trừ phương thức xác thực được khái quát hơn (vì hầu như chúng tôi luôn có thể thực hiện cùng một giao thức - ngoại trừ chúng tôi sử dụng URL nhận dạng khác nhau và đó là định danh dịch vụ của chúng tôi).

Cuối cùng, tôi giữ một bản ghi trong đó danh tính của bên thứ ba được ghép nối với danh tính địa phương nào. Để tạo các bản ghi này, luồng trông như thế này:

  • Người dùng đăng nhập lần đầu tiên bằng danh tính của bên thứ ba. Một bản ghi nhận dạng cục bộ được tạo, sau đó là bản ghi nhận dạng của bên thứ ba và sau đó chúng được ghép nối.
  • Trong bảng điều khiển, người dùng được cung cấp cơ hội liên kết tài khoản bằng cách đăng nhập vào dịch vụ của bên thứ ba. (Khá đơn giản cách thức hoạt động của nó.)
  • Trong trường hợp người dùng vô tình tạo nhiều tài khoản, giải pháp khá đơn giản. Trong khi người dùng đăng nhập vào một trong các tài khoản, anh ta đăng nhập vào một tài khoản khác mà trước đây anh ta đã sử dụng để đăng nhập vào trang web (thông qua tính năng bảng điều khiển ở trên). Dịch vụ web phát hiện xung đột này (rằng danh tính cục bộ của người dùng đã đăng nhập khác với danh tính cục bộ được liên kết với danh tính của bên thứ ba vừa đăng nhập) và người dùng được nhắc nhập hợp nhất tài khoản.

Hợp nhất tài khoản là vấn đề hợp nhất từng trường riêng lẻ của danh tính cục bộ (sẽ thay đổi từ ứng dụng này sang ứng dụng khác và sẽ dễ dàng nếu bạn chỉ có một vài trường trong hồ sơ nhận dạng địa phương của mình), và sau đó đảm bảo nhận dạng bên thứ ba được liên kết được liên kết với bản sắc địa phương kết quả.


1
Đây là một giải pháp rất tốt (tôi thích ý tưởng tự động phát hiện va chạm nhận dạng cục bộ)! Tôi tự hỏi nếu bạn tìm thấy một cách để tự động đăng nhập người dùng vào các tài khoản "phụ" được liên kết sau lần đăng nhập đầu tiên vào ứng dụng. Người dùng có phải đăng nhập riêng vào từng tài khoản mỗi lần họ truy cập không (nếu chưa có phiên hoạt động với nhà cung cấp này)?
Alexandra

@Alexandra Bạn có ý nghĩa gì bởi "tài khoản phụ"? Bạn có hỏi điều gì xảy ra khi người dùng đăng nhập vào ứng dụng thông qua một số trình xác thực khác nhau, tạo ra một danh tính cục bộ mới với mỗi người không?
cheeken

Tôi đoán điều này đảm bảo một sự làm rõ thích hợp và câu hỏi của riêng nó: stackoverflow.com/questions/11060368/ rèn
Alexandra

3
Câu hỏi mặc dù, nhưng nếu người dùng đăng ký vào trang web bằng cùng một địa chỉ email thì sao? chúng tôi có nhắc họ rằng email đã tồn tại (Email đến từ mô hình bên thứ ba) hoặc chúng tôi vẫn đăng ký chúng vào trang web và sau đó chúng tôi đã hợp nhất các tài khoản đó?
dùng962206

@ user962206 nhiều dịch vụ sẽ không cung cấp cho bạn địa chỉ email 'thực' vì lý do riêng tư. Ví dụ: Twitter sẽ không cung cấp cho bạn bất kỳ địa chỉ email nào, theo như tôi biết Facebook sẽ cung cấp cho "user.name@facebook.com" mà tôi nghi ngờ ai đó sẽ sử dụng để đăng ký.
kapex

44

Tôi có xu hướng tìm thấy rất nhiều trang web hợp nhất dựa trên email là yếu tố tham gia chồng chéo.

Tôi có thể thấy đây là một lựa chọn khả thi, nhưng một lần nữa nó phụ thuộc vào sở thích của bạn về cách hợp nhất. Địa chỉ Email là cách chính mà mọi người sử dụng để xác minh một số thay đổi thông tin quan trọng trên trang web của bạn như thay đổi mật khẩu, chấm dứt dịch vụ, số dư tài khoản thấp, v.v ... Nó gần giống như hệ thống số an sinh xã hội của web nhưng có khả năng giao tiếp . Về mặt văn hóa: Tôi nghĩ thật hợp lý khi cho rằng một email là một danh tính khá độc đáo trên các dịch vụ xác thực OAuth. Cấp, đó là những gì các hình thức đăng nhập cho Facebook và Google yêu cầu.

Quá trình suy nghĩ hiện tại của tôi.

Trang đăng nhập có 3 tùy chọn

  • Thành viên trang web của riêng bạn
  • Đăng nhập Facebook
  • Đăng nhập bằng google

1) Lần đầu tiên người dùng đăng nhập: kích hoạt luồng đăng ký nơi tài khoản được tạo và điền lần đầu tiên.

 if the user logins using Facebook (or whatever 3rd party login)
      1) call the Facebook api asking for their information (email, name, etc...) 
      2) create an account membership entry in your database somewhat like this 

         Table = Users
         [ UserId   |       Email             | Password ]
         [    23     | "newuser@coolmail.com" |  *null*  ]

      3) create an external auths entry like so
         *ProviderUserId is the unique id of that user on the provider's site

         Table = ExternalAuths
         [ ExternalAuthId  |  User_UserId   | ProviderName |   ProviderUserId  ]
         [    56           |      23        |   Facebook   |  "max.alexander.9"]

 if the user wants to create an account with your own registration it would just be this           

         Table = Users
         [ UserId   |       Email           |   Password  ]
         [    23     | newuser@coolmail.com |  myCoolPwd  ]

2) Vào một lúc khác, người dùng quay lại nhưng quyết định nhấp vào Đăng nhập Google

      1) call the Google api asking for their information (email, name, etc...) 

      2) once you get the email, match it up to the userId entry with the existing email 

      3) create an additional External auth entry as such

         Table = ExternalAuths
         [ ExternalAuthId  |  User_UserId   | ProviderName |   ProviderUserId  ]
         [    56           |      23        |   Facebook   |  "max.alexander.9"]
         [    57           |      23        |    Google    |  "1234854368"     ]

3) Bây giờ bạn đã hợp nhất trên tài khoản mà bạn tin tưởng email trên các mục cơ sở dữ liệu của bạn giống như tài khoản bạn tin cậy từ thông tin đăng nhập bên ngoài.

Vì vậy, cho lần đăng nhập tiếp theo

Vậy điều gì sẽ xảy ra nếu bạn có thông tin đăng nhập bên ngoài trước và sau đó bạn muốn người dùng có thể đăng nhập bằng mật khẩu sau này?

Tôi thấy hai cách dễ dàng để làm điều này

  • Trong bất kỳ lần đăng nhập đầu tiên nào khi tài khoản được tạo từ xác thực bên ngoài, hãy yêu cầu họ nhập mật khẩu để hoàn thành mục nhập đầu tiên của họ vào ứng dụng của bạn

  • Nếu họ đã đăng ký bằng facebook hoặc google trước thì bằng cách nào đó bạn muốn đăng ký bằng mẫu đăng ký trang web của riêng bạn. Phát hiện nếu địa chỉ email mà họ đã nhập đã tồn tại, hãy hỏi họ mật khẩu và gửi cho họ xác nhận email sau khi đăng ký hoàn tất.


1
Khi bạn sử dụng sơ đồ xác thực bên ngoài (Facebook, Google, Twitter, v.v.), bạn sẽ không bao giờ có quyền truy cập vào mật khẩu của nhà cung cấp bên ngoài. Mật khẩu trong ví dụ trên là mật khẩu cho ứng dụng web OWN của bạn.
Tối đa Alexander

6
Twitter không cung cấp email cho người dùng. Vì vậy, sau lần xác thực đầu tiên của họ nếu Twitter UserId không tồn tại, hãy nhắc anh ta về email và mật khẩu. Nếu email và mật khẩu tồn tại, liên kết các tài khoản. Nếu không, hãy lưu trữ email và mật khẩu để xác thực liền mạch. Điều đó có hữu ích không?
Tối đa Alexander

1
Là một lưu ý phụ, giờ đây có thể yêu cầu quyền cho ứng dụng Twitter của bạn để lấy địa chỉ email của người dùng.
Sunil D.

2
Có phải facebook làm cho mọi người xác minh email của họ? Tôi biết rằng Github, ví dụ, không. Người dùng độc hại có thể đăng ký Github bằng tên email của người khác và sau đó có quyền truy cập vào tài khoản của họ trên trang web của bạn bằng chiến lược này. (API của Github cho bạn biết email của họ có được xác minh hay không - bạn có thể từ chối bất kỳ ai xác thực với Github trên một địa chỉ email chưa được xác minh)
Matthew Moisen

6
Đây là vi phạm bảo mật nếu người dùng đăng ký qua phương tiện truyền thông xã hội, chỉnh sửa địa chỉ email của họ trên phương tiện xã hội, người khác lấy cùng một địa chỉ email, đăng ký với phương tiện truyền thông xã hội và sau đó đăng nhập, họ sẽ được hợp nhất với tài khoản của người khác. Điều này rất khó xảy ra nhưng vi phạm không hơn không kém. Hay tôi đang thiếu một cái gì đó.
Shane

35

Tôi đã trải qua điều này với sled.com. Có nhiều vấn đề ở đây liên quan đến việc tạo tài khoản và hỗ trợ nhiều tài khoản bên thứ ba để đăng nhập. Một số trong số họ là:

  • Bạn có cần hỗ trợ cả mật khẩu cục bộ và thông tin đăng nhập của bên thứ ba không?

Đối với sled.com, tôi đã quyết định bỏ mật khẩu cục bộ do giá trị nhỏ mà nó thêm vào và chi phí bổ sung trong việc bảo mật biểu mẫu nhập mật khẩu. Có nhiều cuộc tấn công được biết đến để phá mật khẩu và nếu bạn định giới thiệu mật khẩu, bạn phải chắc chắn rằng chúng không dễ bị phá vỡ. Bạn cũng cần lưu trữ chúng theo cách băm một chiều hoặc một cái gì đó tương tự để ngăn chặn chúng bị rò rỉ.

  • Bạn muốn cho phép linh hoạt bao nhiêu trong việc hỗ trợ nhiều tài khoản bên thứ ba?

Có vẻ như bạn đã chọn ba nhà cung cấp đăng nhập: Facebook, Twitter và LinkedIn. Điều đó thật tuyệt bởi vì điều đó có nghĩa là bạn đang sử dụng OAuth và làm việc với một nhóm các nhà cung cấp đáng tin cậy được xác định rõ. Tôi không phải là fan hâm mộ của OpenID. Câu hỏi còn lại là nếu bạn cần hỗ trợ nhiều tài khoản bên thứ ba từ cùng một nhà cung cấp (ví dụ: một tài khoản cục bộ có hai tài khoản Twitter được liên kết). Tôi giả sử là không, nhưng nếu bạn làm thế, bạn sẽ cần điều chỉnh nó trong mô hình dữ liệu của mình.

Đối với Sled, chúng tôi hỗ trợ đăng nhập bằng Facebook, Twitter và Yahoo! và trong mỗi tài khoản người dùng lưu một khóa cho mỗi người: {"_id": "djdjd99dj", "yahoo": "dj39djdj", twitter: "3723828732", "facebook": "12837287"}. Chúng tôi thiết lập một loạt các ràng buộc để đảm bảo rằng mỗi tài khoản bên thứ ba chỉ có thể được liên kết với một tài khoản cục bộ.

Nếu bạn sẽ cho phép nhiều tài khoản từ cùng một nhà cung cấp bên thứ ba, bạn sẽ cần sử dụng danh sách hoặc các cấu trúc khác để hỗ trợ điều đó, và cùng với đó, tất cả các hạn chế khác để đảm bảo tính duy nhất.

  • Làm thế nào để liên kết nhiều tài khoản?

Lần đầu tiên người dùng đăng ký dịch vụ của bạn, trước tiên họ đến nhà cung cấp bên thứ ba và quay lại với id của bên thứ ba đã được xác minh. Sau đó, bạn tạo một tài khoản cục bộ cho họ và thu thập bất kỳ thông tin nào bạn muốn. Chúng tôi thu thập địa chỉ email của họ và cũng yêu cầu họ chọn tên người dùng cục bộ (chúng tôi cố gắng điền trước biểu mẫu với tên người dùng hiện tại của họ từ nhà cung cấp khác). Có một số dạng định danh cục bộ (email, tên người dùng) là rất quan trọng để phục hồi tài khoản sau này.

Máy chủ biết đây là lần đăng nhập đầu tiên nếu trình duyệt không có cookie phiên (hợp lệ hoặc hết hạn) cho một tài khoản hiện có và không tìm thấy tài khoản bên thứ ba được sử dụng. Chúng tôi cố gắng thông báo cho người dùng rằng họ không chỉ đăng nhập mà đang tạo một tài khoản mới để nếu họ đã có tài khoản, họ sẽ hy vọng tạm dừng và đăng nhập bằng tài khoản hiện tại của họ.

Chúng tôi sử dụng cùng một luồng để liên kết các tài khoản bổ sung, nhưng khi người dùng quay lại từ bên thứ ba, sự hiện diện của cookie phiên hợp lệ được sử dụng để phân biệt giữa nỗ lực liên kết tài khoản mới với hành động đăng nhập. Chúng tôi chỉ cho phép một tài khoản bên thứ ba của mỗi loại và nếu đã có một tài khoản được liên kết, hãy chặn hành động. Nó không phải là một vấn đề vì giao diện để liên kết một tài khoản mới bị vô hiệu hóa nếu bạn đã có một (mỗi nhà cung cấp), nhưng chỉ trong trường hợp.

  • Làm thế nào để hợp nhất tài khoản?

Nếu người dùng đã cố liên kết tài khoản bên thứ ba mới đã được liên kết với tài khoản cục bộ, bạn chỉ cần nhắc họ xác nhận họ muốn hợp nhất hai tài khoản (giả sử bạn có thể xử lý việc hợp nhất như vậy với tập dữ liệu của mình - thường dễ nói hơn hơn thực hiện). Bạn cũng có thể cung cấp cho họ một nút đặc biệt để yêu cầu hợp nhất nhưng trên thực tế, tất cả những gì họ đang làm là liên kết một tài khoản khác.

Đây là một máy trạng thái khá đơn giản. Người dùng quay lại từ bên thứ ba với id tài khoản bên thứ ba. Cơ sở dữ liệu của bạn có thể ở một trong ba trạng thái:

  1. Tài khoản được liên kết với tài khoản cục bộ và không có cookie phiên nào -> Đăng nhập
  2. Tài khoản được liên kết với tài khoản cục bộ và có cookie phiên -> Hợp nhất
  3. Tài khoản không được liên kết với tài khoản cục bộ và không có cookie phiên nào -> Đăng ký
  4. Tài khoản không được liên kết với tài khoản cục bộ và có cookie phiên -> Liên kết tài khoản bổ sung

    • Làm cách nào để thực hiện khôi phục tài khoản với các nhà cung cấp bên thứ ba?

Đây vẫn là lãnh thổ thử nghiệm. Tôi chưa thấy một UX hoàn hảo cho điều này vì hầu hết các dịch vụ đều cung cấp cả mật khẩu cục bộ bên cạnh tài khoản của bên thứ ba và do đó tập trung vào trường hợp sử dụng "quên mật khẩu của tôi", không phải mọi thứ khác đều có thể sai.

Với Sled, chúng tôi đã chọn sử dụng "Cần trợ giúp đăng nhập?" và khi bạn nhấp, hãy hỏi người dùng về email hoặc tên người dùng của họ. Chúng tôi tìm kiếm nó và nếu chúng tôi tìm thấy một tài khoản phù hợp, hãy gửi email cho người dùng đó một liên kết có thể tự động đăng nhập chúng vào dịch vụ (tốt cho một lần). Sau khi vào, chúng tôi đưa họ trực tiếp đến trang liên kết tài khoản, nói với họ rằng họ nên xem và có khả năng liên kết các tài khoản bổ sung và hiển thị cho họ các tài khoản bên thứ ba mà họ đã liên kết.


Giải pháp này tốt hơn nhiều cho tôi, Cảm ơn!
Roy Shoa

2
Cookie phiên không đủ tốt để xác định người dùng. Nếu người dùng khác sử dụng cùng một thiết bị thì sao?
DeepBlue

23

Cả hai cách tiếp cận cho các tài khoản hợp nhất tự động đều để lại một lỗ hổng khá lớn cho phép ai đó chiếm đoạt một tài khoản. Cả hai dường như đưa ra giả định rằng người dùng là người mà họ nói họ là khi họ cung cấp tùy chọn hợp nhất cho người dùng đăng ký.

Đề xuất của tôi để giảm thiểu lỗ hổng là yêu cầu người dùng xác thực với một trong những Nhà cung cấp danh tính đã biết trước khi thực hiện hợp nhất để xác minh danh tính của người dùng.

Ví dụ: Người dùng A đăng ký với danh tính Facebook. Sau đó, họ quay lại trang web của bạn và cố gắng truy cập bằng Windows Live ID và bắt đầu quá trình đăng ký. Trang web của bạn sẽ nhắc người dùng A bằng ... Có vẻ như bạn đã đăng ký với Facebook trước đây. Vui lòng đăng nhập bằng Facebook (cung cấp liên kết) và chúng tôi có thể hợp nhất Windows Live ID của bạn với hồ sơ hiện có của bạn.

Một cách khác là lưu trữ bí mật chung (mật khẩu / câu hỏi cá nhân) trên đăng ký ban đầu mà người dùng phải cung cấp khi hợp nhất danh tính, tuy nhiên điều này giúp bạn quay lại công việc lưu trữ bí mật chung. Điều đó cũng có nghĩa là bạn phải xử lý tình huống mà người dùng không nhớ bí mật được chia sẻ và quy trình làm việc đi cùng với nó.


Bạn sẽ làm gì nếu bạn không nhận được địa chỉ email từ nhà cung cấp?
fred.kassi

1

Hầu hết các bài đăng đều khá cũ và tôi đoán dịch vụ Xác thực Firebase miễn phí của Google chưa xuất hiện. Sau khi xác minh bằng OAuth, bạn chuyển mã thông báo OAuth cho nó và nhận id người dùng duy nhất mà bạn có thể lưu trữ để tham khảo. Các nhà cung cấp được hỗ trợ là Google, Facebook, Twitter, GitHub và có một tùy chọn để đăng ký các nhà cung cấp tùy chỉnh và ẩn danh.


Có những trường hợp sử dụng được cho là nơi căn cứ hỏa lực không có ý nghĩa. Ví dụ: các hệ thống mạng nội bộ có nhiều hơn một lần đăng nhập ..
Bostwick


-4

Bạn nên cho phép đăng nhập từ một tài khoản, sau đó khi đăng nhập, hãy cung cấp tùy chọn thêm tài khoản khác để hợp nhất với tài khoản đó.


4
Và điều gì xảy ra nếu người dùng không làm điều đó và thấy mình có 4 tài khoản riêng biệt? Làm thế nào để bạn tạo một kiến ​​trúc cho phép hợp nhất trong trường hợp này?
David Boike

Tùy thuộc vào nhu cầu của bạn, đây thực sự có thể là một gợi ý hợp lệ. Tôi chỉ muốn cảnh báo những người nghĩ rằng việc hợp nhất hoặc liên kết các tài khoản có thể được thực hiện sau đó: Nếu bạn tin rằng đây là điều bạn có thể muốn sau đó thì bạn sẽ muốn chuẩn bị cho mình ngay từ đầu với thiết kế cơ sở dữ liệu của bạn: Một tùy chọn là có một Usergroup và UserMapping. Bạn có thể ánh xạ ID người dùng OAuth hoặc ID người dùng Email & Mật khẩu sang Nhóm người dùng.
BumbleB2na
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.