目次
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
- PDO::ATTR_CASE => PDO::CASE_NATURAL
4. クロスサイト・リクエスト・フォージェリ(CSRF)対策
メモ
- セッションにCSRF対策トークンを保持して利用しており、その生成にはopenssl_random_pseudo_bytes関数を使用している(この関数は暗号強度が高い)。
5. セッション管理の不備への対策
Session Encryption を有効にする
- config/session.php 内の ‘encrypt’ を true にする。
メモ
- Full Disclosure: Laravel – PHP Object Injection – 4.1, 4.2, 5.0, master
- http://seclists.org/fulldisclosure/2015/Apr/57
- この件に関しては動向を見守る。
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 フィールドでブラックリスト方式の設定もできるが、ホワイトリスト方式の方が安全である。
参考
- Eloquent ORM – Laravel – The PHP Framework For Web Artisans
8. 参考
- 体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
関連
こちらの記事もご覧ください。
スポンサードリンク