Laravel4、セキュリティーを高める準備

タグ: Laravel4  

Laravelの人気爆発に備え、Laravel本体でセキュリティーを上げる手法をまとめておいたほうが良いかと思いました。いくつかポイントを上げておきます。

念のために確認しておきますが、どのCMSやフレームワークを使用しようと、それが動作するPHPの設定、更にPHPが動作するサーバーの設定が緩ければ、意味がありません。

アプリケーションキーの生成

アプリケーションキーは認証などで施用するHashクラスで、ハッシュ値を生成するために使用されます。そのため、認証やHashクラスを使用する場合は、必ず設定しておく必要があります。

32文字の英数字ですが、自分で設定するのは面倒なのでランダムに生成するArtisanコマンドが用意されています。

artisan key:generate

Laravel本体をインストールするときに、composer create-project ...を使用した場合は、自動的に実行され、キーが生成されます。その他の手段でインストールした場合は、自動的に実行されません。先ず、最初にこのコマンドを走らせる癖を付けましょう。

ただし、認証のためのユーザーを登録後、このキーを変更してしまうとそれまで作成した(もしくは登録してくれた)ユーザーの認証が通らなくなります。もちろん、ベースとなるキーを変更してしまったからです。気をつけましょう。(実働環境にあるのであれば、キー値は安全な場所へコピーを取っておきましょう。)

多くの場合、開発環境と、実働環境ではデータベースも別環境でしょう。ですから、このアプリケーションキーも別々なものを使用し他方が良いでしょう。設定ファイルを使い分ける方法も使用できますが、多少セキュアーにしておいたほうが良いでしょう。次のセクションの手法が役に立ちます。

キーや接続情報の秘匿

通常、Laravelではアプリケーションキーや、その他の設定キー、APIキー、DB接続情報はapp/config下の設定ファイルに記述します。

こうした秘密にすべき情報をソース管理の行われている設定ファイルに書くのは、余り安全な手段ではありません。

また、実働環境でのこうした情報は、システムセキュリティー的に開発者が知るべき情報ではありません。その実働環境の管理者が保守すべき情報です。(もちろん、開発者=管理者=あなたであったり、開発が管理も兼ねている規模の会社もあるでしょう。その場合は、当てはまりません。)

Laravelに比較的新しく追加された、機密な設定の保護を参考にしてください。正に、こうした問題に対処するための仕組みです。

このアイデアは、比較的気密性の高い情報を$_ENVを通し、環境変数から取得するというものです。そのために、実働環境ではプロジェクトルートの.env.phpファイル、開発環境ではプロジェクトルートの.env.local.phpファイルに指定しておき、そのキーで$_ENVから値を取得します。(ドキュメントと同じことは書きませんので、詳細はドキュメントをご覧ください。)

たいていのサーバーはlinux/unix上で動作し、ドットファイルにアクセスしづらくするようなオプションがアチラコチラのツールに用意されています。セキュリティーを重視するなら、こうした仕組みを上手く使用しましょう。そしてドキュメントに記述されているように、上記のドットファイルはソース管理の対象外とします。

ただし、こうした仕組みを活用しようと、サーバーに権限の強いユーザーとして侵入されてしまえば、意味がありません。基本的なサーバーのセキュリティーも強化しておきましょう。

ドキュメントでは、取得に$_ENV[]を使用していますが、getenv()のほうが良いでしょう。当方の環境では、PHP組み込みサーバーをArtisanのserveコマンドで起動した場合、$_ENV[]では取得できませんでした。getenv()なら、どの環境でも動作しました。

動作環境の決定

通常、開発環境ではデバックや開発に役立つような情報をバンバン表示できるようにして作業が捗るようにします。しかし、こうした情報はもちろんセキュリティーリスクになるため、実働環境では表示しません。そのためLaravelでは、動作環境を様々な条件から、決定できるようにしてあります。

当サイトでも同様なのですが、古い情報だと実働環境の決定に、ドメイン名を使用するコードを紹介しています。しかし、URLは偽装される可能性が指摘されており、セキュリティーリスクになり得るため、現在は推奨されていません。ドキュメントも変更されています。

環境を決めているのは、bootstrap/start.phpの以下の部分です。

$env = $app->detectEnvironment(array(
    'local' => array('your-machine-name'),
));

