質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
87.20%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

パスワード

パスワードは主に情報にアクセスする際に扱われます。主に、アクセス可能なユーザーを限定する手段として使われます。

ログイン

ログインは、ユーザーがコンピューターシステムにアクセスするプロセスの事を呼びます。

暗号化

ネットワークを通じてデジタルデータをやり取りする際に、第三者に解読されることのないよう、アルゴリズムを用いてデータを変換すること。

解決済

ログイン機能の実装: password_hashで暗号化したパスワードをpassword_verifyを使って照合する

forestGreen
forestGreen

総合スコア8

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

パスワード

パスワードは主に情報にアクセスする際に扱われます。主に、アクセス可能なユーザーを限定する手段として使われます。

ログイン

ログインは、ユーザーがコンピューターシステムにアクセスするプロセスの事を呼びます。

暗号化

ネットワークを通じてデジタルデータをやり取りする際に、第三者に解読されることのないよう、アルゴリズムを用いてデータを変換すること。

1回答

0評価

2クリップ

2531閲覧

投稿2021/02/14 01:42

編集2021/02/14 05:36

前提・実現したいこと

最近独学でプログラミングを始めたPHP初心者です。

現在、サーバーサイド言語としてPHPを使用してログイン機能のついた掲示板を作成しております。
password_hashとpassword_verifyを使うとパスワードを暗号化して扱えると知り、それらを用いてログイン機能を実装しようと試みましたが、うまくいきません。

周りにプログラミング(PHP)のことを相談できる人がおらず、またウェブで調べても解決法を見つけることができなかったため、質問しました。

※開発環境はXAMPP、掲示板利用時のユーザーアカウント管理用テーブル・投稿管理用テーブルはDB(MySQL)で行っております。

発生している問題・エラーメッセージ

エラーメッセージは出ていませんが、ユーザーログイン時にpassword_verifyを用いてユーザーが入力したパスワードと暗号化したパスワードの一致を確認するところがうまくいかず、困っております。
password_verifyを用いてパスワードの一致を確認する際、引数(ハッシュ値)の書き方に問題があるのではなうか?というところまでは突き止めたのですが、ではどのように記述したらよいのか、が分からない状況です。

※なお、password_hashを用いたパスワードの暗号化は出来ており、暗号化されたパスワードはDB内ユーザーアカウント管理のテーブルに問題なく登録されております。

該当のソースコード

password_verifyを使用しているソースコードはこちら↓です。
既存のアカウントに対してログインできるかチェックを行う(Logincheck.php)

PHP

<?php // session開始 session_start(); // DBと繋ぐ(別ファイルに記述) include "DBconnect.php";  // 入力パスワードとDBパスワードが一致した時にログイン処理を進める // ログインページでユーザーが入力した情報を取得 $user_name = $_POST["user_name"]; $user_password = $_POST["user_password"]; // DB内でユーザー名が一致するレコードを$sqlに代入 $sql = "SELECT * FROM stockuser WHERE user_name = '$user_name'"; // SQLを発行し、SQL文がDBに通ったかを判別 if($result = mysqli_query($conn , $sql)) {   // ユーザー名が一致したレコードを取り出す $row = mysqli_fetch_assoc($result); // implodeを用いて該当レコードが取り出せているかを確認 echo implode($row);   // 該当レコードからパスワードが取り出せているかを確認 echo $row["user_password"]; // 入力したパスワードと暗号化されたパスワードの一致が確認されれば、ログイン許可   // ここが間違っているのではないかと思います。 if(password_verify($user_password, $row["user_password"])) {     // 一致していれば"ログインOK"と画面に表示させる echo "ログインOK"; // $flagを使ってログインOK/NGでの動作を定義 $flag = true; } else { // 一致していなければ"ログインNG"と画面に表示させる echo "ログインNG";    // ログインNGのときはfalseとする $flag = false; } } else { echo mysqli_error($conn)."<BR>"; } // MySQLの切断 mysqli_close($conn); // 以降、画面遷移に関するパート  // password_verifyの部分を動作確認する際は以降の部分をコメントアウトしております // サーバー情報の記述 $host = $_SERVER['HTTP_HOST']; $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\'); // $flagがtrueなら、ログイン成功(main.phpにページ遷移) if($flag){ // user_idとuser_nameで連想配列を作り、sessionに載せる $user = array("user_id" => $row["user_id"] , "user_name" => $row["user_name"]); $_SESSION["user"] = $user; $extra = "main.php"; header("Location: http://$host$uri/$extra"); exit(); } // $flagがfalseなら、login2.html(ログインできませんでした のページ)に遷移 else{ // sessionを破壊 session_destroy(); // session破壊後に遷移先ページへの移動を行う $extra = "login2.php"; header("Location: http://$host$uri/$extra"); exit(); } ?>

