web-dev-qa-db-ja.com

Railsでユニークなトークンを作成する最良の方法は?

これが私が使用しているものです。トークンを推測するために必ずしも聞く必要はありません。それは他のものよりも短い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を使用していますが、これらはアプリでのユーザーのアクションに基づいてバッチで自動的に作成されるためです(注文をしてトークンを購入します) 、本質的に)、アプリにエラーをスローさせることは現実的ではありません。

また、衝突の可能性を減らすために、最後に別の文字列を追加することもできました。時間に基づいて生成されたもの、またはそのようなものですが、トークンが長くなりすぎないようにします。

150
Slick23

-更新-

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..whileloop 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
326
Krule

Ryan Batesは Railscast on beta invitations でニースの小さなコードを使用しています。これにより、40文字の英数字ストリングが生成されます。

Digest::SHA1.hexdigest([Time.now, Rand].join)
51
Nate Bird

この記事で実証されているこれを行うためのいくつかの非常に巧妙な方法があります。

https://web.archive.org/web/20121026000606/http://blog.logeek.fr/2009/7/2/creating-small-unique-tokens-in-Ruby

リストされている私のお気に入りはこれです:

Rand(36**8).to_s(36)
=> "uur0cj2h"
30
coreyward

これは遅い応答かもしれませんが、ループの使用を避けるために、メソッドを再帰的に呼び出すこともできます。見た目も感じも、私にとっては少しきれいです。

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
29
Marius Pop

ユニークなものが必要な場合は、次のようなものを使用できます。

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)
17
Esse

これは役に立つかもしれません:

SecureRandom.base64(15).tr('+/=', '0aZ')

最初の引数「+/=」に入力する特殊文字と、2番目の引数「0aZ」に入力する文字を削除する場合、15はここの長さです。

そして、次のようなものを追加するよりも余分なスペースと改行文字を削除したい場合:

SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")

これが誰にも役立つことを願っています。

14
Vik

この方法を試してください:

Ruby 1.9では、uuid生成が組み込まれています。 SecureRandom.uuid関数を使用します。
RubyでのGUIDの生成

これは私にとって役に立ちました

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"
6
user2627938

適切なmysql、varchar 32 GUIDを作成するには

SecureRandom.uuid.gsub('-','').upcase
5
Aaron Henderson
def generate_token
    self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end
1
miosser

トークンはパスワードと同様に処理する必要があると思います。そのため、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
0
cappie013