[PHP] 簡易ログイン機能を実装してみた

## はじめに
phpを使用して、簡易的なログイン認証を実装してみました。(技術力低いのでやばいかもしれないが...)

### **基本的な流れ**
1. idとpasswordを入力する。
2. 入力したidとpasswordがデータベースに紐づけされて登録されているかをチェック。
3. 登録されていたらログインし、以外はエラーを表示する。

まぁ簡易的なのでそんなかんじです笑笑

## 該当ソースコード
### 1. login.php
ログインするために、ID、パスワードを入力する画面です。

```php:ログイン画面(login.php)
<?php
    require_once("function.php");
    session_start();
    header("Content-type: text/html; charset=utf-8");
?>

<!doctype html>
<html lang="ja">
<head>
    <link rel="stylesheet" type="text/css" href="style1.css">
</head>
<div class="form-wrapper">
    <h1>ログイン</h1>
    <?php
        if (isset($_SESSION["error_status"])) {
            if ($_SESSION["error_status"] == 1) {
                echo "<h2 style='color:red'>IDまたはパスワードが異なります。</h2>";
            }
            if ($_SESSION["error_status"] == 2) {
                echo "<h2 style='color:red'>不正なリクエストです。</h2>";
            }
    
            //エラー情報のリセット
            $_SESSION["error_status"] = 0;
            }
    ?>

    <form action="login_check.php" method="post">
    <div class="form-item">
        <label for="ID"></label>
        <input type="text" name="id" required="required" placeholder="ID">
    </div>
    <div class="form-item">
        <label for="password"></label>
        <input type="password" name="password" required="required" placeholder="パスワード">
    </div>
    <div class="button-panel">
        <input type="submit" class="button" title="ログイン" value="ログイン">
    </div>
    </form>
    
</div>
</html>
```

### 2. login_check.php
入力したID、パスワードが事前に登録されているユーザのものなのかを認証します。

>コメント欄での指摘通り、//ログイン失敗 以下のif文がwhileループに入っていると意味がないので、ループ外に出しました。

```php:ログイン認証(login_check.php)
<?php
  require_once("function.php");
  session_start();
  header("Content-type: text/html; charset=utf-8");

  //パラメーター取得
  $id = $_POST['id'];
  $password = $_POST['password'];

  //ログイン判定

  //DB接続
  try {
    /*dbname: test
      host: localhost (DBをローカル環境に置いている)
      username: root
      password: '' (未設定)
    */
    $pdo = new PDO('mysql:dbname=test;host=localhost;charset=utf8mb4', 'root', '', 
    [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]
    );

    $stmt = $pdo->query('SELECT id, password, name FROM user');

    $count = 0;

    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
      $db_id = $row["id"];
      $db_password = $row["password"];
      $db_name = $row["name"];
      $count = $count + 1;

      // パスワードチェック
      // パスワードが一致したら「1」を、不一致なら「0」を返す
      if(password_verify($password, $db_password)) {
        $hantei = 1;
      } else {
        $hantei = 0;
      }

      if ($id == $db_id && $hantei == 1) {
          // ログイン成功
          // セッションに格納することで、ログインユーザ情報をログイン先画面で使用することが可能
    
          //セッション ID の振り直し
          session_regenerate_id(true);
        
          //セッションに ID を格納
          $_SESSION['id'] = $id;

          //セッションに name を格納
          $_SESSION['name'] = $db_name;

          //CSRF のトークン作成
          $_SESSION["token"] = get_csrf_token();
    
          //リダイレクト
          header("HTTP/1.1 301 Moved Permanently");
          header("Location: 〇〇.php"); // ログイン先
      
          exit();
      }
    }

    //ログイン失敗
    if ($count == 0) {
        $_SESSION["error_status"] = 1;
        header("HTTP/1.1 301 Moved Permanently");
        header("Location: login.php"); 
        exit();
    }
  } catch (PDOException $e) {
    exit('データベース接続失敗。'.$e->getMessage());
  }

  //ログイン失敗
  $_SESSION["error_status"] = 1;
  header("HTTP/1.1 301 Moved Permanently");
  header("Location: login.php"); 
    
?>
```

### 3. function.php

```php:function.php
<?php
//define("DNS","mysql://user01:0000@localhost/Test?charset=utf8");
define("SERVER", "localhost");
define("STRETCH_COUNT", 1000);

/*
* CSRF トークン作成
*/
function get_csrf_token() {
  $TOKEN_LENGTH = 16;  //16*2=32byte
  $bytes = openssl_random_pseudo_bytes($TOKEN_LENGTH);
  return bin2hex($bytes);
}

/*
* パスワードをソルト＋ストレッチング 
*/
function strechedPassword($salt, $password){
    $hash_pass = "";

    for ($i = 0; $i < STRETCH_COUNT; $i++){
        $hash_pass  = hash("sha256", ($hash_pass . $salt . $password));
    }
    
    return $hash_pass;	
}

/*
* ソルトを作成
*/
function get_salt() {
  $TOKEN_LENGTH = 4;//4*2=8byte
  $bytes = openssl_random_pseudo_bytes($TOKEN_LENGTH);
  return bin2hex($bytes);
}

/*
* URL の一時パスワードを作成
*/
function get_url_password() {
  $TOKEN_LENGTH = 16;//16*2=32byte
  $bytes = openssl_random_pseudo_bytes($TOKEN_LENGTH);
  return hash("sha256", $bytes);
}
?>
```

## 環境
windows10 64bit
実行環境: xampp (mysql、php、apache等をまとめてインストールでき、インストールも簡単なので)

## おわりに
こういう系の記事は初めてなので、見よう見まねで書きました笑笑
抜けている箇所がありましたら、コメント欄で教えていただくとありがたいです。

次回は、ログインアカウントの登録機能について記事を書きたいと思います。
**https://qiita.com/N46_myHearter/items/3a34444ef140bc9cb0a8**