関連ファイルの情報
DBと繋ぐためのファイル(DBconnect.php)

PHP

<?php // DBに繋ぐためのファイル // サーバへの接続情報を登録 $DBSERVER = "localhost:3307"; $DBUSER = "root"; $DBPASSWORD = ""; $DBNAME = "hellowordsdb"; //MySQLへの接続 $conn = mysqli_connect($DBSERVER , $DBUSER , $DBPASSWORD , $DBNAME); //接続時にエラーが発生していれば、エラーメッセージを表示 if(mysqli_connect_error()) { echo mysqli_connect_error(); exit(); } else { // 下のechoは後ほど削除(表示があるとページ遷移ができないのでコメントアウト) // echo "Connect OK"; } ?>

新規ユーザー登録時、パスワードは暗号化してDBに登録する(registAccount.php)

PHP

<?php // DBにユーザを新規登録するためのファイル // セッション開始 session_start(); // 新規登録ボタンが押された時の確認項目 if(isset($_POST["register"])){ // DB接続のためのファイルを読み込み include "DBconnect.php";   // ユーザー名、ユーザーアドレスの情報を新規登録ページから取得 $user_name = $_POST["user_name"]; $user_address = $_POST["user_address"]; // パスワードは暗号化して登録する $hash = password_hash($_SESSION["user_password"], PASSWORD_DEFAULT); // insert文の組み立て・sql文の発行 $sql = "INSERT INTO stockUser (user_name , user_address , user_password , enable) "; $sql .= " VALUES ('$user_name' , '$user_address' , '$hash' , 1)"; // 必要な情報が揃っていれば、ユーザ情報を登録 if($result = mysqli_query($conn , $sql) ){ // sessionに情報を積む $user = array("user_id" => mysqli_insert_id($conn) , "user_name" => $user_name); $_SESSION["user"] = $user; $host = $_SERVER['HTTP_HOST']; $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\'); $extra = "main.php"; // 一覧画面に転送 header("Location: http://$host$uri/$extra"); exit(); } // 情報入力が不足していた場合 else { // 入力画面に戻す $err_msg = mysqli_error($conn); $user_name = $_POST["user_name"]; $user_address = $_POST["user_address"]; include "newAccount.php"; } } // 戻るボタンが押された時は、ひとつ前の入力画面へ戻す else { $err_msg = ""; $user_name = $_POST["user_name"]; $user_address = $_POST["user_address"]; include "newAccount.php"; } ?>

ログインフォーム(login.php)

PHP

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" type="text/css" href="login.css?v=5"> <link rel="icon" href="images/wordcard.png"> <title>Hello!Words ログイン</title> </head> <body> <div id="container"> <h2>Hello! Words ログイン画面</h2> <p>ログイン情報を入力してください</p> <!-- action属性で情報の送り先となるページ(Logincheck.php)を指定 --> <form action="Logincheck.php" method="post"> <table> <tr> <th>ユーザー名</th> <td><input type="text" name="user_name"></td> </tr> <tr> <th>パスワード</th> <td><input type="password" name="user_password"></td> </tr> </table> <input class="buttons" type="submit" href="main.php" value="ログイン"> </form> <a href="top.php">トップへ戻る</a> </div> </body> </html>

ユーザー名またはパスワードが一致せずログインできなかった場合に表示するページ(login2.php)

PHP

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" type="text/css" href="login.css?v=4"> <link rel="icon" href="images/wordcard.png"> <title>Hello!Words ログイン</title> </head> <body> <div id="container"> <h2>Hello! Words ログイン画面</h2> <p>ログイン情報を入力してください</p> <!-- action属性で情報の送り先となるページ(Logincheck.php)を指定 --> <!-- エラー表示 --> <div class="err">ユーザー名またはパスワードが違います</div> <form action="Logincheck.php" method="post"> <table> <tr> <th>ユーザー名</th> <td><input type="text" name="user_name"></td> </tr> <tr> <th>パスワード</th> <td><input type="password" name="user_password"></td> </tr> <tr> <!-- <th><input type="submit" href="top.php" value="トップページに戻る"></th> --> <th><input class="buttons" type="submit" href="main.php" value="ログイン"></th> </tr> </table> </form> <a href="top.php">トップへ戻る</a> </div> </body> </html>

