Rails 7.1: Mở Rộng khả Năng bảo mật với generates_token_for và signed_id

Việc tạo ra những token đặc biệt, an toàn và có thể chứa thông tin về mục đích sử dụng cũng như thời gian hết hạn của token rất hữu ích trong một số tình huống cụ thể. Bạn có thể tạo ra một token duy nhất cho các mục đích cụ thể như authenticate access_token, reset email hoặc reset password, sau đó gán chúng vào địa chỉ URL của ứng dụng của bạn và gửi cho người dùng qua email.

Sử dụng ActiveRecord:SignedId

Từ bây giờ bạn có thể sử dụng API ActiveRecord::SignedId để tạo ra các token có thời hạn. Và bạn có thể dùng ActiveRecord để tìm bản ghi sử dụng signed id tương ứng

Hãy xem xét một tình huống trong đó chúng ta muốn gửi một liên kết đặt lại mật khẩu cho người dùng. Nếu bạn muốn tạo ra một token duy nhất có hiệu lực trong vòng 1 ngày.

user = User.first
signed_id = user.signed_id(expires_in: 1.day, purpose: :password_reset)

# Output signed_id
# eyJfcmFpbHMiOnsiZGF0YSI6ODg3LCJleHAiOiIyMDIzLTEwLTI1VDE2OjM2OjI4LjY1MloiLCJwdXIiOiJ1c2VyL3Bhc3N3b3JkX3Jlc2V0In19--4b1bde069dad4fa959fc111dbb401fac724cd43ab7aea772841fc90d87e42354

signed_id trên sẽ có thời hạn 1 ngày và mục đích (purpose) chúng ta có thể ghi bất kỳ thứ gì củng được. Sau đó để tìm lại record từ signed_id trên thì làm như sau:

User.find_signed(signed_id, purpose: :password_reset)

# Output
# <User:0x000000010ed60a38

Vì ở trên khi signed_id ta có điền purpose thì khi find chúng ta củng phải truyền lại purpose. Trường hợp không đúng thì kết quả sẽ trả về nil, còn khi hết hạn sẽ raise lỗi

Sử dụng ActiveRecord::Base::generates_token_for

class User < ActiveRecord::Base
  has_secure_password

  generates_token_for :password_reset, expires_in: 1.day do
    BCrypt::Password.new(password_digest).salt[-10..]
  end
end

Khởi tạo token

user = User.first
token = user.generate_token_for(:password_reset)

# Output
# eyJfcmFpbHMiOnsiZGF0YSI6Wzg4Nywid3FxLlhwdWJjLiJdLCJleHAiOiIyMDIzLTEwLTI0VDE2OjU0OjA5LjU0MloiLCJwdXIiOiJVc2VyXG5wYXNzd29yZF9yZXNldFxuNjAifX0=--8430fc9ee974752f075c78c6d...

Tìm record từ token đã tạo ở trên

User.find_by_token_for(:password_reset, token)
# Output:
# <User:0x000000010ed60a38

generates_token_for hoạt động tương tự như signed_id. Trong khi signed_id sử dụng model ID làm dữ liệu đầu vào, thì generates_token_for sử dụng sự kết hợp giữa model ID và giá trị được trả về bởi block. Block này nên tham chiếu tới một thuộc tính liên quan đến mục đích của token (Ở đây là password_digest). Khi thuộc tính này (password_digest) được cập nhật, dữ liệu đầu vào cũng được cập nhật và từ đó làm vô hiệu hóa token cũ.

Trong ví dụ trên, sự kết hợp dữ liệu đầu vào sẽ là model ID và 10 ký tự cuối cùng của Bycrypt Password

Khi bạn cập nhật mật khẩu, thì password_digest sẽ thay đổi và do đó token bị vô hiệu hóa sau lần cập nhật đầu tiên của mật khẩu. Điều này khiến cho token chỉ có thể được sử dụng một lần.

Trước đây với bài toán reset password hay reset email thông thường ta sẽ tạo ra 2 cột để lưu trữ (reset_email_token và reset_email_token_expired). Bây giờ sẽ không cần nửa bởi vì đã có generates_token_for


Cảm ơn bạn đã đọc bài viết này về tính năng generates_token_forsigned_id trong Rails 7.1. Hy vọng rằng bạn đã tìm thấy thông tin hữu ích và có thêm kiến thức về cách sử dụng tính năng này để tạo ra các token đặc biệt và an toàn trong ứng dụng của mình. Nếu bạn có bất kỳ câu hỏi hoặc góp ý nào, đừng ngần ngại chia sẻ. Chúng tôi luôn đánh giá phản hồi từ độc giả. Hãy tiếp tục theo dõi và chia sẻ kiến thức về lĩnh vực phát triển ứng dụng web. Cảm ơn bạn đã ủng hộ!

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like
Read More

Nâng bao nhiêu pool connect database là phù hợp

Trong bài viết này chúng ta sẽ tìm hiểu về sự quan trọng của việc tối ưu hóa kết nối database và tìm cách đảm bảo rằng bạn đang sử dụng số lượng pool kết nối đúng đắn, tiết kiệm tài nguyên và đảm bảo hiệu suất hệ thống.
Read More

Sentry for Rails

Table of Contents Hide Đặt vấn đềGiới thiệu SentryConfig Sentry cho Rails appSummary Đặt vấn đề Trong quá…