Security

Laravel 5 でのセキュリティ対策 (PHP)

投稿日:2016年6月30日 更新日:

1. ユーザーによる入力値の検証

入力パラメータ値のエンコーディングが正しいことをチェックするMiddlewareの例

以下のミドルウェアを作成して、Global Middleware として登録して使用する。

app/Http/Middleware/EncodingValidateParams.php

<?php namespace App\Http\Middleware;

use Closure;

class EncodingValidateParams
{

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        foreach ($request->all() as $val) {
            if (! $this->isValidEncoding($val)) {
                abort(400, 'Bad Request');
            }
        }

        return $next($request);
    }

    /**
     * @param string $val
     * @return bool
     */
    private function isValidEncoding($val)
    {
        if (mb_check_encoding($val, mb_internal_encoding())) {
            return true;
        }

        return false;
    }
}
  • 上記のミドルウェアを Global Middleware として利用するために、app/Http/Kernel.php の $middleware 配列に、’App\Http\Middleware\EncodingValidateParams’, を追加する。

制御文字を禁止するバリデーションルールを追加する例

1. バリデーション用のクラスファイルを作成する。

  • app/Validation/ParameterValidator.php というクラスファイルを作成し、値に制御文字が含まれていないことをチェックするメソッドを実装する。

app/Validation/ParameterValidator.php

<?php namespace App\Validation;

class ParameterValidator
{
    /**
     * @param $attribute
     * @param $value
     * @param $parameters
     * @return bool
     */
    public function validateNoControlCharacters($attribute, $value, $parameters)
    {
        if (mb_ereg('\A[[:^cntrl:]]*\z', $value)) {
            return true;
        }

        return false;
    }
}

2. app/Providers/AppServiceProvider.php の bootメソッド内に以下を追記する。

  • 1で作成したバリデーション処理を有効にするため、 app/Providers/AppServiceProvider.php の bootメソッド内に以下を追記する。ここでは、今回のルールに対して no_ctrl_chars という名前をつけている。
Validator::extend('no_ctrl_chars', 'App\Validation\ParameterValidator@validateNoControlCharacters');

3. 専用のエラーメッセージを追加する。

(1) 英語用

resources/lang/en/validation.php

<?php
return array(
  // :
  "no_ctrl_chars" => ":attribute is invalid.",
  // :
);
(2) 日本語用

resources/lang/jp/validation.php

<?php
return array(
  // :
  "no_ctrl_chars" => ":attributeは不正な値を含んでいます。",
  // :
);

4. バリデーションのルールとして「no_ctrl_chars」という名前で使用する。

  • 以下のように使用する。
$validator = Validator::make(
    ['name' => 'Dayle'],
    ['name' => 'required|no_ctrl_chars|min:5|max:68']
);

メモ

  • 本当は、デフォルトの string ルールでは制御文字を禁止させておき、それとは別に制御文字を許可するstring_with_ctrl_chars といったルールを用意しておく方が安全であると思う。Laravelでそれをするには、Illuminate\Validation\Validatorクラスを継承したクラスを作り、validateStringメソッドをオーバーライドする必要がある。(参考:Validation – Laravel – The PHP Framework For Web Artisans)

2. XSS対策

Bladeを使っている場合

  • 画面に値を出力するところで、{{ ... }} を使用すれば、HTMLエスケープされる。
  • 言語ファイルに記述した文字列中の改行を <br> に変換して出力させたい場合は、Bladeのテンプレートファイル(xxx.blade.php)内で以下のように記述すればよい。
    {!! nl2br(e(trans('...'))) !!}

3. SQLインジェクション対策

  • DB::raw()whereRaw() を使う場合、信用できない値を埋め込むのであれば、その部分はエスケープ処理すること。
    • エスケープするには、DB::connection()->getPdo()->quote("xxx") とすることで、PDO::quoteメソッドを使用することができる。
    • whereRaw() は、第二引数を使えば安全に値をバインドできる。
      $users = User::whereRaw('age > ? and votes = 100', [25])->get();

メモ

  • MySQLの場合のデータベース接続処理を見たところ、new PDO の第一引数(DSN文字列)ではなく、その直後に set names を発行して文字コードをセットしているようである。これは推奨されないという情報もあり、若干の懸念点ではある。
  • new PDOする時の第4引数で設定しているデフォルトのオプションは以下であった。
    PDO::ATTR_CASE => PDO::CASE_NATURAL,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
    PDO::ATTR_STRINGIFY_FETCHES => false,
    PDO::ATTR_EMULATE_PREPARES => false,
    • PDO::ATTR_CASE => PDO::CASE_NATURAL
      • データベースドライバによって返されるカラム名をそのままにする
    • PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
      • エラー発生時に、例外を投げる
    • PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL
      • NULL と空文字列の変換(Oracle だけでなく、全てのドライバで利用可能)を行わない
    • PDO::ATTR_STRINGIFY_FETCHES => false
      • フェッチする際、数値を文字列に変換しない。
    • PDO::ATTR_EMULATE_PREPARES => false
      • プリペアドステートメントのエミュレーションを無効にする。
      • ドライバによってはネイティブのプリペアドステートメントをサポートしていなかったり 完全には対応していなかったりするものがある。この設定を使うと、常に プリペアドステートメントをエミュレートする (TRUE の場合) か、 ネイティブのプリペアドステートメントを使おうとする (FALSE の場合) かを設定できる。現在のクエリを正しく準備できなかった場合は、常にエミュレート方式を使う。
    • 参照元:PHP: PDO::setAttribute – Manual

4. クロスサイト・リクエスト・フォージェリ(CSRF)対策

メモ

  • セッションにCSRF対策トークンを保持して利用しており、その生成にはopenssl_random_pseudo_bytes関数を使用している(この関数は暗号強度が高い)。

5. セッション管理の不備への対策

Session Encryption を有効にする

  • config/session.php 内の ‘encrypt’ を true にする。

メモ

6. 認証

リメンバーミー機能(remember me)(オートログイン)

デフォルト実装のメモ (v5.0.28で確認)

  • ログイン時、rememeber me というチェックボックスにチェックを入れてログインすると、”remember_xxxxxxxx” という名前のクッキーが発行され、このクッキーさえあればログインできるようになる(セッションクッキーがなくても)。
  • このクッキーの値は「ユーザーテーブルのidカラム値 + “|” + ユーザーテーブルのremember_tokenカラム値」となっている。
  • 但し、クッキーの値は Illuminate\Cookie\Middleware\EncryptCookiesクラスとして実装された Global Middlewareによって暗号化されている。この暗号化には アプリケーションキー(app/config/app.php の key)が使用されている。
  • よって、このクッキーの値を偽装して自分でないユーザーとしてログインするには、「アプリケーションキー」「そのユーザーの remember_token カラム値」の2つを知っている必要がある(idカラム値は予想がつく)。
  • このまま使っても問題はなさそう。

7. Eloquent ORM

Mass Assignment

  • ユーザー入力値がセットできるモデル属性値を限定するために、モデルの $fillable フィールドで属性値を指定する。このようなホワイトリスト方式とは反対に、$guarded フィールドでブラックリスト方式の設定もできるが、ホワイトリスト方式の方が安全である。

参考

8. 参考

関連

こちらの記事もご覧ください。

Web Security

PHPのセキュリティ対策

2016.06.30
Web Security

JavaScript とHTML5のセキュリティ対策

2016.06.30
Web Programming

Webプログラミングのためのリンク集

2017.06.20

スポンサードリンク

📂-Security
-

執筆者:labo


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

Web

openssl s_client コマンドでウェブサーバーの SSL/TLS 対応状況を診断する

openssl s_client コマンドを使い、ウェブサーバーのSSL/TLS対応状況を診断します。 目次1. OpenSSL について2. openssl s_client コマンドについて以降の …

no image

HTML Purifier の使い方

目次HTML Purifier についてComposerで使う方法1. composer.json の require に、以下を追記する2. composer update する3. 実際に使うHT …

Webのセキュリティに役立つWebサービス

目次1. 特定のホスト(サーバー)について調査する2. あるウェブページが安全であるかチェックする3. 特定のWebサイトのSSL設定をチェックする4. 短縮URLの元のURLを取得する5. その他 …

Web Security

CSP Level 3 で Strict CSP を利用する

CSP Level 3 で Strict CSP を利用する方法について説明します。

Web Security

PHPの各PDOドライバは、静的プレースホルダ/動的プレースホルダのどちらを使用しているのか?

目次今のところの結論MySQL (PDO_MYSQL)の場合その他のデータベース用ドライバ参考 今のところの結論 データベースにも依るだろうが、そもそもプリペアドステートメントが使えないSQL文法があ …