これが私が使用しているものです。トークンを推測するために必ずしも聞く必要はありません。それは他のものよりも短いURL識別子のようなものであり、私はそれを短く保ちたいです。私はオンラインで見つけたいくつかの例に従っており、衝突が発生した場合、と思う以下のコードはトークンを再作成しますが、確かではありません。しかし、これはエッジの周りが少し荒いので、より良い提案を見てみたいです。
def self.create_token
random_number = SecureRandom.hex(3)
"1X#{random_number}"
while Tracker.find_by_token("1X#{random_number}") != nil
random_number = SecureRandom.hex(3)
"1X#{random_number}"
end
"1X#{random_number}"
end
トークンのデータベース列は一意のインデックスであり、モデルでもvalidates_uniqueness_of :token
を使用していますが、これらはアプリでのユーザーのアクションに基づいてバッチで自動的に作成されるためです(注文をしてトークンを購入します) 、本質的に)、アプリにエラーをスローさせることは現実的ではありません。
また、衝突の可能性を減らすために、最後に別の文字列を追加することもできました。時間に基づいて生成されたもの、またはそのようなものですが、トークンが長くなりすぎないようにします。
-更新-
2015年1月9日。ソリューションはRails 5で実装されました ActiveRecordのセキュアトークン実装 =。
-Rails 4&3-
将来の参照用に、安全なランダムトークンを作成し、モデルに対して一意であることを確認します(Ruby 1.9およびActiveRecordを使用する場合):
class ModelName < ActiveRecord::Base
before_create :generate_token
protected
def generate_token
self.token = loop do
random_token = SecureRandom.urlsafe_base64(nil, false)
break random_token unless ModelName.exists?(token: random_token)
end
end
end
編集:
@ kain が提案され、以前の実装は将来削除される可能性があるため、この回答でbegin...end..while
をloop do...break unless...end
に置き換えることに同意しました。
編集2:
Rails 4と懸念がある場合、これを懸念に移行することをお勧めします。
# app/models/model_name.rb
class ModelName < ActiveRecord::Base
include Tokenable
end
# app/models/concerns/tokenable.rb
module Tokenable
extend ActiveSupport::Concern
included do
before_create :generate_token
end
protected
def generate_token
self.token = loop do
random_token = SecureRandom.urlsafe_base64(nil, false)
break random_token unless self.class.exists?(token: random_token)
end
end
end
Ryan Batesは Railscast on beta invitations でニースの小さなコードを使用しています。これにより、40文字の英数字ストリングが生成されます。
Digest::SHA1.hexdigest([Time.now, Rand].join)
この記事で実証されているこれを行うためのいくつかの非常に巧妙な方法があります。
リストされている私のお気に入りはこれです:
Rand(36**8).to_s(36)
=> "uur0cj2h"
これは遅い応答かもしれませんが、ループの使用を避けるために、メソッドを再帰的に呼び出すこともできます。見た目も感じも、私にとっては少しきれいです。
class ModelName < ActiveRecord::Base
before_create :generate_token
protected
def generate_token
self.token = SecureRandom.urlsafe_base64
generate_token if ModelName.exists?(token: self.token)
end
end
ユニークなものが必要な場合は、次のようなものを使用できます。
string = (Digest::MD5.hexdigest "#{ActiveSupport::SecureRandom.hex(10)}-#{DateTime.now.to_s}")
ただし、これにより32文字の文字列が生成されます。
ただし、他の方法もあります。
require 'base64'
def after_create
update_attributes!(:token => Base64::encode64(id.to_s))
end
たとえば、IDが10000の場合、生成されたトークンは "MTAwMDA ="のようになります(IDを簡単にデコードできます。
Base64::decode64(string)
これは役に立つかもしれません:
SecureRandom.base64(15).tr('+/=', '0aZ')
最初の引数「+/=」に入力する特殊文字と、2番目の引数「0aZ」に入力する文字を削除する場合、15はここの長さです。
そして、次のようなものを追加するよりも余分なスペースと改行文字を削除したい場合:
SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")
これが誰にも役立つことを願っています。
has_secure_tokenを使用できます https://github.com/robertomiranda/has_secure_token
本当に使いやすい
class User
has_secure_token :token1, :token2
end
user = User.create
user.token1 => "44539a6a59835a4ee9d7b112b48cd76e"
user.token2 => "226dd46af6be78953bde1641622497a8"
適切なmysql、varchar 32 GUIDを作成するには
SecureRandom.uuid.gsub('-','').upcase
def generate_token
self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end
トークンはパスワードと同様に処理する必要があると思います。そのため、DBで暗号化する必要があります。
モデルのユニークな新しいトークンを生成するためにこのようなことをしていません:
key = ActiveSupport::KeyGenerator
.new(Devise.secret_key)
.generate_key("put some random or the name of the key")
loop do
raw = SecureRandom.urlsafe_base64(nil, false)
enc = OpenSSL::HMAC.hexdigest('SHA256', key, raw)
break [raw, enc] unless Model.exist?(token: enc)
end