Token_authenticatable của devise có an toàn không?


79

Tôi đang xây dựng một api đơn giản với Rails API và muốn đảm bảo rằng tôi đang đi đúng hướng ở đây. Tôi đang sử dụng devise để xử lý thông tin đăng nhập và quyết định sử dụng token_authenticatabletùy chọn của Devise, tùy chọn này tạo khóa API mà bạn cần gửi theo mỗi yêu cầu.

Tôi đang ghép nối API với giao diện người dùng backbone / marionette và thường tự hỏi mình nên xử lý các phiên như thế nào. Suy nghĩ đầu tiên của tôi là chỉ lưu trữ khóa api trong bộ nhớ cục bộ hoặc một cookie và truy xuất nó khi tải trang, nhưng có điều gì đó về việc lưu trữ khóa api theo cách đó đã làm phiền tôi từ quan điểm bảo mật. Sẽ không dễ dàng lấy được khóa api bằng cách xem trong bộ nhớ cục bộ / cookie hoặc đánh hơi bất kỳ yêu cầu nào được gửi qua và sử dụng nó để mạo danh người dùng đó vô thời hạn? Tôi hiện đang đặt lại khóa api cho mỗi lần đăng nhập, nhưng thậm chí điều đó dường như thường xuyên - bất kỳ khi nào bạn đăng nhập trên bất kỳ thiết bị nào, điều đó có nghĩa là bạn sẽ đăng xuất trên mọi thiết bị khác, đó là một điều khá khó khăn. Nếu tôi có thể bỏ thiết lập lại này, tôi cảm thấy như nó sẽ cải thiện từ quan điểm khả năng sử dụng.

Tôi có thể hoàn toàn sai ở đây (và hy vọng tôi là vậy), bất cứ ai có thể giải thích liệu xác thực theo cách này có an toàn đáng tin cậy không và nếu không phải là một giải pháp thay thế tốt là gì? Nhìn chung, tôi đang tìm cách có thể giữ cho người dùng 'đăng nhập' một cách an toàn vào quyền truy cập API mà không phải thường xuyên buộc xác thực lại.

Câu trả lời:


190

token_authenticatabledễ bị tấn công theo thời gian, được giải thích rất kỹ trong bài đăng blog này . Các cuộc tấn công này là lý do token_authenticatablebị xóa khỏi Devise 3.1. Xem bài đăng trên blog plataformatec để biết thêm thông tin.

Để có cơ chế xác thực mã thông báo an toàn nhất, mã thông báo:

  1. Phải được gửi qua HTTPS.

  2. Phải ngẫu nhiên, có độ bền mật mã.

  3. Phải được so sánh một cách an toàn.

  4. Không được lưu trữ trực tiếp trong cơ sở dữ liệu. Chỉ một hàm băm của mã thông báo có thể được lưu trữ ở đó. (Hãy nhớ rằng, mã thông báo = mật khẩu. Chúng tôi không lưu trữ mật khẩu ở dạng văn bản thuần túy trong db, phải không?)

  5. Nên hết hạn theo logic nào đó.

Nếu bạn bỏ qua một số điểm này để ủng hộ khả năng sử dụng, bạn sẽ kết thúc với một cơ chế không an toàn như nó có thể. Nó đơn giản như vậy. Bạn sẽ đủ an toàn nếu bạn đáp ứng ba yêu cầu đầu tiên và hạn chế quyền truy cập vào cơ sở dữ liệu của bạn.

