PHPで簡単なログイン機能を作る（パスワードのハッシュ化も）

##はじめに
自分でPHPを使ってログイン機能を作ったので、一連の流れをまとめました。
間違えていることなどがあれば、教えてくださるとありがたいです。
PHPのバージョンは７.3を使用しています。
password_hash関数とpassword_verify関数を使ってパスワードのハッシュ化とパスワードの認証をやっていきます。

##そもそもハッシュ化とは
ユーザーのパスワードなど、大切な情報をデータベースに格納する時に、元データをハッシュ関数と呼ばれるものを利用して変換することです。

##ハッシュ関数の特徴
以下の２つが大きな特徴です。
● 元データが同じならば変換後は必ず同じ値になる
● 変換後の値から元データの値を復元することが不可能である

##password_hash関数の書き方
```
 $password = password_hash([ユーザーが入力したpassword], PASSWORD_DEFAULT);
```
自分はこのように書きました。```$password```この変数をDBに格納することで、ハッシュ化されたパスワードを保存することが可能です。ちなみに変数名は何でも大丈夫です。

##注意点
デフォルトのアルゴリズムを使ってパスワードをハッシュするので、現時点でのデフォルトは 
BCRYPT で、その結果は 60 文字になります。
デフォルトは、今後変わる可能性があることに注意しましょう。結果が
60文字以上になっても対応できるようにしておきましょう (255 あたりが適切です)
と公式のページに書いていました。　詳しくは、公式ページを見てください。[password_hash](https://www.php.net/manual/ja/function.password-hash.php)

##DB設定
DBにuserの情報を追加するテーブルを作成します。カラムはシンプルに名前とパスワードのみにしました。

|user_name  |password  |
|---|---|
|-  |-  |

##コード例
```
<?php

   if(isset($_POST['user_name'])){
     $user_name = $_POST['user_name'];
   }

   if(isset($_POST['user_password'])){
     $user_password = password_hash($_POST['user_password'], PASSWORD_DEFAULT);
   }
?>

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>登録画面</title>
</head>
  <body>

      <!-- ユーザーネーム -->
      <form method = "post">
        <p class="name">ユーザーネーム：
          <input type="text" name="user_name"></p>   

        <!-- パスワード -->
        <p class="password">パスワード：
          <input type="password" name="user_password"></p>
          <input type="submit" value="登録する">
      </form>
  
  </body>
</html>
```
あとはデータベース接続して、それぞれの変数をテーブルに保存することでハッシュ化は完了です。

##テーブルの中身

|user_name  |password  |
|---|---|
|sample  |0$8U6sqRKNF5iYMJ.8zSQ8GOsk2efw2ZFWWKRtDSMGIspTaBOOYcn |

user_nameはそのままで、passwordのみランダムな文字列に変換されています。
これで、登録は完了です

##ログイン認証の手順
登録された、データを使ってログインの仕組みを作ります。

```
      <form method = "post">
        <p class="name">ユーザーネーム：
          <input type="text" name="user_name"></p>   

        <p class="password">パスワード：
          <input type="password" name="user_password"></p>
          <input type="submit" value="ログインする">
      </form>
```
登録と同じように、ユーザーネームと、パスワードを入力できるフォームを用意します。
認証では、password_verify関数を使います。

##password_verify関数の使い方
```
password_verify('入力されたパスワード', 'DBに保存したhash化したパスワード')
```
このように引数が二つ必要になります。

##コード例
```
<?php

   if(isset($_POST['user_name'])){
     $user_name = $_POST['user_name'];
   }

   if(isset($_POST['user_password'])){
     $user_password = password_hash($_POST['user_password'], PASSWORD_DEFAULT);
   }


 try {
      // データベースに接続
          $dbh = new PDO($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'));
          $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
          $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
                      
          $sql = 'SELECT * FROM users WHERE user_name = :user_name';
          $stmt = $dbh->prepare($sql);
          $stmt->execute(array(':user_name' => $user_name));
          $user = $stmt->fetch(PDO::FETCH_ASSOC);
              
      }catch(PDOException $e){
          echo '接続できませんでした。理由：'.$e->getMessage();
      }

      if($user === false || password_verify($user_password, $user['password']) === false){
          echo 'ログインできませんでした';
        }
        else{
          echo 'ログイン成功';
        }
?>
```
SELECT文が失敗したり、passwordが間違っていると、ログインできないような条件をif文で書きました。

##最後に
これで、完全に安全というわけではなく、辞書攻撃など弱点もあるので、上記のコードでは、ほとんどバリデーションをしていませんが、なるべく辞書にないような、文字の組み合わせをユーザーに登録させるよう、バリデーションをしていくことが必要だと思います。