試したこと

Logincheck.php内に記載をしておりますが、レコードが問題なく取得できているかどうか、echoで画面に出力させて確認しました。

PHP

// implodeを用いて該当レコードが取り出せているかを確認 echo implode($row);   // 該当レコードからパスワードが取り出せているかを確認 echo $row["user_password"];

画面には以下↓のように取得したレコードの値が出力されます。

ユーザーID ユーザー名 ユーザーアドレス ユーザーパスワード(暗号化したもの)
ユーザーパスワード(暗号化したもの)
ログインNG

...この結果から、抽出したいレコード及びパスワードは取り出せているものの、"ログインNG"との表示が出るため、password_verifyがきちんと機能できていないのではないかと考えております。

補足情報(FW/ツールのバージョンなど)

データベースの情報
DB名: hellowordsdb
DBでユーザー管理を行うテーブルの情報は以下の通りです。
テーブル名: stockuser
カラム名: user_id, user_name, user_address, user_password, enable(有効なアカウントかを入力)

使用ツールについて
OS: Windows10
開発環境: XAMPP Control Panel v3.2.4
コーディングツール: Visual Studio Code

追記:

hoshi-takanori様からご指摘いただいた内容を試してみました(ご指摘の内容はコメント欄をご覧ください)。

  • まず、既存のアカウントでログインを行う際にパスワードを空文字でログインを試みたところ、以下↓の画面表示となりました。

 ユーザーID ユーザー名 ユーザーアドレス ユーザーパスワード(暗号化したもの)
ユーザーパスワード(暗号化したもの)
ログインOK

...上記 "ログインOK"との表示から、ログイン出来ており、その際のパスワードは空文字で登録されていた。ということが判明しました。

  • そこで、registAccount.phpファイル内にてパスワードハッシュ化を行う箇所のスーパーグローバル変数を以下のように変更

($_SESSION → $_POST)

PHP

// パスワードは暗号化して登録する $hash = password_hash($_POST["user_password"], PASSWORD_DEFAULT);

上記コードに変更後、テストを行った(新規ユーザー登録を行い、一旦ログアウトした後にログインを試みた)ところ、問題なくログインすることができました。

質問した内容について解決することができ、本当に助かりました。ご回答いただいた皆様、本当にありがとうございました。

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

hoshi-takanori

2021/02/14 01:50

暗号化の処理で、パスワードは $_SESSION["user_password"] ではなく $_POST["user_password"] では。 あと、$sql .= " VALUES ('$user_name' , '$user_address' , '$hash' , 1)"; という書き方は SQL インジェクションの脆弱性があります。ちゃんとプレースホルダーを使いましょう。
phoepsilonix

2021/02/14 02:53

見たところ、password_hashの部分には問題はないかと思いました。 データベースへ格納されているデータと不整合が起きていませんか? データベースに格納されているhashとログイン時に入力したpasswordにpassowrd_hashを使ってみて、一致するか、調べてみてはどうでしょうか? 上の方のコメントにあるとおり、SQLインジェクション対策のプレースホルダーはマストです。 またmysql_ 関数は非推奨になっています。代替方法やプレースホルダーのことも含めて、こちらに詳しい解説がありますので、ご覧になってみてください。 https://liginc.co.jp/344413
m.ts10806

2021/02/14 03:14 編集

PHPマニュアルを確認された上でしょうか。 phoepsilonixさん mysql_は非推奨どころか7以降では削除されたものですが質問者さんはmysqli_を使っているので問題ないです。
forestGreen

2021/02/14 03:28

hoshi-takanori様 コメントありがとうございます。 暗号化の際(password_hash)に$_SESSIONでなく$_POSTを使用してみましたが、同様の結果でした。 2つの変数に関して自分の理解は以下の通りです。 $_SESSION: 複数のページ間で値を引き継ぐことができる $_POST: 値は1対1の画面間で受け渡すのみで、複数のページ間で値を引き継ぐことはできない 新規登録後はログイン状態を保持しなければならないため、$_SESSIONを使うのかなと思っておりました(初学者で理解不足ですので自分でもう少し調べてみます)。 またSQLインジェクションに関してご指摘くださりありがとうございます。セキュリティ面は勉強を始めたばかりで知らないことだらけですので、実装できるようによく調べるようにします。
forestGreen

2021/02/14 03:39

