在Rails中实现完全匿名

时间:2014-09-09 17:24:47

标签: ruby-on-rails

每个用户拥有多个角色,每个角色属于一个用户。用户可以随时创建和销毁新的角色。用户使用传统的电子邮件/密码组合登录/创建新会话,然后可以按照他们选择的角色进行操作(用户最多允许3个角色 - 在网站上称为“Dominos'”)。

每个角色扮演操作就像用户在任何其他网站上的操作(个人资料照片,制作帖子,发送消息等)。我希望无法确定每个角色所属的用户。我的问题是,如何实现这一目标?

此外,我不想要人物角色之间的任何联系。因此,我认为有必要确保不能确定角色身份。如果用户快速连续创建说三个角色,他们可能会有连续的角色ID,因此您可以合理地猜测具有连续角色ID的角色可能是由同一个用户创建的。

friendly_id gem隐藏了网址中的ID,但是仍然可以通过某种方式识别它们吗?

还应该考虑哪些其他因素?我真的不知道从哪个匿名目标开始。

修改

这就是我所拥有的:

用户模型:

class User < ActiveRecord::Base

  has_one  :ghost,    dependent: :destroy
  has_many :personas, dependent: :destroy

  before_create :create_remember_token
  before_save do
    email.downcase!
    callsign.downcase!
  end
  after_save do
    self.create_ghost unless ghost
  end

  validates :name, presence: true,
                   length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
  validates :email, presence:   true,
                    format:     { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
  validates :callsign, presence:   true,
                       length:     { maximum: 20 },
                       format:     { with: VALID_CALLSIGN_REGEX },
                       uniqueness: { case_sensitive: false }
  validates :password, length: { minimum: 6 }

  has_secure_password

  def User.new_remember_token
    SecureRandom.urlsafe_base64
  end

  def User.digest(token)
    Digest::SHA1.hexdigest(token.to_s)
  end

  private

    def create_remember_token
      self.remember_token = User.digest(User.new_remember_token)
    end

end

用户控制器:

class UsersController < ApplicationController

  before_action :signed_in_user,     only: [:index, :show, :edit, :update, :destroy]
  before_action :non_signed_in_user, only: [:new, :create]
  before_action :correct_user,       only: [:edit, :update]
  before_action :admin_user,         only: :destroy

  def index
    @users = User.paginate(page: params[:page])
  end

  def show
    @user = User.find(params[:id])
    @page_name = "user_page"
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      sign_in @user
      flash[:success] = "Welcome, " + @user.name
      redirect_to @user
    else
      render 'new'
    end
  end # create

  def destroy
    @user = User.find(params[:id])
    if ( current_user != @user )
      @user.destroy
      flash[:success] = "User deleted."
      redirect_to users_url
    else
      redirect_to @user, notice: "Suicide is not permitted, admin chappie. Hard cheese."
    end
  end

  def edit
  end

  def update
    if @user.update_attributes(user_params)
      flash[:success] = "Profile updated"
      redirect_to @user
    else
      render 'edit'
    end
  end # update

  private

    def user_params
      params.require(:user).permit(:name, :email, :callsign, :password, :password_confirmation)
    end

    # Before filters

    def non_signed_in_user
      if signed_in?
        redirect_to root_url, notice: "Nice try pal. You can't create a new user 
                                       if you're already signed in."
      end
    end

    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end

    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end

end

女神异闻录模特:

class Persona < ActiveRecord::Base

  belongs_to :user

  before_save :make_downcase_callsign

  validates :name, presence: true,
                   length:   { maximum: 50 }
  VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
  validates :callsign, presence:   true,
                       length:     { maximum: 20 },
                       format:     { with: VALID_CALLSIGN_REGEX },
                       uniqueness: { case_sensitive: false }
  validates :user_id, presence: true
  validates :persona_id, presence: true

  private

    def make_downcase_callsign
      return unless callsign
      self.callsign = callsign.downcase
    end

end # Persona

角色控制器:

class PersonasController < ApplicationController

  before_action :signed_in_user
  before_action :correct_persona,   only: [:edit, :update]
  before_action :correct_destroyer, only: :destroy
  before_action :too_many_personas, only: :create

  def index
    @personas = Persona.paginate(page: params[:page])
  end

  def show
    @persona = Persona.find(params[:id])
    @page_name = "domino_" + @persona.persona_id.to_s + "_page"
  end

  def new
    @persona = Persona.new
  end

  def create
    @persona = current_user.personas.build(persona_params)
    set_persona_id
    if @persona.save
      flash[:success] = "This is the moment of your creation, " + @persona.name
      redirect_to @persona
    else
      render 'new'
      end
  end

  def edit
    @persona = Persona.find(params[:id])
  end

  def update
    @persona = Persona.find(params[:id])
    if @persona.update_attributes(persona_params)
      flash[:success] = "Persona profile updated"
      redirect_to @persona
    else
      render 'edit'
    end
  end

  def destroy
    @persona = Persona.find(params[:id])
    @persona.destroy
    flash[:success] = "Persona deleted."
    redirect_to current_user
  end


  private

    def persona_params
      params.require(:persona).permit(:name, :callsign)
    end

    def correct_persona
      @persona = Persona.find(params[:id])
      redirect_to(root_url) unless current_user.id == @persona.user_id
    end

    def correct_destroyer
      @persona = Persona.find(params[:id])
      redirect_to(root_url) unless ( (current_user.id == @persona.user_id) || current_user.admin? )
    end

    def too_many_personas
      if ( current_user.personas.count >= 3 )
        flash[:message] = "Sorry, you're not allowed more than three dominos."
        redirect_to(root_url)
      end
    end

    def set_persona_id
      if ( current_user.personas.count == 0 )
        @persona.persona_id = 1
        return
      end
      if ( current_user.personas.count == 1 )
        if current_user.personas.first.persona_id == 1
          @persona.persona_id = 2
          return
        else
          @persona.persona_id = 1
          return
        end
      end
      if ( current_user.personas.count == 2 )
        if current_user.personas.first.persona_id == 1
          if current_user.personas.second.persona_id == 2
            @persona.persona_id = 3
            return
          else
            @persona.persona_id = 2
            return
          end
        end
        if current_user.personas.first.persona_id == 2
          if current_user.personas.second.persona_id == 1
            @persona.persona_id = 3
            return
          else
            @persona.persona_id = 1
            return
          end
        end
        if current_user.personas.first.persona_id == 3
          if current_user.personas.second.persona_id == 1
            @persona.persona_id = 2
            return
          else
            @persona.persona_id = 1
            return
          end
        end
      end
    end

end # class PersonasController < ApplicationController

我想知道如何组织所有内容,以便恶意用户无法确定哪个角色属于哪个用户。这个想法是,用户可以根据自己的喜好创建任意数量的角色,并且与其他(可能是恶意的)用户之间没有明显的联系。

1 个答案:

答案 0 :(得分:2)

我不认为这会完全回答你的问题,因为我不认为你已经很好地定义了问题的范围。

但是,让我们从较高的层面思考应用程序应该如何保证我作为用户的匿名性。

1)任何用户都无法通过网址识别我的身份。

  • 这看起来很简单。如果每个Persona都有一个随机生成的密钥,并且该密钥用作URL中的id,则应满足此条件。

2)任何用户都不应该通过网站行为对我的身份进行反向工程。

  • 这有点棘手。您希望确保您没有跟踪有关登录IP地址的任何信息(我认为如果您正在使用该宝石,Devise会这样做),因为这可能是一个问题。
  • 此外,您可能想要考虑是否要为该角色的任何行为加时间戳,因为您至少可以从这些时间戳中对某人所在的世界的可能方进行逆向工程。
  • 您还希望避免使用任何第三方进行网站分析(例如Google Analytics),因为这样做会打败整个点。
