Chiến lược tạo các số nhận dạng duy nhất và an toàn để sử dụng trong ứng dụng web đôi khi ngoại tuyến


47

Tôi có một dự án dựa trên web cho phép người dùng làm việc cả trực tuyến và ngoại tuyến và tôi đang tìm cách tạo id duy nhất cho các bản ghi ở phía máy khách. Tôi muốn một cách tiếp cận hoạt động trong khi người dùng ngoại tuyến (nghĩa là không thể nói chuyện với máy chủ), được đảm bảo là duy nhất và an toàn. Bằng cách "bảo mật", tôi đặc biệt lo lắng về việc khách hàng gửi các id trùng lặp (độc hại hoặc theo cách khác) và do đó phá hoại tính toàn vẹn dữ liệu.

Tôi đã làm một số việc, hy vọng điều này đã được giải quyết. Tôi đã không tìm thấy bất cứ điều gì rất dứt khoát, đặc biệt là về phương pháp tiếp cận được sử dụng trong các hệ thống sản xuất. Tôi đã tìm thấy một số ví dụ cho các hệ thống mà người dùng sẽ chỉ truy cập dữ liệu mà họ đã tạo (ví dụ: danh sách Todo được truy cập trên nhiều thiết bị, nhưng chỉ bởi người dùng đã tạo dữ liệu đó). Thật không may, tôi cần một cái gì đó tinh vi hơn một chút. Tôi đã tìm thấy một số ý tưởng thực sự tốt ở đây , phù hợp với cách tôi nghĩ mọi thứ có thể hoạt động.

Dưới đây là giải pháp đề xuất của tôi.

Một số yêu cầu

  1. ID phải là duy nhất trên toàn cầu (hoặc ít nhất là duy nhất trong hệ thống)
  2. Được tạo trên máy khách (tức là thông qua javascript trong trình duyệt)
  3. An toàn (như đã nêu ở trên và mặt khác)
  4. Dữ liệu có thể được xem / chỉnh sửa bởi nhiều người dùng, bao gồm cả những người dùng không tạo ra nó
  5. Không gây ra sự cố hiệu suất đáng kể cho db phụ trợ (như MongoDB hoặc CouchDB)

Giải pháp đề xuất

Khi người dùng tạo một tài khoản, họ sẽ được cung cấp một uuid do máy chủ tạo ra và được biết là duy nhất trong hệ thống. Id này KHÔNG phải giống với mã xác thực người dùng. Hãy gọi id này là "mã thông báo id" của người dùng.

Khi người dùng tạo một bản ghi mới, họ sẽ tạo một uuid mới trong javascript (được tạo bằng window.crypto khi có sẵn. Xem ví dụ tại đây ). Id này được nối với "mã thông báo id" mà người dùng nhận được khi họ tạo tài khoản của họ. Id tổng hợp mới này (mã thông báo id phía máy chủ + uuid phía máy khách) hiện là định danh duy nhất cho bản ghi. Khi người dùng trực tuyến và gửi bản ghi mới này đến máy chủ phụ trợ, máy chủ sẽ:

  1. Xác định đây là hành động "chèn" (nghĩa là không cập nhật hoặc xóa)
  2. Xác thực cả hai phần của khóa tổng hợp là uuids hợp lệ
  3. Xác thực rằng phần "mã thông báo id" được cung cấp của id tổng hợp là chính xác cho người dùng hiện tại (nghĩa là nó khớp với mã thông báo id mà máy chủ đã gán cho người dùng khi họ tạo tài khoản của họ)
  4. Nếu mọi thứ đều copasetic, chèn dữ liệu vào db (cẩn thận để làm một chèn và không phải là một "upsert" để nếu id không đã tồn tại nó không cập nhật một bản ghi hiện do nhầm lẫn)

Truy vấn, cập nhật và xóa sẽ không yêu cầu logic đặc biệt nào. Họ chỉ đơn giản là sử dụng id cho bản ghi theo cách tương tự như các ứng dụng truyền thống.

Những lợi thế của phương pháp này là gì?

  1. Mã khách hàng có thể tạo dữ liệu mới khi ngoại tuyến và biết id cho bản ghi đó ngay lập tức. Tôi đã xem xét các cách tiếp cận thay thế trong đó một id tạm thời sẽ được tạo trên máy khách mà sau đó sẽ được đổi thành id "cuối cùng" khi hệ thống trực tuyến. Tuy nhiên, điều này cảm thấy rất giòn. Đặc biệt là khi bạn bắt đầu nghĩ về việc tạo dữ liệu con bằng khóa ngoại cũng cần phải cập nhật. Chưa kể xử lý các url sẽ thay đổi khi id thay đổi.

  2. Bằng cách biến id thành tổng hợp của giá trị do khách hàng tạo và giá trị do máy chủ tạo, mỗi người dùng sẽ tạo id một cách hiệu quả trong hộp cát. Điều này nhằm hạn chế thiệt hại có thể được thực hiện bởi một khách hàng độc hại / lừa đảo. Ngoài ra, mọi va chạm id đều trên cơ sở mỗi người dùng, không phải toàn cầu đối với toàn bộ hệ thống.

  3. Vì mã thông báo id người dùng được gắn với tài khoản của họ, id chỉ có thể được tạo trong hộp cát người dùng bởi các máy khách được xác thực (tức là nơi người dùng đăng nhập thành công). Điều này nhằm ngăn khách hàng độc hại tạo id xấu cho người dùng. Tất nhiên, nếu mã thông báo xác thực người dùng bị đánh cắp bởi một khách hàng độc hại, họ có thể làm điều xấu. Nhưng, một khi mã thông báo xác thực đã bị đánh cắp, tài khoản sẽ bị xâm phạm. Trong trường hợp điều này xảy ra, thiệt hại sẽ được giới hạn ở tài khoản bị xâm nhập (không phải toàn bộ hệ thống).