phoepsilonix様 コメントありがとうございます。 DBに入れたデータとの不整合は確認できておりませんでした。アドバイスいただいたように、ログイン時に入力した値をハッシュ化し、DBに格納されているデータと一致するか調べてみます。 また分かりやすい解説されたサイトをご紹介いただきありがとうございます。今回、mysqlでなくmysqliを使用するようにしておりますが、PDOの実装はできておらず(今後身に着けるつもりです)ですので勉強になりました。
forestGreen

2021/02/14 03:48

m.ts10806様 コメントありがとうございます。 password_hashとpassword_verifyの使い方含め、不明点はPHPマニュアルのdocumentationを確認した後に投稿しておりますが、コードを読む経験が足りないことにより理解不十分(間違って理解している)の点は多いと自覚しております。
hoshi-takanori

2021/02/14 04:11

「同様の結果」というのは、$_POST["user_password"] に書き換えてから登録処理を行なったアカウントでのログインを試したのでしょうか? $_SESSION["user_password"] の状態で登録したアカウントは、おそらくパスワードは空文字列として登録されていると思いますので、試しにパスワードを空文字列にしてログインをお試しください。 > $_SESSION: 複数のページ間で値を引き継ぐことができる > $_POST: 値は1対1の画面間で受け渡すのみで、複数のページ間で値を引き継ぐことはできない $_POST はブラウザの form から post で送られてきた値 (基本的には、ユーザーが入力した値) です。画面間で受け渡すというのは誤解を招く言い方だと思います。 一方、$_SESSION は php 側で明示的に $_SESSION に保存した値が、別の画面に移動しても引き継がれるものです。$_SESSION["user_password"] に値を保存しているところは見当たらないので、たぶん何も保存されてないと思います。(平文のパスワードを $_SESSION に保存してはいけないので、保存してないこと自体は正しいです。) で、$_SESSION["user_password"] に値が保存されてない状態で password_hash すると、たぶん空文字列をハッシュした結果が返ると思います。かつ、ソルトがかかってるので、毎回違う値になるはずで、ハッシュ値を見ただけではパスワードが空だったかどうかは分からないはず。
forestGreen

2021/02/14 05:08

hoshi-takanori様 > 「同様の結果」というのは、$_POST["user_password"] に書き換えてから登録処理を行なったアカウ> ントでのログインを試したのでしょうか? はい、そのようにしてログインを試しましたが、ログインできなかったため、「同様の結果」とコメント致しました。 > $_SESSION["user_password"] の状態で登録したアカウントは、おそらくパスワードは空文字列とし> て登録されていると思いますので、試しにパスワードを空文字列にしてログインをお試しください。 $_POST["user_password"]に変更後、$_SESSION["user_password"] の状態で登録したアカウントにて仰るようにパスワード空文字でログインを試みたところ、ログインすることができました。 > で、$_SESSION["user_password"] に値が保存されてない状態で password_hash すると、たぶん空> 文字列をハッシュした結果が返ると思います。かつ、ソルトがかかってるので、毎回違う値になるはず> で、ハッシュ値を見ただけではパスワードが空だったかどうかは分からないはず。 空文字でもハッシュ化されてしまうのですね。知りませんでした。 hoshi-takanori様よりいただいたご指摘を確認するべく、$_POST["user_password"]に変更後、テストを行いました。新規ユーザーを登録し、一度ログアウトした後にログインを試みたところ、パスワードが空文字ではログイン不可で、新規登録時に入力したパスワードを入力することでログインすることができました。 また$_SESSIONと$_POSTの定義について理解不足であった点、分かりやすくご教示いただきありがとうございました。大変勉強になり、お陰様で質問内容を解決することができました。ありがとうございました。
m.ts10806

2021/02/14 05:19

ひとまず、入力(フォーム)部分のコードも提示してください。
forestGreen

2021/02/14 05:44

m.ts10806様 フォーム部分の情報を追加していたら、返信が遅くなってしまいました。申し訳ありません。 またteratailで質問が解決した後はどう操作すればよいか、ご教示いただきありがとうございます。今後も利用させていただくにあたり、マナーを身に着けるように致します。ありがとうございました。

まだ回答がついていません

会員登録して回答してみよう

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
87.20%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

パスワード

パスワードは主に情報にアクセスする際に扱われます。主に、アクセス可能なユーザーを限定する手段として使われます。

ログイン

ログインは、ユーザーがコンピューターシステムにアクセスするプロセスの事を呼びます。

暗号化

ネットワークを通じてデジタルデータをやり取りする際に、第三者に解読されることのないよう、アルゴリズムを用いてデータを変換すること。