29
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

パスワードのハッシュ化とログイン画面

Last updated at Posted at 2015-11-24

#参照/引用
PHP逆引きレシピ第2版

#password_hash()関数を使用する
http://php.net/manual/ja/function.password-hash.php

#解説

ハッシュ関数としてよく使われるmd5()関数やsha1()関数をそのまま使用することは推奨されない。総当たり攻撃に弱いため。

パスワードのハッシュ化で重要なことはその計算量とソルト。パスワード文字列にソルトと呼ばれるデータを追加し、実質的なパスワード文字列を長くすることで安全性を高めることができる。

#password_hash()関数を使用してハッシュ化済のパスワードを得るサンプル

hash.php

<?php
//PHP5.5より前(PHP5.3.7以降)を使用する場合はpassword_compactライブラリを使う
// require_once 'lib/password.php'; 
require_once 'lib/h.php';

if (isset($_POST['submit'])) {
  $password = $_POST['password'];

# ハッシュ処理の計算コストを指定します
  $options = array('cost' => 10);
# ハッシュ化方式にPASSWORD_DEFAULTを指定し、パスワードをハッシュ化する。
# password_hash()関数は自動的に安全なソルトを生成してくれる。(ハッシュ値を取得するたびにソルトが自動生成されるので、同じパスワードでもハッシュ値が変わる)
  $hash = password_hash($password, PASSWORD_DEFAULT, $options);
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ハッシュ化済みパスワードを取得するスクリプト</title>
</head>
<body>
<div>
<?php
if (isset($hash)) {
  echo '生パスワード: ' . h($password) . '<br>';
  echo 'ハッシュ化済みパスワード: ' . h($hash);
}
?>
  <hr>
  <form action="" method="post">
    <label for="password">ハッシュ化したいパスワード文字列:</label>
    <input type="text" name="password" id="password" value="">
    <input type="submit" name="submit" value="ハッシュ化">
  </form>
</div>
</body>
</html>

##PHP5.3.7以降
password_compactライブラリ(MITライセンス)
https://github.com/ircmaxell/password_compat

#エスケープ

下記は、引数が文字列と配列のどちらも処理できる。
is_array()関数で引数が配列かどうかを判定。配列だった場合は、array_map()関数で全ての要素をh()関数で処理する。

lib/h.php
<?php
function h($var)
{
  if (is_array($var)) {
    return array_map('h', $var);
  } else {
    return htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
  }
}

#ログイン画面サンプル

index.php

<?php
//require_once 'lib/password.php'; php5.5より前の場合使う
require_once 'lib/h.php';

# クリックジャッキング対策
header('X-FRAME-OPTIONS: SAMEORIGIN');

# セッションを開始します。
session_start();

//複数人設定することができる
$userid[]   = '';   // ユーザーID
$username[] = '';  // お名前
// パスワードをハッシュ化した文字列
$hash[] = '';

# エラーメッセージ初期化
$error = '';

# 認証済みかどうかのセッション変数を初期化
if (! isset($_SESSION['auth'])) {
  $_SESSION['auth'] = false;
}

if (isset($_POST['userid']) && isset($_POST['password'])) {
  foreach ($userid as $key => $value) {
    if ($_POST['userid'] === $userid[$key] &&
# 入力されたパスワード文字列とハッシュ化済みパスワードを照合
        password_verify($_POST['password'], $hash[$key])) {
# セッション固定化攻撃対策(セッションIDを変更)
      session_regenerate_id(true);
      $_SESSION['auth'] = true;
      $_SESSION['username'] = $username[$key];
      break;
    }
  }
  if ($_SESSION['auth'] === false) {
    $error = 'ユーザーIDかパスワードに誤りがあります。';
  }
}

if ($_SESSION['auth'] !== true) {
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ログイン</title>
</head>
<body>
<div id="login">
  <h2>ログイン</h2>
  <?php
  if ($error) {
    echo '<p style="color:red;">' . h($error) . '</p>';
  }
  ?>
  <form action="<?php echo h($_SERVER['SCRIPT_NAME']); ?>" method="post">
  <p>ユーザーID:<input type="text" name="userid" id="userid" value=""></p>
  <p>パスワード:<input type="password" name="password" id="password" value=""></p>
  <p><input type="submit" name="submit" value="ログイン"></p>
  </form>
</body>
</html>
<?php
# スクリプトを終了し、認証が必要なページが表示されないように
  exit();
}

#パスワードの照合

解説
//照合の結果、マッチする場合はtrue、それ以外はfalseが返る
$auth = password_verify($password, $hash);
login.php
<?php
//require_once 'lib/password.php'; php5.5より前の場合使う
require_once 'lib/h.php';

# クリックジャッキング対策
header('X-FRAME-OPTIONS: SAMEORIGIN');

# セッションを開始します。
session_start();

//複数人設定することができる
$userid[]   = '';   // ユーザーID
$username[] = '';  // お名前
// パスワードをハッシュ化した文字列
$hash[] = '';

# エラーメッセージ初期化
$error = '';

# 認証済みかどうかのセッション変数を初期化
if (! isset($_SESSION['auth'])) {
  $_SESSION['auth'] = false;
}

if (isset($_POST['userid']) && isset($_POST['password'])) {
  foreach ($userid as $key => $value) {
    if ($_POST['userid'] === $userid[$key] &&
# 入力されたパスワード文字列とハッシュ化済みパスワードを照合
        password_verify($_POST['password'], $hash[$key])) {
# セッション固定化攻撃対策(セッションIDを変更)
      session_regenerate_id(true);
      $_SESSION['auth'] = true;
      $_SESSION['username'] = $username[$key];
      break;
    }
  }
  if ($_SESSION['auth'] === false) {
    $error = 'ユーザーIDかパスワードに誤りがあります。';
  }
}

if ($_SESSION['auth'] !== true) {
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ログイン</title>
</head>
<body>
<div id="login">
  <h2>ログイン</h2>
  <?php
  if ($error) {
    echo '<p style="color:red;">' . h($error) . '</p>';
  }
  ?>
  <form action="<?php echo h($_SERVER['SCRIPT_NAME']); ?>" method="post">
  <p>ユーザーID:<input type="text" name="userid" id="userid" value=""></p>
  <p>パスワード:<input type="password" name="password" id="password" value=""></p>
  <p><input type="submit" name="submit" value="ログイン"></p>
  </form>
</body>
</html>
<?php
# スクリプトを終了し、認証が必要なページが表示されないように
  exit();
}
logout.php
<?php
# セッション開始
session_start();
# セッションを破棄
$_SESSION = array();
session_destroy();
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>ログアウト</title>
</head>
<body>
<div>
  <p>ログアウトしました。</p>
  <p><a href="">戻る</a></p> <!-- URLを入力 -->
</div>
</body>
</html>
29
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?