php セキュリティの基本と対策を理解しよう
» STARTOUT詳細はこちら

WEBサービスづくり学習「STARTOUT」はじめました!

自分の「WEBサービス」を作りながら「制作技術」を習得しよう!

STARTOUTとは、WEB制作×サービス作りのオンライン実践学習サービスです。ゼロからWEBサービスを作り、収益化しながら技術を習得してみましょう!

 詳しくはこちら
ここでは「WEB制作×事業づくり実践学習サービス STARTOUT」の学習コンテンツの一部を公開しています。もし1,000以上あるすべてのコンテンツを学び尽くしたい場合、ぜひこちらからご登録ください!

セキュリティの基本と対策を理解しよう

php|2021年06月21日

2021年06月21日
  • このエントリーをはてなブックマークに追加

実は、駆け出しエンジニアの初期段階で学習がすっ飛ばされる傾向に多いのが、セキュリティではないでしょうか。確かに、そこまで大きなWEBサービスで無い限り、攻撃の対象になることは無いかもしれませんし、laravel等のフレームワークを使っていれば、最初からセキュリティが対策されている箇所も多いので、意識しない、という現実もあるかもしれません。

しかし、当然なにかの拍子でサービスが急成長することもあるでしょうし、どんなサービスでも悪さをする人が狙わないとも限りません。そんな時、セキュリティ対策を怠っていたことを深く後悔するなら、最初から実装しておくことをおすすめします。そういうわけで、今回は基本的な攻撃方法と、それらに対する具体的な対処法をご説明します。

ディレクトリ・トラバーサル

基本的にほとんどのWEBサービス「直接URLを指定してアクセスしないファイル」が存在します。例えば、MVCでいうcontorollersやmodelsなどは、プログラムから参照されるだけで、直接URLを叩いてアクセスしたりはしません。なので、もし直接URLからアクセスされ、URLパラメータなどから不正な値を入力された場合、予期しない動作を起こすこともあります。結果、個人情報の流出などにつながってしまいます。

対策1. htaccessでファイルへの直接アクセスを避ける

まず、通常プログラムからしか参照しないPHPファイルに関してはhtaccessファイルを使って、直接アクセスを拒否します。htaccessファイルに下記の記述を行い、アクセスされたくないディレクトリに置くことで、PHPへの直接アクセスを避けられます。

<Files ~ "\.(php)$">
deny from all
</Files>

対策2. ルートの外にアクセスされたくないファイルを移動する

サーバーには、非公開領域が存在します。例えばエックスサーバーの場合public_html内のファイルはブラウザから参照できますが、さらにその上のディレクトリは直接参照ができません。なので、public_htmlと並列の階層に重要なファイルを起き、php経由でのみアクセスさせることで、直接アクセスを禁止します。下記のケースでは、public_htmlと同じ階層にappというフォルダを作り、そこに重要なファイルを格納しています。

対策3 . ファイルの構造をわかりにくくする

まずルーティングを実装して、本体のファイルや構造がわかりにくい形にしましょう。どこにファイルがあるのか分からなければ、攻撃しにくくなります。またajax等からPOSTする際に、POSTするファイル名を指定しますが、これも隠しましょう。別ファイルにURLを変数に格納するファイルを用意しておいて、使う時にURLファイルを読み込み、POSTのURL自体には変数を使います。このようにURL = 実際のディレクトリやファイルの位置、としなければ、他の対策との組み合わせで直接アクセス自体は防げます。

OSコマンド・インジェクション

phpにはexecやsystemのように、ターミナルやコマンドプロンプトで使うコマンドを入力して実行する仕組みが用意されています。こうした仕組みが用意されているのはphpに限ったことではありませんが、ひとまず、この仕組を悪用されると様々なデータを盗み取る、盗み見ることも可能です。なので、execやsystemを使う時は、外部からデータを入力できないようにしておく必要があります。