3)如果坏人获得了数据库的副本,他/她就不应该对Persona的身份进行逆向工程。

  • 现在我们开心了!显然,您正在对密码进行哈希处理,但现在我们希望加密任何可识别的唯一身份信息。这意味着姓名,用户名,电子邮件,出生日期等。我真的喜欢称为symmteric加密(https://github.com/reidmorrison/symmetric-encryption)的宝石,它将您的加密密钥分成两个独立的部分,以便有人需要您的数据库,你的ruby代码,以及一个单独的密钥文件,将它们重新组合在一起。

4)其他功能

  • 我不知道你是否熟悉Kinja(它是一个评论平台),但他们知道一个刻录机帐户。基本上,您创建一个用户名,获得一个16位密钥作为密码,但是如果密码丢失,您就无法更改密码或恢复密码。从匿名的角度来看,如果某人获得了对您电子邮件的访问权限,他们就无法使用“忘记密码”这一点,那就很好了。&#34;功能作为获取对该刻录机帐户的访问权限的一种方式。所以根据你的需要,你可以实现类似的东西。

5)SSL

  • 显然,您希望网站的每个方面都落后于SSL。

这些只是我对任何声称是匿名&#34;的事情的主要考虑因素。这实际上归结为您尝试保证的匿名程度。对于博客网站来说,这都是 INSANE 的匿名级别,但从挫败NSA的角度来看,这是不足够的。所以考虑你的观众!