1. yk2220s

    Posted

    yk2220s
Changes in title
+[Laravel5.5] APP_KEY の行方を追う
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,117 @@
+# はじめに
+[Laravel Advent Calendar 2017](https://qiita.com/advent-calendar/2017/laravel) 22日目の記事です。
+
+Laravelerにおなじみ?、最初にやらされる `php artisan key:generate` 。それで `.env` に `APP_KEY=base64:xxxxxxx` が埋まりますよね。自分はLaravel5くらいから使っているんですが、あれって具体的に何に使われているんだろうと思って、今回調べてみました!
+
+[注] composer でインストールすれば自動的に実行されてます。`php artisan key:generate`。
+
+# まずはドキュメント
+
+何事もまずはドキュメントを確認してみましょう。
+Laravelerにはおなじみ [Readouble](https://readouble.com/laravel/) には `key:generate` に関して主に "インストール"と、"暗号化"項目にて触れられています。
+#### インストール
+> 次にインストール後に行うべきなのは、アプリケーションキーにランダムな文字列を設定することです。ComposerかLaravelインストーラを使ってインストールしていれば、php artisan key:generateコマンドにより、既に設定されています。
+通常、この文字列は32文字にすべきです。キーは.env環境ファイルに設定されます。もし、.env.exampleファイルをまだ.envにリネームしていなければ、今すぐ行ってください。**アプリケーションキーが設定されていなければ、ユーザーセッションや他の暗号化済みデーターは安全ではありません!**
+
+#### 暗号化
+> Laravelのエンクリプタを使用する準備として、config/app.php設定ファイルのkeyオプションをセットしてください。php artisan key:generateコマンドを使用し、このキーを生成すべきです。このArtisanコマンドはPHPの安全なランダムバイトジェネレータを使用し、キーを作成します。この値が確実に指定されていないと、Laravelにより暗号化された値は、すべて安全ではありません。
+
+ふむふむ、どうやら暗号化の際に使われているようですね。LaravelのSessionやAuth機能などで使われているのでしょうか。
+
+# APP_KEY の生成
+
+次は `php artisan key:generate` を中身を見て見ましょう。
+
+`APP_KEY` でgrepすればわかりますが、 `key:generate` は `Illuminate\Foundation\Console\KeyGenerateCommand` というartisan command用のクラスが実行されます。 (ディレクトリでいうと `verdor/laravel/framework/src/illuminate/Foundation/Console/` 内にあります。)
+
+このクラスにて、 `APP_KEY` の値が生成され、 `.env` ファイルに追加されているようです。基本的にファイルへの書き込み用のロジックがかかれていますが、生成部分は下記の通り。
+
+```php:KeyGenerateCommand.php
+protected function generateRandomKey()
+{
+ return 'base64:'.base64_encode(
+ Encrypter::generateKey($this->laravel['config']['app.cipher'])
+ );
+}
+```
+`Encrypter` にてKeyをを生成、[base64](https://ja.wikipedia.org/wiki/Base64)方式でエンコードしています。configの`app.cipher`にはデフォルトでは`AES-256-CBC`(暗号化アルゴリズムの種類) が設定されています。 `Encrypter`では
+
+```php:Encrypter.php
+public static function generateKey($cipher)
+{
+ return random_bytes($cipher == 'AES-128-CBC' ? 16 : 32);
+}
+```
+という風に生成されています。 `random_bytes` はphp7から追加された、ランダムバイトを生成する関数です。([技術自体](https://github.com/paragonie/random_compat)はphp5.xからでも使えたみたいです。)
+つまり実際は `base64_encode(random_bytes(32))` が実行されています。
+実際に実行してみたら `nnxsf4WwqhHKhwgx7sLjwhEl0DqsBUMpffCmTlvh+CE=` このように無事出力されました。
+
+# APP_KEY の使用され方
+
+そして、実際に生成されたAPP_KEYがどのように使われているかを確認してみます。
+基本 `config/app.php` 内で `'key' => env('APP_KEY')` と格納されているので、 `config('app.key')` もしくは `['config']['app.key']` などと検索してみて使われてない場所がないかなーと探してみます。
+
+そして引っかかるのが、 `Illuminate\Auth\Passwords\PasswordBrokerManager.php` 。このクラスは`PasswordBroker` を生成するFactoryクラスのようです。
+
+(ちなみに上記の`KeyGenerateCommand.php`以外にはここしか引っかかりませんでした。)
+(また 'auth.password' => PasswordBrokerManager, 'auth.password.broker' => PasswordBroker, という感じでコンテナに登録されています。)
+
+```php:PasswordBrokerManager.php
+protected function resolve($name)
+{
+ $config = $this->getConfig($name);
+
+ if (is_null($config)) {
+ throw new InvalidArgumentException("Password resetter [{$name}] is not defined.");
+ }
+
+ // The password broker uses a token repository to validate tokens and send user
+ // password e-mails, as well as validating that password reset process as an
+ // aggregate service of sorts providing a convenient interface for resets.
+ return new PasswordBroker(
+ $this->createTokenRepository($config),
+ $this->app['auth']->createUserProvider($config['provider'] ?? null)
+ );
+}
+
+// doc略
+protected function createTokenRepository(array $config)
+{
+ $key = $this->app['config']['app.key'];
+
+ if (Str::startsWith($key, 'base64:')) {
+ $key = base64_decode(substr($key, 7));
+ }
+
+ $connection = $config['connection'] ?? null;
+
+ return new DatabaseTokenRepository(
+ $this->app['db']->connection($connection),
+ $this->app['hash'],
+ $config['table'],
+ $key,
+ $config['expire']
+ );
+}
+
+```
+`APP_KEY`は デコードされ `Illuminate\Auth\Passwords\DatabaseTokenRepository` のコンストラクタ第四引数として設定されていますね。
+
+`DatabaseTokenRepository` で `APP_KEY` は `$hashKey`というプロパティで登録されます。そして実際にはこのように使用されます。
+
+```php:DatabaseTokenRepository.php
+public function createNewToken()
+{
+ return hash_hmac('sha256', Str::random(40), $this->hashKey);
+}
+```
+[`hash_hmac`](http://php.net/manual/ja/function.hash-hmac.php)は `HMAC方式を使用してハッシュ値を生成する組み込み関数` です。 第三引数は秘密鍵にあたるので、 token 生成の際の秘密鍵として利用されていたのですね。
+
+このtokenは `PasswordBrokerManager`( `Password` ) にてパスワードをリセットする時に発行される Tokenなどとして利用されるようです。
+
+# 終わりに
+
+最後のほうはだいぶ駆け足になってしまいましたが、 `APP_KEY` がどのように生成され、利用されているかがだいぶ見えてきました。 .. がReadoubleには
+>**アプリケーションキーが設定されていなければ、ユーザーセッションや他の暗号化済みデーターは安全ではありません!**
+
+とあり、パスワードリセット以外にも利用されているはずなので、ほかにも調査次第追記したいと思います! (もしご存知の方いたらご教授いただければ幸いです!)