Mối quan tâm

Dưới đây là một số mối quan tâm của tôi với phương pháp này

  1. Điều này sẽ tạo ra các id đủ độc đáo cho một ứng dụng quy mô lớn? Có bất kỳ lý do để nghĩ rằng điều này sẽ dẫn đến va chạm id? Javascript có thể tạo ra một uuid đủ ngẫu nhiên để làm việc này không? Có vẻ như window.crypto có sẵn khá rộng rãi và dự án này đã yêu cầu các trình duyệt hiện đại hợp lý. ( câu hỏi này hiện có một câu hỏi SO riêng )

  2. Có bất kỳ sơ hở nào mà tôi thiếu có thể cho phép người dùng độc hại thỏa hiệp hệ thống không?

  3. Có lý do để lo lắng về hiệu suất DB khi truy vấn khóa tổng hợp gồm 2 uuids. Làm thế nào id này nên được lưu trữ để có hiệu suất tốt nhất? Hai trường riêng biệt hoặc một trường đối tượng duy nhất? Sẽ có một cách tiếp cận "tốt nhất" khác cho Mongo vs Couch? Tôi biết rằng việc có một khóa chính không tuần tự có thể gây ra các vấn đề hiệu suất đáng chú ý khi thực hiện chèn. Sẽ thông minh hơn khi có một giá trị được tạo tự động cho khóa chính và lưu trữ id này dưới dạng một trường riêng biệt? ( câu hỏi này hiện có một câu hỏi SO riêng )

  4. Với chiến lược này, sẽ dễ dàng xác định rằng một bộ hồ sơ cụ thể đã được tạo bởi cùng một người dùng (vì tất cả họ đều chia sẻ cùng một mã thông báo id hiển thị công khai). Mặc dù tôi không thấy bất kỳ vấn đề tức thời nào với vấn đề này, tốt hơn hết là không rò rỉ thêm thông tin về các chi tiết nội bộ hơn mức cần thiết. Một khả năng khác là băm khóa tổng hợp, nhưng có vẻ như nó có thể rắc rối hơn giá trị của nó.

  5. Trong trường hợp có xung đột id cho người dùng, không có cách nào đơn giản để khôi phục. Tôi cho rằng khách hàng có thể tạo ra một id mới, nhưng điều này có vẻ như rất nhiều công việc cho một trường hợp cạnh thực sự không nên xảy ra. Tôi đang có ý định rời bỏ điều này.

  6. Chỉ người dùng được xác thực mới có thể xem và / hoặc chỉnh sửa dữ liệu. Đây là một giới hạn chấp nhận được cho hệ thống của tôi.

Phần kết luận

Là trên một kế hoạch hợp lý? Tôi nhận ra một số điều này dẫn đến một cuộc gọi phán xét dựa trên sự hiểu biết đầy đủ hơn về ứng dụng được đề cập.


Tôi nghĩ rằng câu hỏi này có thể can thiệp bạn stackoverflow.com/questions/105034/ Từ đó cũng đọc cho tôi như GUID nhưng dường như chúng không có nguồn gốc trong javascript
Rémi

2
Tôi nhận ra rằng UUID đã đáp ứng 5 yêu cầu được liệt kê. Tại sao chúng không đủ?
Gabe

@Gabe Xem ý kiến ​​của tôi về bài viết nói dối ryans dưới đây
Herbrandson

thảo luận meta của câu hỏi này: meta.stackoverflow.com/questions/251215/...
một loại muôi

"khách hàng độc hại / rouge" - lừa đảo.
David Conrad

Câu trả lời:


4

Cách tiếp cận của bạn sẽ làm việc. Rất nhiều hệ thống quản lý tài liệu sử dụng kiểu tiếp cận này.

Một điều cần xem xét là bạn không cần sử dụng cả uuid người dùng và id mục ngẫu nhiên như một phần của chuỗi. Thay vào đó, bạn có thể băm kết hợp cả hai. Điều này sẽ cung cấp cho bạn một mã định danh ngắn hơn và có thể một số lợi ích khác vì id kết quả sẽ được phân bổ đều hơn (cân bằng tốt hơn để lập chỉ mục và lưu trữ tệp nếu bạn đang lưu trữ tệp dựa trên uuid của chúng).

