【问题标题】:Password hashed twice on edit - CakePHP编辑时密码哈希两次 - CakePHP
【发布时间】:2013-02-01 08:19:51
【问题描述】:

我有一个用户编辑视图。当人们访问此视图时,它的密码块中有散列密码。

如果您点击保存,它(显然)会再次对密码进行哈希处理,因为这在我的用户模型中。

public function beforeSave($options = array()) {
    if(!empty($this->data['User']['password'])) {
        $this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
    }
    return true;
}

但我不想让它散列两次(因为这意味着密码已更改)。我更改了编辑视图并将array('value' => '','autocomplete'=>'off') 添加到密码字段中。现在当我保存它时,它会在数据库中保存一个空白字符串。我认为它会阻止它使用beforeSave 函数中的if(!empty($this->data['User']['password'])) 语句来执行此操作。

如何防止密码被双重哈希?

【问题讨论】:

    标签: php authentication cakephp-2.0


    【解决方案1】:

    解决方案相当简单。只需将 beforeSave 中的 if 语句更改为:

    public function beforeSave($options = array()) {
        if(!empty($this->data['User']['password'])) {
            $this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
        }
        return true;
    }
    

    到:

    public function beforeSave($options = array()) {
        if(!empty($this->data['User']['password'])) {
            $this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
        } else {
            unset($this->data['User']['password']);
        }
        return true;
    }
    

    【讨论】:

      【解决方案2】:

      比较哈希值:

      您可以将保存在数据库中的哈希值与密码字段中的文本进行比较。如果您没有触摸密码(更改密码字段),哈希值应该匹配。假设您保存的散列是xyz,而密码散列中加载的散列仍然保持不变,xyz;在这种情况下,您不必重新散列任何内容,并且散列应该保持不变。

      在另一种情况下,假设您保存的哈希是xyz,但您将密码html字段编辑为abc;在这种情况下,您必须重新设置新密码 (abc),然后替换数据库记录中的旧密码。

      所有这些都可以翻译成如下代码:

      public function beforeSave($options = array()) {
      
          if (isset($this->data[$this->alias]['password'])) {
      
              if(isset($this->data[$this->alias]['id'])) {
                  $id = $this->data[$this->alias]['id'];
                  $user = $this->findById($id);
              } else {
                  $id = false;
              }
      
              if(!$id || $this->data[$this->alias]['password'] != $user['User']['password']) {
                  $passwordHasher = new SimplePasswordHasher();
                  $this->data[$this->alias]['password'] = $passwordHasher->hash(
                      $this->data[$this->alias]['password']
                  );
              }
      
          }
      
          return true;
      }
      

      问候, M

      【讨论】:

        【解决方案3】:

        在您的视图中使用备用字段名称(例如 $this->Form->password('pwd')

        public function beforeSave($options = array()) {
            if (!empty($this->data[$this->alias]['pwd'])) {
                $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['pwd']);
         }
            return true;
        }
        

        这可以防止密码被覆盖/空白。 信用、解释和其他最佳实践at this link.

        【讨论】:

          【解决方案4】:

          通常用户编辑页面与您描述的不同。如果您存储散列密码,则密码永远不会在编辑视图中返回。

          如果用户想要更改他的密码,一个表单通常包含 3 个字段

          • 旧密码
          • 新密码
          • 新密码(检查是否与之前的密码相同)

          接下来会发生什么:

          • 检查两个新密码是否相等
          • 散列旧密码
          • 哈希新密码
          • 检查旧密码散列是否等于数据库中的散列...如果是,旧散列将被新散列替换

          在您的示例中,您无法区分散列和常规密码,因此您的代码可能永远不会在其当前状态下工作,因为散列也只是一个字符串文字。

          【讨论】:

          • 事情是这样的,这个表格是供我们的 IT 部门在用户要求时重置用户密码的。但 IT 也可以更改用户的姓名或组别。所以不确定他们是否会更改密码。密码可能会在用户编辑时更改,也可能不会。那么当它没有改变时我会怎么做?在我的代码中,我检查该字段是否为空,如果是,我不想更改数据库值。但确实如此。它删除 DB 值并保存一个空字符串。
          • 那你可能需要检查sql语句了。
          • 哇,在阅读了数据是如何发送到数据库的之后,如何让它不保存空字段是有意义的。只需声明unset($this->data['User']['password'],如果密码字段为空,它将从 SQL 语句中删除。
          【解决方案5】:
          public function beforeSave($options = array()) {
              if(AuthComponent::user('id') == null)
                  if (isset($this->data[$this->alias]['password'])) {
                      $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
                  }
                  return true;
          }
          

          【讨论】: