Laravel5.1 で CVE-2017-14775 対応メモ


対応しきれてるか自身ないけど、多分コレで大丈夫なはず。自己責任でおなしゃす。間違いあったら教えてくださいまし。

# 参考

- [Laravel の脆弱性 CVE-2017-14775 の対処法](http://tech.innovator.jp.net/entry/2017/10/13/115613)
- [[5.5] [Security] Close `remember_me` Timing Attack Vector ](https://github.com/laravel/framework/pull/21320)

# 作業見立て

`\Illuminate\Auth\EloquentUserProvider` と `\Illuminate\Auth\DatabaseUserProvider` にある `remember_token` のチェックを `hash_equals`関数をかましてやる。ただその二つは Laravel5.1 では `\Illuminate\Auth\AuthManager`にベタ書きで使われているので、設定で変更して対処することはできない。

`\Illuminate\Auth\AuthManager`は`\Illuminate\Auth\AuthServiceProvider`でしか使われていないので、`\Illuminate\Auth\AuthServiceProvider`の部分から書き換えていく必要がある。ただこの部分、全文検索とかで調べた程度なのでほんとうに大丈夫かちょっと不安。
とりあえずで変更していく。 


# 変更箇所

1. config/app.php
1. \Illuminate\Auth\AuthServiceProvider
2. \Illuminate\Auth\AuthManager
3. \Illuminate\Auth\DatabaseUserProvider
4. \Illuminate\Auth\EloquentUserProvider

それぞれ継承して、プロジェクトディレクトリに置いて対応。 `app.php` は一行だけ置き換えが必要。

## EloquentUserProvider


```app/Providers/EloquentUserProvider.php
<?php
namespace App\Providers;

class EloquentUserProvider extends \Illuminate\Auth\EloquentUserProvider
{
    /**
     * Retrieve a user by their unique identifier and "remember me" token.
     *
     * @param  mixed $identifier
     * @param  string $token
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token)
    {
        $model = $this->createModel();

        $model = $model->where($model->getAuthIdentifierName(), $identifier)->first();

        if (!$model) {
            return null;
        }

        $rememberToken = $model->getRememberToken();

        return $rememberToken && hash_equals($rememberToken, $token) ? $model : null;
    }
}
```

## DatabaseUserProvider


```app/Providers/DatabaseUserProvider.php
<?php
namespace App\Providers;


class DatabaseUserProvider extends \Illuminate\Auth\DatabaseUserProvider
{
    /**
     * Retrieve a user by their unique identifier and "remember me" token.
     *
     * @param  mixed  $identifier
     * @param  string  $token
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token)
    {
        $user = $this->conn->table($this->table)
            ->where('id', $identifier)
            ->first();

        return $user && hash_equals($user->remember_token, $token) ? $this->getGenericUser($user) : null;
    }

}
```

## AuthManager

`app/Lib/Auth` ディレクトリは標準では存在しないが、自己都合の為に作っている。

```app/Lib/Auth/AuthManager.php
<?php
namespace App\Lib\Auth;


use App\Providers\DatabaseUserProvider;
use App\Providers\EloquentUserProvider;

/**
 * Class AuthManager
 * @package App\Lib\Auth
 */
class AuthManager extends \Illuminate\Auth\AuthManager
{
    /**
     * Create an instance of the database user provider.
     *
     * @return \Illuminate\Auth\DatabaseUserProvider
     */
    protected function createDatabaseProvider()
    {
        $connection = $this->app[ 'db' ]->connection();

        // When using the basic database user provider, we need to inject the table we
        // want to use, since this is not an Eloquent model we will have no way to
        // know without telling the provider, so we'll inject the config value.
        $table = $this->app[ 'config' ][ 'auth.table' ];

        return new DatabaseUserProvider( $connection, $this->app[ 'hash' ], $table );
    }

    /**
     * Create an instance of the Eloquent user provider.
     *
     * @return \Illuminate\Auth\EloquentUserProvider
     */
    protected function createEloquentProvider()
    {
        $model = $this->app[ 'config' ][ 'auth.model' ];

        return new EloquentUserProvider( $this->app[ 'hash' ], $model );
    }
}
```

## AuthServiceProvider

```app/Providers/AuthServiceProvider.php
<?php
namespace App\Providers;


use App\Lib\Auth\AuthManager;

/**
 * Class AuthServiceProvider
 * @package App\Providers
 */
class AuthServiceProvider extends \Illuminate\Auth\AuthServiceProvider
{

    /**
     * Register the authenticator services.
     *
     * @return void
     */
    protected function registerAuthenticator()
    {
        $this->app->singleton( 'auth', function ($app){
            // Once the authentication service has actually been requested by the developer
            // we will set a variable in the application indicating such. This helps us
            // know that we need to set any queued cookies in the after event later.
            $app[ 'auth.loaded' ] = true;

            \Log::debug( 'CVE-2017-14775 check.', [ 'file' => __FILE__, 'line' => __LINE__ ] );
            return new AuthManager( $app );
        } );

        $this->app->singleton( 'auth.driver', function ($app){
            return $app[ 'auth' ]->driver();
        } );
    }

}
```

## config/app.php

全部は出せないので、変更箇所だけ。

要は `providers`にある`Illuminate\Auth\AuthServiceProvider`を`App\Providers\AuthServiceProvider`にすれば良い

```config.app.php
<?php

return [ 
    'providers'       => [
        'App\Providers\AuthServiceProvider',
//        'Illuminate\Auth\AuthServiceProvider',
];
```

# 他のバージョンは

5.2,5.3,5.4 系なら [Laravel の脆弱性 CVE-2017-14775 の対処法](http://tech.innovator.jp.net/entry/2017/10/13/115613) で対応可能なはずです。

5.0,5.1系はこの方法になるかな…