【ログイン機能から学ぶ】PHPでパスワードをハッシュ化する方法を初学者向けに解説します

今日はデータベースへのパスワード登録をおこなっていきます。

僕も初学者の時は知らなかったのですが、パスワードは直で登録してはいけないんです。

理由は、データベースを攻撃された時にユーザーのパスワードが全て盗まれてしまうからです。

そのため、パスワードを暗号化して登録する必要があるんですね。

このことを、パスワードをハッシュ化すると言ったりします。

ここで覚えておきましょう。

今日の記事は、

  • パスワードの暗号化はどうやるの?
  • 暗号化したパスワードはどうやって確認するの?
  • ユーザーのログイン機能を簡単に理解したい

こんな方におすすめです。

それでは、やっていきましょう。

ユーザー会員登録フォームを作成しよう

まずはデータベースから作成しましょう。

> create database salary;
> use salary;
> create table users (id int primary key auto_increment, name varchar(255),email varchar(255), password varchar(100));

続いてはユーザー会員登録フォームの作成です。

<?php
error_reporting(0);
include "Warning.php";
if (!empty($_POST)) {
    if ($_POST['name'] === '') {
        $errors['name'] = 'blank';
    }
    if ($_POST['email'] === '') {
        $errors['email'] = 'blank';
    }
    if ($_POST['password'] === '') {
        $errors['password'] = 'blank';
    }
    if (strlen($_POST['password']) < 6) {
        $errors['password'] = 'length';
    }

    if (empty($errors)) {
        try {
            $db = new PDO('mysql:dbname=salary;host=localhost;charset=utf8', 'root', '');
            $userh = $db->prepare('insert into users set name=?, email=?, password=?');
            $userh->execute(array($_POST['name'], $_POST['email'], password_hash($_POST['password'], PASSWORD_DEFAULT)));
            header('Location: ../index.php');
            exit();
        } catch(Exception $e) {
            echo "接続エラー". $e->getMessage();
        }
    }
}
?>
<!doctype html>
<html lang="ja">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

        <title>Hello, world!</title>
    </head>
    <body>
    <?php include('../shared/header.html'); ?>
    <div class="container mt-5">
        <form action="" method="POST">
            <div class="form-group">
                <label for="exampleInputName">Name</label>
                <input type="text" class="form-control" name="name" value="<?php print($_POST['name']); ?>" id="exampleInputEmail1" aria-describedby="nameHelp" placeholder="Enter name">
                <?php if ($errors['name'] === 'blank'): ?>
                <p class="text-danger">名前を入力してください</p>
                <?php endif; ?>
            </div>
            <div class="form-group">
                <label for="exampleInputEmail1">Email address</label>
                <input type="email" class="form-control" name="email" value="<?php print($_POST['email']); ?>" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
                <?php if ($errors['email'] === 'blank'): ?>
                <p class="text-danger">メールアドレスを入力してください</p>
                <?php endif; ?>
                <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
            </div>
            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" class="form-control" name="password" value="<?php print($_POST['password']); ?>" id="exampleInputPassword1" placeholder="Password">
                <?php if ($errors['password'] === 'blank'): ?>
                <p class="text-danger">パスワードを入力してください</p>
                <?php endif; ?>
                <?php if ($errors['password'] === 'length'): ?>
                <p class="text-danger">パスワードは6文字以上で入力してください</p>
                <?php endif; ?>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    </body>
</html>

バリデーションの記述に関しては、【超初学者向け】PHPで入力フォームにバリデーションをかけるで紹介していますので、ご参考ください。

ちなみに、なんとなくビューがダサかったので、bootstrapを使って簡単に装飾しています(笑)

今回重要なのが、以下の記述です。

$userh->execute(array($_POST['name'], $_POST['email'], password_hash($_POST['password'], PASSWORD_DEFAULT)));

PHPにはパスワードをハッシュ化するために、メソッドが用意されています。

プログラミングを教えていると、簡単な実装ほど忘れるので、しっかり自分で書いてみてくださいね。

ログイン機能を実装しよう

次はハッシュ化されたパスワードの確認方法をみていきましょう。

<?php
error_reporting(0);
include "Warning.php";
if (!empty($_POST)) {
    if ($_POST['email'] === '') {
        $errors['email'] = 'blank';
    }
    if ($_POST['password'] === '') {
        $errors['password'] = 'blank';
    }

    if (empty($errors)) {
        try {
            $db = new PDO('mysql:dbname=salary;host=localhost;charset=utf8', 'root', '');
            $emailh = $db->prepare('select * from users where email=?');
            $emailh->execute(array($_POST['email']));
            $user = $emailh->fetch(PDO::FETCH_ASSOC);
            if (password_verify($_POST['password'], $user['password'])) {
                header('Location: ../index.php');
                exit();
            } else {
                $errors['login'] = 'fail';
            }
        } catch(Exception $e) {
            echo "接続エラー". $e->getMessage();
        }
    }
}
?>
<!doctype html>
<html lang="ja">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

        <title>Hello, world!</title>
    </head>
    <body>
    <?php include('../shared/header.html'); ?>
    <?php if ($errors['login'] === 'fail'): ?>
        <div class="alert alert-danger alert-dismissible fade show" role="alert">
            <strong>ログインに失敗しました</strong>
            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                <span aria-hidden="true">×</span>
            </button>
        </div>
    <?php endif; ?>
    <div class="container mt-5">
        <form action="" method="POST">
            <?php if ($errors['login'] === 'fail'): ?>
            <p class="text-danger">パスワードかメールアドレスが間違っています</p>
            <?php endif; ?>
            <div class="form-group">
                <label for="exampleInputEmail1">Email address</label>
                <input type="email" class="form-control" name="email" value="<?php print($_POST['email']); ?>" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
                <?php if ($errors['email'] === 'blank'): ?>
                <p class="text-danger">メールアドレスを入力してください</p>
                <?php endif; ?>
                <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
            </div>
            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" class="form-control" name="password" value="<?php print($_POST['password']); ?>" id="exampleInputPassword1" placeholder="Password">
                <?php if ($errors['password'] === 'blank'): ?>
                <p class="text-danger">パスワードを入力してください</p>
                <?php endif; ?>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    </body>
</html>

またまたコードが長いですが、重要な部分は以下です。

 if (password_verify($_POST['password'], $user['password'])) {

これまたPHPがパスワード確認用のメソッドを用意してくれています。

プログラミング言語ってよくできてますよね。

本当に感動します。

まとめ

  • password_hashメソッドでパスワードを暗号化する
  • password_verifyメソッドでパスワードの一致を確認する

今日は以上です。

コピペするとすぐに確認できるので、ぜひデバックしてながら作成してみてください。

参考:

【PHP】パスワードのハッシュ化とパスワードの認証をする方法