This is an old revision of the document!


Yii 2 Basic Application Template
Improved Password Security

By default, the basic template comes with plain text password support in file @app/models/User.php. To improve this to use a password hash with salt, we must do some changes.

Model file @app/models/User

class User extends \yii\base\Object implements \yii\web\IdentityInterface
{
    public $id;
    public $username;
    //public $password;   // Remove plain password support
    public $authKey;
    public $accessToken;
    public $passwordHash; // Use password hash. Generated like... password_hash("User'sPassword", PASSWORD_DEFAULT);
 
    private static $users = [
        '100' => [
            'id' => '100',
            'username' => 'admin',
            'authKey' => 'test100key',
            'accessToken' => '100-token',
            'passwordHash' => '$2y$10$/lVWm8iL07.zoBE.7nM8ueDSPiR8XwxyoAuZPfCclPZ3PscOXM.KK'  // 123admin
        ],
        ...
    ];
 
    /**
     * Find username for specified userid.
     *
     * @id integer  $id of user to search.
     * @return string Username if found, 'N/A' if not found.
     */
    public static function findUsername($id)
    {
        $usr = isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
        if (count($usr) > 0)  {
            return $usr->username;
        } else {
            return 'N/A';
        }
    }
 
    ...
 
    /**
     * Validates password
     *
     * @param  string  $password password to validate
     * @return boolean if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        //return $this->password === $password;   // disable plain password support
        return password_verify($password, $this->passwordHash);  // enable password hash support
    }
}

Model file @app/models/PasswordForm

<?php
 
namespace app\models;
 
use Yii;
use yii\base\Model;
 
/**
 * LoginForm is the model behind the login form.
 */
class PasswordForm extends Model
{
    //public $username;
    public $password;
    public $encrypted_password;
    //public $rememberMe = true;
 
    //private $_user = false;
 
 
    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            [['encrypted_password'], 'safe'],
            // password required
            [['password'], 'required'],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
        ];
    }
 
    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params)
    {
        if ($this->hasErrors()) {
            $this->addError($attribute, 'Invalid or unsupported password.');
        }
    }
 
    public function encrypt()
    {
        if ($this->validate()) {
            return password_hash($this->password, PASSWORD_DEFAULT);  // hash
            //return $this->password;                                 // plain
        }
        return false;
    }
}

Password generation view. This is a tool to help generate passwords for the User model. Copy the password here and enter it in file @app/models/User.php as a passworHash for the required user. Eg:

'passwordHash' => '$2y$10$/lVWm8iL07.zoBE.7nM8ueDSPiR8XwxyoAuZPfCclPZ3PscOXM.KK'  // 123admin

View @app/views/site/password

<?php
 
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\PasswordForm */
 
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
 
$this->title = 'Password Encryption';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-password">
    <?php if (!empty($encrypted_password)) : ?>
        <div class="alert alert-success" role="alert">
          <strong>Encrypted Password:</strong><pre><?= $encrypted_password ?></pre>
        </div>
    <?php endif; ?>
 
    <h1><?= Html::encode($this->title) ?></h1>
 
    <p>Enter a password to encrypt:</p>
 
    <?php $form = ActiveForm::begin([
        'id' => 'login-form',
        'options' => ['class' => 'form-horizontal'],
        'fieldConfig' => [
            'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>",
            'labelOptions' => ['class' => 'col-lg-1 control-label'],
        ],
    ]); ?>
 
        <?= $form->field($model, 'password')->passwordInput() ?>
 
        <div class="form-group">
            <div class="col-lg-offset-1 col-lg-11">
                <?= Html::submitButton('Encrypt', ['class' => 'btn btn-primary', 'name' => 'encrypt-button']) ?>
            </div>
        </div>
 
    <?php ActiveForm::end(); ?>
 
</div>

View @app/views/layout/main to call password view:

    echo Nav::widget([
        'options' => ['class' => 'navbar-nav navbar-right'],
        'items' => [
            ['label' => 'Home', 'url' => ['/site/index']],
            // Admin menu only
            !Yii::$app->user->isGuest && Yii::$app->user->identity->username === 'admin' ?
                ['label' => 'Admin', 'url' => ['/site/admin'], 
                    'items' => [
                        ...
                        ['label' => 'Encrypt User Password', 'url' => ['/site/password']],
                    ]] : 
                '',
        ],
    ]);
    NavBar::end();
?>                

Controller @app/controllers/SiteController to display password view:

use app\models\PasswordForm;
...
 
class SiteController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'actions' => [..., 'password'],
                        'allow' => true,
                        'roles' => ['@'],  // @ = Authenticated users
                    ],
                    ...
                ],
            ],
            ...
        ];
    }
 
    ...
    public function actionPassword()
    {
        $encrypted_password = '';
        $model = new PasswordForm();
        if ($model->load(Yii::$app->request->post()) && $model->encrypt()) {
            Yii::$app->session->setFlash('Password encrypted');
            $encrypted_password = $model->encrypt();
        }
 
        return $this->render('password', [
            'model' => $model,
            'encrypted_password'  => $encrypted_password,
        ]);
    }
}