Node.jsでbcryptでハッシュ化した文字列をPHPでチェックする

# 概要

- jsで文字列「hogefuga」をbcryptアルゴリズムを用いてハッシュ化し、PHPでハッシュ化された文字列と文字列「hogefuga」が一致するか確認する方法をまとめる。

# ユースケース

- lambda関数からPHP/Laravelで作られたエンドポイントにリクエストを送る際にAPIキーを実装する。
- APIキーはlambda関数のjsでbcryptでハッシュ化され、axiosでリクエストヘッダに格納されリクエストが投げられる。
- PHP/Laravelでは平文のAPIキーを.envなどで保持する。受け取ったリクエストのリクエストヘッダからハッシュ化されたAPIキーを受け取り、平文のAPIキーと比較し一致していたら「valid!」と、一致しなかったら「invalid!」とechoする。

# この記事でやること

- ランタイムNode.js18のlambda関数で文字列「hogefuga」をbcryptjsモジュールでハッシュ化する。
- ハッシュ化した文字列をconsole.logで出力する。
- 出力されたハッシュ化された文字列を手動でコピーする。
- PHPがブラウザで動作する下記サービスを使って、確認用コードを記載し、ハッシュ化した文字列と平文の「hogefuga」が一致することを確認する。

https://paiza.io/ja/projects/new

# 方法

1. ランタイムNode.js18のlambda関数を作成する。
1. bcryptjsモジュールをlambdaレイヤーに登録し、作成した関数に紐付ける。
    1. 任意のモジュールをlambdaレイヤーに登録する方法はこちら → [lambda レイヤーを作成しよう（Node.js18）](https://qiita.com/miriwo/items/e1b6cdb8fd765e46f6c4)
1. lambda関数に下記の内容を記載し、保存 → Deployする（ちなみに`hashSync()`メソッドの第二引数はソルトと言って指定した階数分bcryptでのハッシュを繰り返す。回数を多くすれば特定しにくい値になるが、トレードオフで処理時間がかかる。）

    ```index.mjs
    import bcrypt from "bcryptjs";
    
    let hashed = bcrypt.hashSync('hogefuga', 8);
    console.log(hashed);
    
    export const handler = async(event, context) => {
        const response = {
            statusCode: 200,
            body: JSON.stringify('Hello from Lambda!'),
        };
        return response;
    };
    ```

1. lambdaのHello-Worldテストを定義する。
1. lambda関数をHello-Worldテストを実行して動かす。
1. 「Execution results」のタブに「hogefuga」をbcryptアルゴリズムでハッシュ化した文字列が出力されていることを確認する。

    ![bcrypt_-_Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/4a708cb1-2de4-b8c7-ab56-e39baa664587.png)

1. 下記を開く
    1. [paiza.IO](https://paiza.io/ja/projects/new)
1. PHPを開き、下記のコードを記載する。

    ```php
    <?php
    
    $hash = 'jsでhogefugaをハッシュ化した文字列';
    
    if (password_verify('hogefuga', $hash)) {
        echo 'valid!';
    } else {
        echo 'invalid!';
    }
    
    ?>
    ```

1. 「jsでhogefugaをハッシュ化した文字列」の部分にlambdaの「Execution results」のタブに「hogefuga」をbcryptアルゴリズムでハッシュ化した文字列が出力されているのでコピーして貼り付ける。

    ![bcrypt_-_Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/b7e8ad4a-fe8b-9a53-0699-39eeb36812ad.png)

1. 筆者の場合貼り付けた後のPHPのコードは下記のようになる。

    ```php
    <?php
    
    $hash = '$2a$08$Co0vJF0mKz4Py4wj..RkwezaCnF8yoQO85qyrnpVSMs/WY0GUhTS.';
    
    if (password_verify('hogefuga', $hash)) {
        echo 'valid!';
    } else {
        echo 'invalid!';
    }
    
    ?>
    ```

1. 上記を実行すると「valid!」と出力されたのでチェックが通ったことになる。

# 応用

1. 「hogefuga」+「日時情報」をjsにてbcryptでハッシュ化し、PHPでもハッシュ化された文字列が「hogefuga」+「日時情報」と一致するか確認する方法をまとめる。
1. lambdaに下記のように記載する。

    ```js
    import bcrypt from "bcryptjs";
    
    const key = 'hogefuga';
    const date = new Date();
    const dateToISOString = date.toISOString();
    console.log(dateToISOString);
    const beforeHash = key + dateToISOString;
    
    let hashed = bcrypt.hashSync(beforeHash, 8);
    console.log(hashed);
    
    export const handler = async(event, context) => {
        const response = {
            statusCode: 200,
            body: JSON.stringify('Hello from Lambda!'),
        };
        return response;
    };
    ```

1. PHPは下記のように記載する。

    ```php
    $hash = 'リクエストヘッダなどで受け取ったハッシュ化された文字列';
    $date = 'リクエストヘッダなどで受け取ったハッシュ化時に連結した日時情報';
    $key = 'hogefuga';

    if (password_verify($key . $date, $hash)) {
        echo 'valid!';
    } else {
        echo 'invalid!';
    }
    ```

1. 筆者の場合、下記のようになった。

    ```php
    $hash = '$2a$08$ASiujjadHSSfvEcivPCe1eNTYfgx4yMGbNRyhyAwp7knO4mmArOvq';
    $date = '2023-05-16T14:13:20.764Z';
    $key = 'hogefuga';

    if (password_verify($key . $date, $hash)) {
        echo 'valid!';
    } else {
        echo 'invalid!';
    }
    ```