対策. フォームやパラメータからデータを入力できないようにする

たとえばphpにおいて、フォームから入力したデータを受け取って使う、URLパラメータから受け取ったデータを使う、といった方法はよく使われています。しかし、フォームやパラメータから直接入力できる内容をexecやsystemに渡してしまうと、自由にコマンドを実行されてしまいかねません。なので、フォームやURLパラメータとexecやsystemを切り離し、完全に関わりが無いように作る必要があります。

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

例えば、本体のサイトでログインしている状態で、本物そっくりの詐欺サイトに誘導されたとします。そこで、何らかのフォームに入力し、送信ボタンを押すと、本体のサイトへデータをPOSTされるように仕組まれていたとしましょう。すると、詐欺サイトに予め用意された不正なデータが、本体サイトにPOSTされてしまう、という事態が発生します。こうなると、本人の意図しないデータが本体サーバーに書き込まれたり、予期しない商品が勝手に購入されてしまう、という事態が起こってしまうのです。

対策. どこのフォームから入力されたかトークンを発行して送る

まず、フォームで送信ボタンを押した時に、ランダムな英数字(トークン)を作ります。そして、セッションに保存しましょう。あとは、送信時、POST先のページにトークンをPOSTし、セッションとすり合わせます。もしセッションに保存されたトークンと、POSTされたトークンが一緒であれば処理を実行し、違っていれば処理を停止します。このように作っておけば、たとえ偽サイトからPOSTされても、トークンが無い、もしくは異なるわけですから、処理が実行されません。これで、CSRFを防ぐことができます。

クロスサイトスクリプティング

もっともシンプルに言えば、WEBサイトにJavaScriptなどのプログラムを埋め込まれてしまう攻撃です。例えば掲示板などでJavaScriptがそのまま投稿できる仕組みだったらどうでしょう。掲示板に「そっくりに作られた罠サイトにリダイレクトする」スクリプトなども埋め込むことが可能になります。ページにアクセスした瞬間、埋め込まれたJavaScriptによってそっくりの罠サイトに飛ばされるのですから、ユーザーは気づきません。そのまま罠サイトに個人情報などを入力してしまうかもしれませんよね。それにjavaScriptでhtmlなどの変更もできますから、例えばformの送信先を別サイトに変更して、個人情報を抜かれてしまうことも考えられます。

対策 : サニタイズ / エスケープ

そもそも、サイトにスクリプトが埋め込まれたり実行されてしまうことが、多くの原因なので、データを投稿する際に、JavaScriptやhtmlに関連した記述を消してしまえば良いのです。こうした処理をサニタイズと呼びますが、phpにもサニタイズ用のタグが用意されています。下記のソースコードをご確認ください。

$hogehoge = htmlspecialchars($_POST['hogehoge'], ENT_QUOTES);

POSTでデータを受け取った先で、POSTをhtmlspecialcharsで囲んでください。すると、スクリプトに関連されたタグが無効化され、$hogehogeという変数に格納されます。これで、悪意あるスクリプトが保存されることはありませんし、サービス内で実行されることもありません。

セッション・ハイジャック

なにかWEBサイトにログインすると、セッションIDというランダムな英数字が発行され、ブラウザにCookie形式で保存されます。ログインの仕組みを作る時に、自分でセッションを用意して、ログインしているかどうかを判断する処理を書くかと思いますが、こちらのセッションIDは、自分で作るセッションとは別で、自動で発行されるものです。現在、サーバーに保存されたセッションの状況と紐付けられたIDという解釈がわかりやすいかもしれません。デベロッパーツールなどでCookieを確認すればPHPSESSIDのように記録されているのがわかると思います。