Một tùy chọn khác bạn có là chỉ tạo một uuid tạm thời cho mỗi mục. Sau đó, khi bạn kết nối và gửi chúng lên máy chủ, máy chủ sẽ tạo uuid (được bảo đảm) cho từng mục và trả lại cho bạn. Sau đó, bạn cập nhật bản sao địa phương của bạn.


2
Tôi đã xem xét sử dụng hàm băm của 2 làm id. Tuy nhiên, tôi thấy không có cách nào phù hợp để tạo sha256 trong tất cả các trình duyệt mà tôi cần hỗ trợ :(
Herbrandson

12

Bạn cần tách hai mối quan tâm:

  1. Tạo ID: khách hàng phải có khả năng tạo một mã định danh duy nhất trong hệ thống phân tán
  2. Mối quan tâm bảo mật: khách hàng PHẢI có mã thông báo xác thực người dùng hợp lệ VÀ mã thông báo xác thực hợp lệ cho đối tượng được tạo / sửa đổi

Giải pháp cho hai điều không may này là riêng biệt; nhưng may mắn là chúng không tương thích.

Mối quan tâm về việc tạo ID được giải quyết dễ dàng bằng cách tạo bằng UUID, đó là những gì UUID được thiết kế cho; tuy nhiên, vấn đề bảo mật sẽ yêu cầu bạn thực hiện kiểm tra trên máy chủ rằng mã thông báo xác thực đã cho phép hoạt động (nghĩa là nếu mã thông báo xác thực dành cho người dùng không sở hữu quyền cần thiết trên đối tượng cụ thể đó, thì nó PHẢI từ chối).

Khi được xử lý chính xác, xung đột sẽ không thực sự gây ra vấn đề bảo mật (người dùng hoặc khách hàng sẽ chỉ được yêu cầu thử lại thao tác với một UUID khác).


Đây là một điểm thực sự tốt. Có lẽ đó là tất cả những gì cần thiết và tôi đang xem xét nó. Tuy nhiên, tôi có một vài lo ngại về phương pháp này. Lớn nhất là các uuids được tạo bằng javascript dường như không ngẫu nhiên như người ta có thể hy vọng (xem các bình luận tại stackoverflow.com/a/2117523/13181 cho các vụ giam giữ). Có vẻ như window.crypto sẽ giải quyết vấn đề này, nhưng dường như nó không có sẵn trong tất cả các phiên bản trình duyệt mà tôi cần hỗ trợ.
Herbrandson

tiếp tục ... Tôi thích đề xuất của bạn về việc thêm mã trong ứng dụng khách sẽ tạo lại một uuid mới trong trường hợp va chạm. Tuy nhiên, dường như điều này đối với tôi giới thiệu lại mối quan tâm mà tôi có trong bài viết của mình dưới điểm # 1 của phần "Ưu điểm của phương pháp này" là gì. Tôi nghĩ rằng nếu tôi đã đi theo con đường đó, tốt hơn hết là tôi chỉ nên tạo id tạm thời ở phía máy khách và sau đó cập nhật chúng với "id cuối cùng" từ máy chủ khi được kết nối
Herbrandson

tiếp tục một lần nữa ... Hơn nữa, cho phép người dùng gửi id duy nhất của riêng họ có vẻ như là một vấn đề bảo mật. Có lẽ kích thước của một uuid và khả năng thống kê cao của sự va chạm là đủ để giảm thiểu mối quan tâm này trong chính họ. Tôi không chắc. Tôi đã có một sự nghi ngờ dai dẳng rằng việc giữ mỗi người dùng trong "hộp cát" của riêng họ chỉ là một ý tưởng tốt trong trường hợp này (tức là tin tưởng không có đầu vào của người dùng).
Herbrandson

@herbrandson: Không có vấn đề bảo mật nào tôi có thể nghĩ đến khi cho phép người dùng tạo id duy nhất của riêng họ miễn là bạn luôn kiểm tra xem người dùng có quyền cho hoạt động không. ID chỉ là thứ có thể được sử dụng để xác định các đối tượng, nó không thực sự quan trọng giá trị của nó là gì. Tác hại tiềm năng duy nhất là người dùng có thể dự trữ một loạt ID cho mục đích sử dụng của riêng họ, nhưng điều đó không thực sự gây ra bất kỳ vấn đề nào cho toàn bộ hệ thống bởi vì những người dùng khác chỉ có thể đến các giá trị đó một cách tình cờ.
Lie Ryan

Cảm ơn phản hồi của bạn. Nó thực sự buộc tôi phải làm rõ suy nghĩ của mình! Có một lý do tôi cảnh giác với cách tiếp cận của bạn, và tôi đã quên nó trên đường đi :). Nỗi sợ hãi của tôi gắn liền với RNG tội nghiệp trong nhiều trình duyệt. Đối với thế hệ uuid, người ta sẽ thích RNG mạnh về mật mã. Nhiều trình duyệt mới hơn có điều này thông qua window.crypto, nhưng các trình duyệt cũ hơn thì không. Do đó, người dùng độc hại có thể tìm ra hạt giống của người dùng RNG khác và từ đó biết được uuid tiếp theo sẽ được tạo. Đây là phần cảm thấy như nó có thể bị tấn công.
Herbrandson
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.