LaravelでCAPTCHAを導入してみる

皆さんこんにちは

最近はホントセキュリティ関連の事件が多くて辟易しますが、その中でも厄介なのがリスト型攻撃というやつで、どっかの脆弱なサイトから漏れたIDとパスワードの組を使って攻撃するというものです。
パスワードは割と使いまわされるものなので、こいつの突破率はかなり高く、10%を超えるという話も聞いたことがあります。
対策としてはパスワード以外の要素で認証する２要素認証というものがいいのですが、今回は代わりに連続自動攻撃を防ぐためにCAPTCHAを使って防衛する方策を取ってみましょう。

そしたらLaravelで簡単に実現できる方法があったのですよヤッホ〜。

# TL;DR

+ Laravel用のサードパーティ製のパッケージがあるぞ https://github.com/mewebstudio/captcha
+ 導入してみた

<img width="751" alt="スクリーンショット 2016-11-21 22.53.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/102182/531d5066-7621-1bc5-1710-8e2577a62180.png">
こんなんできました。

それでは、以下で詳しい導入方法を解説していきます。

# CAPTCHA
導入の前に、軽くCAPTCHAについて解説しておきます。
CAPTCHAとは、機械では読みにくい画像化したフレーズを表示し、ユーザーに入力させることで、機械ではなく人間が入力しているであろうことを認定するための認証方法です。
パスワードリスト攻撃とはいえ、一旦は機械が手当たり次第にログインしまくった後に成功したアカウントで操作することになると思うので、Capthca認証を導入しておいて機械によるログインを防いでおくことは有用なんじゃないかと思います。
超人海戦術で一斉攻撃されたらだめかもしれませんが、そこまでやるのは人的リソースと時間的に無理じゃないかと思いたい。

そんなわけで、上の画像にあるようなフレーズ画像を自動生成して認証に利用する仕組みを導入してみましょう。

# 準備
## Laravelのプロジェクト作成
今回はLaravel 5.2を使いました。
5.3使い慣れていないのと、今使っているプロジェクトでは使えないので。
[環境は例によってLaradock使ってます](http://qiita.com/niisan-tokyo/items/d4485b8bb4fbfcfcc6be)。
サーバはNginx + PHP-FPM、DBはMySQL使いました。

Laravel5.2を導入するためには以下のように`composer`でバージョンを指定してあげます。

```
$ composer create-project laravel/laravel:5.2.* capauth
```
## 認証機構の作成
composerでプロジェクト作った時点で、ユーザー用のテーブル生成用のマイグレーションファイルとモデルがあるので、以下のコマンドで認証ページとデータベースを整えておきます。

```
$ php artisan make:auth
$ php artisan migrate
```
トップページが変化し、ログインページが出せるようになります。
<img width="753" alt="スクリーンショット 2016-11-21 23.07.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/102182/f99d069c-f12d-a9d5-6a6c-220f3efa71b5.png">
右上にRegisterとかがあるので、それで適当にユーザー作って認証の動きを確認できます。

# CAPTCHAの導入
## パッケージのインストール
次にCpatcha認証を導入しますが、これにはサードパーティ製のパッケージを使用します。
buildがfailしているのが気になりますが、とにかくcomposerでパッケージを導入します。

```
$ composer require mews/captcha
```
インストールしたらLaravelで使えるように設定を行います。Githubで製作者の方が解説していますが、それをトレースしてみましょう。
まず、`config/app.php`に設定を追加していきます。

```config/app.php
'providers' => [
    // 中略
    Illuminate\Translation\TranslationServiceProvider::class,
    Illuminate\Validation\ValidationServiceProvider::class,
    Illuminate\View\ViewServiceProvider::class,
+   Mews\Captcha\CaptchaServiceProvider::class,
]
```
もう一箇所、aliasにも追加します。

```config/app.php
'aliases' => [
    // 中略
    'Validator' => Illuminate\Support\Facades\Validator::class,
    'View' => Illuminate\Support\Facades\View::class,
+   'Captcha' => Mews\Captcha\Facades\Captcha::class,
]
```
追加設定が完了したら以下のコマンドで、設定ファイルを出力しておきます。

```
php artisan vendor:publish
```
これでCAPTCHAを行う準備が整いました。
## 認証画面にcaptchaフィールドを導入する
それでは、認証画面に導入してみましょう。初めに`artisan`コマンドでauth用の画面を生成してあるので、そいつをいじりましょう。

```resources/views/auth/login.blade.php
<div class="form-group{{ $errors->has('captcha') ? ' has-error' : '' }}">
    <label for="captcha" class="col-md-4 control-label">Input captcha</label>
    <div class="col-md-6">
        <input id="captcha" type="text" class="form-control" name="captcha">

        @if ($errors->has('captcha'))
            <span class="help-block">
                <strong>{{ $errors->first('captcha') }}</strong>
            </span>
        @endif
    </div>
</div>
<div class="col-md-6 col-md-offset-4">
    {!! captcha_img() !!}
</div>
```
上記のテンプレートをPasswordとRemenberMeの間に挿入します。
これで、認証画面に初めに見たようなcaptchaフィールドを作ることができます。

## 認証コントローラにCAPTCHA(バリデーション)を導入する
最後にログインボタンを押した際に、captchaフィールドにある文字との齟齬をチェックするバリデーションを追加することで、CAPTCHAを実現します。
変更は以下のとおりです。

```app/Http/Controllers/Auth/AuthController.php
- use AuthenticatesAndRegistersUsers, ThrottlesLogins;
+ use AuthenticatesAndRegistersUsers, ThrottlesLogins {
+     AuthenticatesAndRegistersUsers::login as rawLogin;
+ }

+ public function login(Request $req)
+ {
+     $this->validate($req, ['captcha' => 'required|captcha']);
+     $this->rawLogin($req);
+ }
```
まあ、少々不細工ではありますが、これでCAPTCHAを実現できます。
## 設定ファイル
最後に設定ファイルについて少しだけ開設します。
CAPTCHAの設定は`config\captcha.php`で行えます。
文字の長さ(length)を変更して、イメージの幅(width)を変えないと、文字が詰まったり重なったりするので、やり過ぎ注意です。
また、設定ファイルにある幾つかのまとまりについては、ビューテンプレート上のヘルパー関数`captcha_img`に引数で指定できます。
例えば、`captcha_img('flat')`のように設定できます。

# まとめ

最近セキュリティに対する意識が高まってきており、私のプロジェクトでも様々な対策が必要になってきました。
そこで、CAPTCHAの導入を検討してみたのですが、さすがはLaravel、割と簡単に導入できました。

今回はこんなところで失礼します。

# 参考
[CAPTCHA発明者の話](http://logmi.jp/28125)
[wikiの説明](https://ja.wikipedia.org/wiki/CAPTCHA)