Как мне скрыть идентификаторы моих записей в рельсах? - PullRequest
13 голосов
/ 16 февраля 2012

Я пытаюсь выяснить, как скрыть идентификаторы моих записей в рельсах.

Например: типичный путь может выглядеть как http://domain/records/1,, поэтому людям довольно легко сделать выводсколько трафика получает сайт, если они просто создают новую запись.

Одним из решений, которое я использовал, является хеширование идентификатора с помощью соли, но, поскольку я не уверен, является ли эта функция биективной, в итоге я сохраняю ее в другом столбце в моей базе данных и проверяюуникальность.

Другой вариант, о котором я думал, - генерировать случайный хеш и сохранять его как другой столбец.Если это не уникально ... просто создайте еще один.

Какой лучший способ сделать это?

Ответы [ 5 ]

25 голосов
/ 16 февраля 2012

Вы можете использовать встроенную библиотеку OpenSSL для шифрования и дешифрования ваших идентификаторов, так что вам нужно будет только перезаписать to_param на ваших моделях. Вам также понадобится использовать Base64 для преобразования зашифрованных данных в простой текст. Я бы вставил это в модуль, чтобы его можно было повторно использовать:

require 'openssl'
require 'base64'

module Obfuscate
  def self.included(base)
    base.extend self
  end

  def cipher
    OpenSSL::Cipher::Cipher.new('aes-256-cbc')
  end

  def cipher_key
    'blah!'
  end

  def decrypt(value)
    c = cipher.decrypt
    c.key = Digest::SHA256.digest(cipher_key)
    c.update(Base64.decode64(value.to_s)) + c.final
  end

  def encrypt(value)
    c = cipher.encrypt
    c.key = Digest::SHA256.digest(cipher_key)
    Base64.encode64(c.update(value.to_s) + c.final)
  end
end

Так что теперь ваши модели должны выглядеть примерно так:

class MyModel < ActiveRecord::Base
  include Obfuscate

  def to_param
    encrypt id
  end
end

Тогда в вашем контроллере, когда вам нужно найти запись по зашифрованному идентификатору, вы будете использовать что-то вроде этого:

MyModel.find MyModel.decrypt(params[:id])

Если вы хотите зашифровать / расшифровать идентификаторы без их сохранения в базе данных, это, вероятно, самый простой способ.

5 голосов
/ 16 февраля 2012

Вместо числовых идентификаторов используйте какой-нибудь дружественный URL-адрес или читабельный слаг. В этом отделе можно выбрать много инструментов . Они не только более дружелюбны к вашим пользователям, но и удачно подобранные слизни могут дать хорошее преимущество поисковым системам.

4 голосов
/ 10 февраля 2013

Вот гем, который хранит его в числовом формате, не требует миграций базы данных и изменений маршрутизации: https://github.com/namick/obfuscate_id


Я обнаружил, что этот драгоценный камень не работает вместе с некоторыми другими драгоценными камнями, особенно paper_trail. Это связано с тем, что он заменяет метод find, а paper_trail вызывает find с фактическим идентификатором записи.

Так что я использовал функциональность gem "scatter_swap", но не все остальное. Вот модель:

require 'obfuscate_id/scatter_swap'

class Page < ActiveRecord::Base
  # This is a random number that, if changed, will invalidate all existing URLs. Don't change it!
  @@obfuscate_spin = # random number here, which is essentially the encryption key

  ##
  # Generate URL parameter to be used in the URL as the "id"
  def to_param
    # Use the obfuscate_id gem's class to "spin" the id into something obfuscated
    spun_id = ScatterSwap.hash(self.id, @@obfuscate_spin)

    # Throw any additional attributes in here that are to be included in the URL.
    "#{spun_id} #{name}".parameterize
  end

  def self.find_by_slug!(slug)
    spun_id = slug[/^[0-9]+/]
    begin
      find_by_id! ScatterSwap.reverse_hash(spun_id, @@obfuscate_spin)
    rescue ActiveRecord::RecordNotFound => e
      raise ActiveRecord::RecordNotFound, "Couldn't find matching Page."
    end
  end
end

А в контроллере:

class PagesController < InheritedResources::Base
  # Find the page using its URL slug
  before_filter :find_page, except: [:index, :create, :new]

  def find_page
    @page = Page.find_by_slug! params[:id]

    # If the URL doesn't match exactly, and this is a GET.
    # We'll redirect to the new, correct URL, but if this is a non-GET, let's let them finish their request instead.
    if params[:id] != @page.to_param && request.get?
      redirect_to url_for({ id: @page.to_param }), status: 301
    end
  end
end

В качестве альтернативы перенаправлению, которое происходит там, вы можете просто включить канонический URL на странице. В перенаправлении есть ошибка игнорирования любых параметров запроса в URL. Это не было проблемой для моего проекта, так как у меня его не было. Но канонический URL был бы лучше.

4 голосов
/ 14 октября 2012

Прочитав пост @ siannopollo , я создал драгоценный камень на основе идеи его поста (но с некоторыми улучшениями): https://github.com/pencil/encrypted_id

4 голосов
/ 16 февраля 2012

Довольно просто генерировать уникальные случайные идентификаторы для ваших записей, используя генератор случайных строк или простой вызов Digest::SHA1.hexdigest, который дает достаточно случайные и криптографически уникальные результаты.

Например, вы можете создать дополнительный столбец с именем ident или unique_id, в котором хранятся ваши открытые идентификаторы. Затем вы можете переписать to_param, чтобы использовать вместо этого:

class MyModel < ActiveRecord::Base
  before_create :assign_ident

  def self.from_param(ident)
    find_by_ident(ident)
  end

  def to_param
    self.ident
  end

protected
  def assign_ident
    self.ident = Digest::SHA1.hexdigest(SecureRandom.random_number(1<<256).to_s)
  end
end

Теоретически существует вероятность столкновения на SHA1, но шансы настолько астрономически малы, что вы более склонны к сбою программного обеспечения из-за ошибки памяти или аппаратного сбоя. Вы можете проверить это, чтобы убедиться, что оно соответствует вашим потребностям, сгенерировав несколько миллиардов идентификаторов, чтобы увидеть, сталкиваются ли они когда-либо, чего они не должны. 256-битное случайное число должно обеспечить достаточный объем данных для алгоритма SHA1 для анализа.

...