試しにログイン機能のあるサイトを開き、このセッションIDをコピーしてみましょう。さらに、ほかのブラウザで同じサイトを開き、デベロッパーツールを通じてセッションIDをPHPSESSIDの項目に書き込んでみてください。すると、該当のサイトにログインしてもいないのに、ログインされた状態になるはずです。このように、なんらかの形でセッションIDを奪取してログインされてしまう状況をセッションフィクセイションと言い、さらに、開発者側で用意したセッションを含め、悪意の利用をされてしまう被害をセッションハイジャックと呼びます。

対策 : セッションIDをリロード毎に変える

session_regenerate_id(true); という処理が、PHPに用意されています。これを、それぞれのページに入れておくことで、セッションIDがリロード毎に書き換えられるので、たとえセッションIDを奪取されたとしても、長期的に悪用し続けられることを防ぐことができます。

クリックジャッキング

iframeに関連する内容です。例えば、悪意のある人が、ショッピングサイトに出品している、ある商品を購入させたいとします。そんな時は、まずとあるボタンが用意されたページを作ります。「100万円もらえるキャンペーンに応募!」みたいなボタンのあるサイトだと、クリックされやすいかもですね。

一方で、自分がそのショッピングサイトに登録していたとします。悪意ある人が、その商品購入ページのURLをiframeで、先程のボタンが表示された画面に重ねたらどうでしょう。iframe側を透明にすると「キャンペーンに応募!」のページしか見た目では見えなくなります。まさかショッピングサイトがiframeで重なっているとは思いません。さらに、キャンペーンに応募!ボタンと同じ位置に、ぴったり透明iframe上の「購入ボタン」が設置されていたら。キャンペーンに応募したと思っていたのに、購入ボタンが押されている、という形になるわけです。

iframeを使って透明なボタンを重ね、商品を購入させたり、アカウントをフォローさせたり、意図しない変更をしてしまったり、という状況を引き起こすのがクリックジャッキングです。

対策. 自分のサイトをiframeで表示できないようにする

.htaccessを使えば、自分のサイトをiframeで表示させないように設定することができます。iframeで表示そのものを禁止することもできますし、自分のサイト内であれば表示できるようにも出来ます。大抵のケースでは後者を使うと思いますが、ひとまず外部のURLでiframe禁止にする場合、下記をhtaccessに書き込みましょう。

Header set X-FRAME-OPTIONS "SAMEORIGIN"

これで基本的に、同じサイト内でしかiframeで自分のサイトを表示することができなくなりました。自分のサイトも含め、すべてiframe表示禁止にする場合は、下記を記述しましょう。

Header set X-Frame-Options "DENY"

メールヘッダーインジェクション

PHPでメールを送信する時に、メールアドレスやタイトルなどを入力しますよね。それらのデータは通常、メールのヘッダーに適用されます。ではメールのヘッダーがどこかというと、メールを使った経験のある人なら誰もが1度は見たことがあるかと思いますが、本文エリアの上の部分です。タイトルやBCC、差出人、メールアドレスなどが入力されているエリアがありますよね。それがメールヘッダーです。

PHPなどのプログラムを使ってメールフォームを作る場合、これらのヘッダーもまた、PHPのプログラムによって書かれています。さらに、ざっくり言えばFrom、BCCやTOなどの送信プログラムは、改行して記載することで機能するので、例えば複数のメールアドレスに送信したい場合、改行コードを混ぜた上で適切な文字列を記載さえすれば、複数のアドレスにメールを送信できてしまうというわけです。

対策. 改行コードなどをすべて削除する

対策は非常に単純で、改行コードを消せば良いのです。フォームから入力された内容から改行を削除してしまうことで、メールヘッダーインジェクションを防ぐことができます。下記のソースコードを参考にしてください。

$mail=str_replace(PHP_EOL, '', $main_mail);

フォームから送られてきたメールアドレス情報($main_mail)を、一度str_replaceを通し、改行コードを削除した後、$mailへと代入しています。これで、改行コードが削除されるので、メールヘッダーインジェクションを避けることができます。このように、ユーザーが直接入力したデータは必ず一度、サニタイズ等の処理をするようにしましょう。