Mở rộng và giải thích câu trả lời của tôi:

  1. Sử dụng HTTPS . Đây chắc chắn là điểm quan trọng nhất vì nó đối phó với những kẻ đánh hơi.

    Nếu bạn không sử dụng HTTPS, thì rất nhiều lỗi có thể xảy ra. Ví dụ:

    • Để truyền thông tin đăng nhập của người dùng (tên người dùng / email / mật khẩu) một cách an toàn, bạn sẽ phải sử dụng xác thực thông báo nhưng điều đó không cắt giảm được những ngày này vì các hàm băm mặn có thể bị ép buộc .

    • Trong Rails 3, cookie chỉ được bao bọc bởi mã hóa Base64, vì vậy chúng có thể bị lộ khá dễ dàng. Xem Giải mã cookie phiên Rails để biết thêm thông tin.

      Kể từ Rails 4, kho lưu trữ cookie được mã hóa để dữ liệu được xác minh kỹ thuật số và kẻ tấn công không thể đọc được. Cookie phải được bảo mật miễn là của bạn secret_key_basekhông bị rò rỉ.

  2. Tạo mã thông báo của bạn bằng:

    • SecureRandom.hex chỉ khi bạn đang sử dụng Ruby 2.5+.
    • Viên ngọc sysrandomnếu bạn đang sử dụng một viên Ruby cũ hơn.

    Để có lời giải thích về lý do tại sao điều này là cần thiết, tôi khuyên bạn nên đọc sysrandomREADME của và bài đăng trên blog Cách tạo số ngẫu nhiên an toàn bằng các ngôn ngữ lập trình khác nhau .

  3. Tìm hồ sơ người dùng bằng ID, email hoặc một số thuộc tính khác của người dùng. Sau đó, so sánh mã thông báo của người dùng đó với mã thông báo của yêu cầu với Devise.secure_compare(user.auth_token, params[:auth_token]. Nếu bạn đang sử dụng Rails 4.2.1+, bạn cũng có thể sử dụng ActiveSupport::SecurityUtils.secure_compare.

    Đừng không tìm thấy hồ sơ người dùng với một công cụ tìm Rails như User.find_by(auth_token: params[:auth_token]). Điều này dễ bị tấn công thời gian!

  4. Nếu bạn có nhiều ứng dụng / phiên cùng một lúc cho mỗi người dùng, thì bạn có hai tùy chọn:

    • Lưu trữ mã thông báo chưa được mã hóa trong cơ sở dữ liệu để nó có thể được chia sẻ giữa các thiết bị. Đây là một thực tiễn không tốt, nhưng tôi đoán bạn có thể làm điều đó với danh nghĩa UX (và nếu bạn tin tưởng nhân viên của mình có quyền truy cập DB).

    • Lưu trữ bao nhiêu mã thông báo được mã hóa cho mỗi người dùng tùy ý để cho phép các phiên hiện tại. Vì vậy, nếu bạn muốn cho phép 2 phiên trên 2 thiết bị khác nhau, hãy giữ 2 hàm băm mã thông báo riêng biệt trong cơ sở dữ liệu. Tùy chọn này ít đơn giản hơn một chút để thực hiện nhưng nó chắc chắn an toàn hơn. Nó cũng có ưu điểm là cho phép bạn cung cấp cho người dùng tùy chọn kết thúc các phiên hoạt động hiện tại trong các thiết bị cụ thể bằng cách thu hồi mã thông báo của họ (giống như GitHub và Facebook).

  5. Phải có một số loại cơ chế khiến mã thông báo hết hạn. Khi thực hiện cơ chế này, hãy tính đến sự cân bằng giữa UX và bảo mật.

    Google sẽ hết hạn mã thông báo nếu nó không được sử dụng trong sáu tháng .

    Facebook hết hạn một mã thông báo nếu nó không được sử dụng trong hai tháng :

    Các ứng dụng di động gốc sử dụng SDK của Facebook sẽ nhận được mã thông báo truy cập tồn tại lâu dài, tốt trong khoảng 60 ngày. Các mã thông báo này sẽ được làm mới một lần mỗi ngày khi người sử dụng ứng dụng của bạn yêu cầu máy chủ của Facebook. Nếu không có yêu cầu nào được thực hiện, mã thông báo sẽ hết hạn sau khoảng 60 ngày và người đó sẽ phải thực hiện lại quy trình đăng nhập để nhận mã thông báo mới.

  6. Nâng cấp lên Rails 4 để sử dụng kho cookie được mã hóa của nó. Nếu bạn không thể, hãy tự mã hóa kho lưu trữ cookie, như được đề xuất ở đây . Sẽ hoàn toàn không có vấn đề gì khi lưu trữ mã xác thực trong kho cookie được mã hóa.

Bạn cũng nên có một kế hoạch dự phòng, ví dụ, một nhiệm vụ cào để đặt lại một tập hợp con các mã thông báo hoặc mọi mã thông báo đơn lẻ trong cơ sở dữ liệu.

Để bắt đầu, bạn có thể xem ý chính này (của một trong những tác giả của Devise) về cách triển khai xác thực mã thông báo với Devise. Cuối cùng, Railscast về việc bảo mật một API sẽ hữu ích.


Tuyệt vời, điều này giúp ích rất nhiều - cảm ơn bạn! Điều này gần như chắc chắn sẽ có câu trả lời đúng. Bounty là của bạn nếu bạn thêm vào đó ý kiến của bạn / khuyến nghị (đặc biệt) về cách tốt nhất để xác thực API xử lý:)
Jeff Escalante

1
Trong khi cả hai phương pháp tạo một chuỗi ngẫu nhiên, urlsafe_base64tạo một chuỗi an toàn cho url. Đó là tất cả trong tên. Trừ khi bạn muốn sử dụng mã thông báo trong url của mình ( mà bạn không nên ), hãy sử dụng hex.
Ashitaka

2
mã thông báo! = mật khẩu. Không có gì sai khi lưu trữ mã thông báo ở dạng văn bản thuần túy. Vấn đề với việc lưu trữ mật khẩu ở dạng văn bản thuần túy là mật khẩu có thể được sử dụng ở một nơi khác, điều đó không nên xảy ra với mã thông báo của bạn.
fatfrog

2
@fatfrog Số Token == mật khẩu. Nếu một tin tặc hoặc một nhân viên bất mãn có quyền truy cập vào cơ sở dữ liệu của bạn, anh ta sẽ không thể xác thực là một người dùng hoặc quản trị viên nhất định.
Ashitaka

1
Tôi không đồng ý, nếu một hacker hoặc nhân viên bất mãn có quyền truy cập vào cơ sở dữ liệu của bạn, thì mã thông báo là thứ cuối cùng bạn cần lo lắng. Họ đã có dữ liệu của bạn.
fatfrog


3

Bạn có thể thử sử dụng rails4 với API của mình, nó cung cấp bảo mật cao hơn và sử dụng devise 3.1.0rc

Đối với mã thông báo, cửa hàng phiên bạn có thể truy cập http://ruby.railstutorial.org/cha Chapter/sign-in-sign-outhttp://blog.bigbinary.com/2013/03/19/cookies-on-rails .html để hiểu rõ hơn.

Cuối cùng, bạn nên trải qua loại mã hóa và giải mã " Không thể giải mã dữ liệu được mã hóa được lưu trữ " để có được sự bảo mật hơn.


Có ai có một ví dụ tùy chỉnh được xây dựng thay thế không?
Daniel Morris
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.