PHP ハッシュ関数

# はじめに
・PHPで、セキュリティに関心がある方
・ハッシュ、ハッシュ関数が分からない方

## ハッシュとは
ハッシュとはアルゴリズムに基づき、**`読解不可能な文字列などに置換する仕組み`**のことです。

※パスワードなどの機密情報を扱う時になどに使用します。

## 代表的なハッシュ関数

PHPには標準で、**`文字列からハッシュ値を計算し、生成する関数`**が存在します。

※以下が代表的な、ハッシュ関数です。

|ハッシュ関数|詳細|
|:---:|:|
|hash|指定したアルゴリズムで、ハッシュ値を生成する **※バージョン 5.1.2以降**|
|md5|MD5方式で、16進数・32文字のハッシュ値を生成する|
|sha1|SHA-1方式で、16進数・40文字のハッシュ値を生成する|
|password_hash|機密性の高い文字列のハッシュ値を生成する **※バージョン 5.5以降**|

### hash

```php:sample_hash.php
<?php 
$str = 'hoge';
var_dump(hash("sha256", $str));
 ?>
```

```:実行結果
string(64) "ecb666d778725ec97307044d642bf4d160aabb76f56c0069c71ea25b1e926825"       
```

### md5

```php:sample_md5.php
<?php
$str = 'hoge';
var_dump(md5($str));
 ?>
```

```:実行結果
string(32) "a577f0035386e7d04551e867b3d9f024"               
```

### sha1


```php:sample_sha1.php
<?php
$str = 'hoge';
var_dump(sha1($str));
 ?>
```

```:実行結果
string(40) "31f30ddbcb1bf8446576f0e64aa4c88a9f055e3c"          
```

### password_hash

```php:sample_password_hash.php
<?php 
// ハッシュ値を計算する前の文字列
$str = "hoge";

// ハッシュ値を計算
password_hash($str, PASSWORD_DEFAULT);
 ?>
```

```:実行結果
string(60) "$2y$10$zgsW6rm.9wcBnkSw2/Jp3OiJSDy5gsEUAM6fum4zva7/5jauNVixK"        
```

## おまけプログラム
IDとパスワードを入力し、DBに保存するプログラムを作成しました。なお、パスワードは**`password_hash`**を使用し、ハッシュ化しています。

**※今回使用するDB情報**

|ユーザー名|DB名|カラム名情報①|カラム名情報②|
|:---:|:|:---:|:---:|:---:|
|id_pass_user|hashdb|id_data varchar(100)|pass_data varchar(100)|

新規で作成した、ユーザーにはパーミッション（権限）がないので、以下のコマンドで付与します。今回の場合は、**`id_pass_user`**にDBレベル(hashdb)での付与になります。

```shell:パーミッションの付与（DBレベル）
mysql> grant all on hashdb.*to id_pass_user@localhost;
```

**※入力する、IDとパスワード**

|ID|パスワード|
|:---:|:|:---:|
|hogeID|hogePASS|

#### コード一覧

```php:data_vaildataion.php
<?php
// ID,パスワードと、DBのバリデーションの補助を実装したクラス
class DataVaildataion {
    
    //ID,パスワードと、DBのバリデーションに必要なデータをカプセル化
	private $hash_pass = null;
	private $success_message = null;
	private $error_message = null;
	private $clean = array();


	# ゲッターは今回必要ないので、コメントアウト

	/*public function getHashPass() {
		return $this->hash_pass;
	}

	public function getSuccessMessage() {
		return $this->success_message;
	}

	public function getErrorMessage() {
		return $this->error_message;
	}

	public function getClean() {
		return $this->clean;
	}*/

	// セッターで、ID,パスワードのバリデーションの補助	
	public function setHashPass($hash_pass) {
		return $this->hash_pass = $hash_pass;
	}

	public function setSuccessMessage($success_message) {
		return $this->success_message = $success_message;
	}

	public function setErrorMessage($error_message) {
		return $this->error_message = $error_message;
	}

	public function setClean($clean) {
		return $this->clean = $clean;

	}
}
 ?>
```

```php:db.php
<?php
// 必要なプロパティのあるクラスファイルの呼び出しと、インスタスの生成
require_once('data_vaildataion.php');
require_once('from.php');
$data = new DataVaildataion(); 

// データベースの接続情報を定数で実装
define( 'DB_HOST', '127.0.0.1'); # phpadminの場合は、localhost
define( 'DB_USER', 'id_data_user');
define( 'DB_PASS', '各自のパスワード');
define( 'DB_NAME', 'hashdb');


// Dataクラスのセッターを使用し、ID,パスワードのバリデーションを実装
if(!empty($_POST['btn_submit'])) {

    if(empty($_POST['id_data']) || empty($_POST['pass_data'])) {
        echo $data->setErrorMessage('IDまたは、パスワードが未入力です。').PHP_EOL;
        exit();
    }else {

        // Dataクラスのセッターを使用し、IDのサニタイズの実装 (シングルクフォートと、ダブルクフォートをHTMLに変換しない）
        $data->setClean($clean['id_data'] = htmlspecialchars( $_POST['id_data'], ENT_QUOTES));

        // 上記と同じく、パスワードのサニタイズの実装
        $data->setClean($clean['pass_data'] = htmlspecialchars($_POST['pass_data'], ENT_QUOTES));

        // セキリュティを考慮し、password_hashを採用（同じハッシュを生成しない。DBとの比較の際は、password_verifyを採用）
        $data->setHashPass($hash_pass['hash_pass'] = password_hash($clean['pass_data'], PASSWORD_DEFAULT));
    }

    // Dataクラスのセッターを使用し、DBのバリデーションを実装後、DBに保存
    if(empty($data->setErrorMessage($error_message)) ) {

        $mysqli = new mysqli( DB_HOST, DB_USER, DB_PASS, DB_NAME);
        if($mysqli->connect_errno ) {
            echo $data->setErrorMessage('エラー番号 '.$mysqli->connect_errno).PHP_EOL;
            exit();
        }else{
            $sql = "INSERT INTO hash (id_data, pass_data) VALUES ( '$data->setClean($clean[id_data])', '$data->setClean($hash_pass[hash_pass])')";
        }

        $res = $mysqli->query($sql);
        if($res) {
            echo $data->setSuccessMessage('登録が完了しました。');
        }else{
            echo $data->setErrorMessage('登録に失敗しました。');
        }
        $mysqli->close();
    }
}
?>
```

```php:from.php
<?php
require_once('data_vaildataion.php');
?>

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>ハッシュ化しよう</title>
</head>
<body>
    <h1>新規登録画面</h1>
    <div class="input_area">
        <form action="db.php" method="post">
            <dl class="id">
                <dt>ID</dt>
                <dd><input type="text" name="id_data"></dd>
            </dl>
            <dl class="pass">
                <dt>PASS</dt>
                <dd><input type="text" name="pass_data"></dd>
            </dl>
             <input type="submit" name="btn_submit" value="新規登録">
        </form>
    </div>
</body>
</html>
```

```shell:hashdb
+----+---------+----------------------------------------------------------------+
| id | id_data | pass_data                                                      |
+----+---------+----------------------------------------------------------------+
|  1 | (hogeID)  | ($2y$10$0V2iu9qPDLJ/LOo629jT2OSNWlEHqUzADek23k.Ns.swWZiT3GRxu) |
+----+---------+----------------------------------------------------------------+
```
※上記のコードで、IDとハッシュ化されたパスワードがDBに保存できます。

**参考サイト**
・[文字列からハッシュ値を取得する](https://gray-code.com/php/make-the-hash-from-string/)