【问题标题】:How do I obfuscate the ids of my records in rails?如何在 Rails 中混淆记录的 ID?
【发布时间】:2012-02-16 03:15:22
【问题描述】:

我正在尝试弄清楚如何在 rails 中混淆我的记录的 ID。

例如:一个典型的路径可能看起来像http://domain/records/1,因此人们很容易推断出如果他们只是创建一条新记录,该网站获得了多少流量。

我使用的一个解决方案是用盐对 id 进行哈希处理,但由于我不确定该函数是否是双射的,所以我最终将它存储在数据库的另一列中并仔细检查唯一性。

我正在考虑的另一个选项是生成一个随机散列并将其存储为另一列。如果它不是唯一的......只需生成另一个。

最好的方法是什么?

【问题讨论】:

    标签: ruby-on-rails database ruby-on-rails-3 data-binding obfuscation


    【解决方案1】:

    您可以使用内置的 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
    

    然后在您的控制器中,当您需要通过加密的 id 查找记录时,您将使用如下内容:

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

    如果您希望加密/解密 id 而不将它们存储在数据库中,这可能是最简单的方法。

    【讨论】:

    • 这太棒了 :) 有什么办法可以去掉加密字符串末尾多余的“==”?
    • 这是 Base64 加密的一部分,因此需要解密 id。您可以在 encrypt 方法中将其删除,然后在 decrypt 方法中重新添加,但这可能更容易出错(因为我不确定 Base64 always 是否包含 @987654327 @ 在字符串的末尾)。
    • Base64 填充 = 如果被编码的位数不精确匹配。根据输入的长度,它们将有 0 个、1 个或 2 个。
    • 这可能会产生一个带有“/”的 id,这会破坏您的路线。 (可能还有其他一些字符组合)。请改用urlsafe_encode64urlsafe_decode64
    • @Anthony 我认为他只是意味着他在他的 User 类中覆盖了 find 方法。
    【解决方案2】:

    不要使用数字 ID,而是使用某种友好的 url 或人类可读的 slug。该部门有lots of tools可供选择。它们不仅对您的用户更友好,而且精心挑选的 slug 可以为搜索引擎带来很大的优势。

    【讨论】:

      【解决方案3】:

      这是一个保持数字的 gem,不需要数据库迁移,也不需要更改路由:https://github.com/namick/obfuscate_id


      我发现这个 gem 不能与其他一些 gem 协同工作,尤其是 paper_trail。这是因为它替换了 find 方法的方式,而 paper_trail 导致 find 使用实际记录 id 被调用。

      所以我一直在使用 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】:

        使用随机字符串生成器或对Digest::SHA1.hexdigest 的简单调用可以很容易地为您的记录生成唯一的随机标识符,这会产生合理的随机和加密唯一结果。

        例如,您可以创建一个名为 identunique_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 算法提供足够的数据量。

        【讨论】:

        • 我很高兴伟大的思想家想法一致。这基本上就是我想要做的。
        • 我认为 OP 也希望能够在不依赖查找的情况下解密混淆的结果。
        【解决方案5】:

        阅读@siannopollo's post后,我根据他的帖子的想法创建了一个Gem(但有一些改进):https://github.com/pencil/encrypted_id

        【讨论】:

          【解决方案6】:

          只是因为这里没有提到:您可以简单地使用 UUID (wikipedia article)

          在 Rails 中有多种使用 UUID 作为主键的方法,具体取决于您的 Rails 版本和数据库引擎。很容易找到。

          作为一种可能性,如果您过于依赖现有的整数主键,您也可以在表中添加一个 UUID,并通过覆盖Model#to_param@ 让您的模型在生成 URL 时自动使用它987654322@

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-08-05
            • 2023-03-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多