ご覧の通り、マシン名を指定するように推奨されています。ただ、もしURLが偽装可能であれば、URLをマシン名に合わせることも可能だということになります。そのため、マシン名が推測できるものであれば、それもまたセキュリティリスクということになります。

では、マシン名が推測可能であるかですが、例えば私の使用しているopenSUSEのディスクトップはインストール時にマシン名にランダムな文字列を含めて生成しており、推測しづらくなっています。ところが、これが共有サーバーの場合、サーバー名として公開されている名前が、マシン名になっていることがほとんどのため、簡単に推測できます。また、VPSを使用する場合でも自分で変更しない限り、決め打ちの名前であったり、ある規則に沿って名付けられたりと、比較的推測しやすい条件が整っています。

そこで、環境変数を利用するのが一番簡単で、安全ということになります。

$env = $app->detectEnvironment(function(){
    return isset($_SERVER['APP_ENV']) ? $_SERVER['APP_ENV'] : 'development';
});

環境変数はPHP組み込みサーバーを使用する場合なら、シェルの環境変数として設定しておきます。シェルにbashを使用している場合なら、自分のホームディレクトリーの.bash_profileに、次の1行を追加します。

export APP_ENV=local

ローカルマシンでWebサーバーを動作させている場合、一番簡単なのは設定ファイルに指定してしまうことです。例えばApache2であれば、httpd.confに次の1行を加えます。

SetEnv APP_ENV local

httpd.confに設定できない場合は、各仮想ホストの設定で指定することもできます。もちろん、publicディレクトリーの.htaccessに書くこともできますが、実動マシンにデプロイする前にこの1行を消す必要が起きます。手違いでそのままデプロイした場合、local環境となり、余計な情報をユーザーに晒してしまう可能性があります。そのため、おすすめではありません。

なお、Apache2でSetEnvディレクティブを使用するには、mod_envモジュールが必要です。

さて、Laravel上の設定はセキュリティ的には、本当にわずかの効果しかありません。前述の通りPHPやサーバー自体の対策のほうがメインです。そうしたメインが破られるような状況であれば、Laravelで取れるセキュリティー対策は意味をなしません。

サーバーとPHP、その他の対策がきちんとなされていれば、Laravelの設定によりフレームワークとしての穴を塞ぐことができます。それでも、開発者としてフォームでcsrfフィルターを使わないなど、穴を開けないようにしてください。

ただし、これもよく知られていますが、技術的に破られ、ユーザ名やパスワードが盗まれるより、人間が行うミスにより、情報が漏れるほうが多いのです。少なくとも、パスワードを書き残したり、平文で保存するのは止めましょう。そして、ホスティングにはしっかりした会社を見つけるのが大切です。

最後に、昨日実際にあった、本当の話をどうぞ。


共有サーバーの管理画面に突然ログインできなくなりました。その会社は、運営を中止し、サーバーは他社へ移管しました。後は移行先が面倒を見るということでした。それでも、ユーザーサポートはその会社のホームページで行われていました。しかし昨日確認したら、ホームページ自体にアクセスできなくなっていました。

そこで、移行先のユーザーサポートにメールで問い合わせました。その会社と直接契約していないため、通常のサポート画面は利用できません。IDがありませんから、ログインできません。一般的な問い合わせとしてフォームからメールを送りました。すぐに返事をもらい、何度かやりとりし、それでも上手くいきません。そこで、「パスワードを一度リセットしたいんだけど、元の会社の変更画面もなくなっているんだけど。」と連絡しました。多分、メールのやりとりだけでは、私の身元はわからないはずですので、どんな方法で身元確認を行い、リセットを受け付けてくれるのか、楽しみにしていました。これにより、この会社のセキュリティー意識が分かります。

なんと返事は、「やっておいたよ、新しいパスワードは…」アウトです。もしか私が悪意を持った別人であれば、私の管理パネルは乗っ取られていたでしょう。怖いですね。

ちなみにそれでも、上手くログインできませんでした。最終的に成功したのは、相手から指定されたドメイン名/cpanelではなく、cpanel.ドメイン名にアクセスすることを思い付き、試したら入れました。設定のどこかの部分の手違いで、サブドメインにcpanelを付けた場合のみ、ログインできるようになってしまっていたようです。(もしかしたら、新手のセキュリティー強化策かも知れません。;P )