SQLインジェクション

例えば、WEBサイトに検索機能があったとします。その検索機能の仕組み上、検索したキーワードが、そのままSQL文に代入されるケースだと、どうなるでしょうか。例えば、下記のような文章があり、$idに検索されたワードが直接代入されるとします。

$hogehoge = $pdo->prepare("SELECT * FROM userdata WHERE id = '. $id . ';');

一見、普通ですが、$idに検索ワードが直接代入される、ということが問題なのです。もしここに悪意ある攻撃者が、検索フォームに「0 OR TRUE; DELETE FROM userdata;」差し込んだらどうなるでしょう。SQL文そのものが書き換えられ、すべてのデータが削除されることになります。これがSQLインジェクションです。

対策. プレースホルダを使う

今回PDO形式のでご説明しますが(mysqli形式にもプレースホルダの書き方はあります)、phpでSQLを扱う際に、プレースホルダという書き方があります。プレースホルダを使うと、例えば検索ワード等にSQL文が差し込まれても、実行されなくなるんですね。あくまでSQL文ではなく、文字列や値として扱われるようになるイメージです。下記のような書き方をします。

$prepare = $pdo->prepare('SELECT * FROM userdata WHERE id = :id;');
$prepare->bindValue(':id', $id, PDO::PARAM_INT);

sql文には:idという形で書いておいて、すぐ下のbindValueで:idに対し、変数$idを代入しています。こうして1つプレースホルダの処理を挟むことで、入力されたSQL文を無害化することができます。

まとめ

このように、セキュリティ対策にはたくさんの種類があり、攻撃方法も、データを守る方法も、年々進化し続けています。時代と共に、常に移り変わっていくものですので、セキュリティに関しては、常に最新情報のキャッチアップが必要となってきます。ただ、今回挙げた内容は、PHPでプロダクトを開発する中で、基本であり、最低限押さえておく必要がある内容ですので、ぜひ、実装する中で試していってください。

開発の予算も期間もある中で、ついついセキュリティ対策は後回しになりがちです。ただ、ちょっとした不運で大きな損失を被ってしまう可能性もありますので、なるべく意識出来るところは意識しながら、開発していきましょう。今回の内容を知っていれば、実際に開発する際も、意識を向けながら設計することができるはずです。

WEBサービスづくり学習「STARTOUT」はじめました!

自分の「WEBサービス」を作りながら「制作技術」を習得しよう!

STARTOUTとは、WEB制作×サービス作りのオンライン実践学習サービスです。ゼロからWEBサービスを作り、収益化しながら技術を習得してみましょう!

 詳細はこちら
  • このエントリーをはてなブックマークに追加

ウエヤマ ショウタ

WEB制作 ✕ 事業づくり学習サービス「STARTOUT」|事業づくり実践学習サービス「WAREHOUSE」|教えない学校、IT留学シェアハウス「WORKROOM」|以上3つ運営してます。近々、上記の成果を束ねて、クリエイターと起業家が生まれ、事業が生み出され続ける町を作る人。ベルリンのホルツマルクトはロールモデル。

ウエヤマ ショウタ

WEB制作 ✕ 事業づくり学習サービス「STARTOUT」|事業づくり実践学習サービス「WAREHOUSE」|教えない学校、IT留学シェアハウス「WORKROOM」|以上3つ運営してます。近々、上記の成果を束ねて、クリエイターと起業家が生まれ、事業が生み出され続ける町を作る人。ベルリンのホルツマルクトはロールモデル。

WEBサービスづくり学習「STARTOUT」はじめました!

自分の「WEBサービス」を作りながら「制作技術」を習得しよう!

STARTOUTとは、WEB制作×サービス作りのオンライン実践学習サービスです。ゼロからWEBサービスを作り、収益化しながら技術を習得してみましょう!

 詳しくはこちら