PHPやLaravelを利用し実務をしたいという方。昨今では特に、副業・複業をしたいという方は多いのではないでしょうか。
今やクラウドワークス、ランサーズ、ココナラなどのクラウドソーシングを利用すれば、即実務を行える時代だからです。
PHP開発者は免許が要りません。医師免許を持っていない人が診断できるように、誰でも仕事をするこおが可能です。
まあPHPでは人命はかからないからね!
それはそうですが、セキュリティ的な配慮をせずに実務をこなすととんでもない自体を引き起こしますよ!
手軽に実務を行えるこの時代だからこそ、実務の前にセキュリティのことを学んでおきましょう!
セキュリティを理解せずPHP案件をこなすと起こりうること
セキュリティを理解しなくても実務案件は請けられます。しかしがら、そうしてしまうとどんな事態になるのか知っておきましょう。
データ漏洩
データベースなどに蓄えた情報が漏洩する可能性があります。
個人情報の場合、顧客の名前、住所、クレジットカード情報などが外部に漏洩し、不正利用されるリスクがあります。
企業情報の場合、機密文書や戦略的情報、知的財産が漏洩することで競合他社や悪意のある者に利用される可能性があります。
法的責任
データ保護に関する法律(例: GDPR、CCPA)に違反すると、企業は罰金や訴訟を受ける可能性があります。
評判の損失
セキュリティ事故は顧客の信頼を失い、企業の評判を損なう可能性があります。これは顧客離れや売上減少につながることがあります。
例えばサーバー会社が顧客のデータを飛ばしてしまったなどすれば、信頼は地の底に落ちるようなものです。
サービス中断
攻撃により重要なシステムが機能しなくなり、ビジネスの継続性が損なわれる可能性があります。
ランサムウェアの感染
システムがランサムウェアに感染し、データが人質に取られる可能性があります。身代金を支払うか、バックアップからの復旧が必要になります。
セキュリティ対策のコスト増加
攻撃に対応するための対応が必要になります。それらの対応には、予防措置よりもはるかに多くコストがかかることでしょう。
最初からきちんとしたシステムにしておくのがベストです。
コンプライアンス違反
特定のセキュリティ基準(例: PCI DSS、HIPAA)に違反すると、罰金やライセンス剥奪のリスクがあります。
インシデント対応の困難
インシデント(=好ましくない出来事)発生時に、適切なセキュリティ対策がないと、問題の特定、対応、解決が困難になります。
体験談ですが、ログを全くとっていないWebシステムがありました。不正アクセスされたのか調査もできず、お手上げでした。
セキュリティに必要な知識
セキュリティを知らないと、いろいろ困ったことになるのはお分りいただけたと思います。そしてその原因を作ったエンジニアは、信用を失うことでしょう。
それだけならまだしも、損害賠償請求される可能性もあるのです。お金を払って作ってもらったシステムで問題が発生すれば当然です。
……と、ここまで脅かすように書いてしまいましたが、セキュリティの知識をきちんと持っていれば防げます。
ここでは改めて、セキュリティ関連の必要な知識を軽く取り上げていきます。
SQLインジェクション
攻撃者がウェブアプリケーションを通じてデータベースに不正なSQLコマンドを注入し、情報の不正抽出や操作を試みる攻撃です。
<?php
// このスクリプトはユーザーの入力を直接SQLクエリに組み込んでいるため、SQLインジェクションに脆弱です。
// データベース接続を確立
$conn = new mysqli('localhost', 'username', 'password', 'database');
// ユーザーからの入力を取得
$user_id = $_GET['user_id'];
// 直接クエリに組み込む(脆弱な方法)
$sql = "SELECT * FROM users WHERE id = '$user_id'";
$result = $conn->query($sql);
// 結果を表示
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo "Name: " . $row['name'] . "<br>";
}
} else {
echo "No results";
}
$conn->close();
対策方法:
- プリペアドステートメントとパラメータ化クエリを使用する。
- ORM(Object-Relational Mapping)ツールを使用する。
- ユーザー入力に対する厳格なバリデーションとエスケープ処理を行う。
- データベースアクセスには最小限の権限を持つユーザーを使用する。
- Laravel等のフレームワークを使う(もちろん理解して使う前提で)
XSS(クロスサイトスクリプティング)
攻撃者が悪意のあるスクリプトをウェブページに注入し、他のユーザーのブラウザ上でそのスクリプトが実行される攻撃です。
これによりセッションハイジャック(乗っ取り)やデータの盗難などが行われることがあります。
<?php
// XSSに脆弱なシンプルなPHPスクリプト
// このスクリプトはクエリパラメータから入力を受け取り、それをブラウザに直接出力します。
// "name"クエリパラメータからユーザー入力を取得
$user_input = $_GET['name'];
// HTMLでユーザー入力を直接出力する(XSSに脆弱)
echo "Hello, " . $user_input . "!";
対策方法:
- 入力を適切にサニタイズ(無害化)することで、ユーザー入力に基づく出力を安全にする。
- 文字列出力時には、HTMLエンティティをエスケープする。
- Content Security Policy (CSP) を実装して、信頼できるソースからのスクリプトのみを許可する。
- フレームワークが提供するXSS防止機能を活用する。
CSRF(クロスサイトリクエストフォージェリー)
攻撃者がユーザーのブラウザを利用して、そのユーザーが認証された状態で、意図しない行動(例えば、パスワード変更、メール送信など)を強制的に行わせる攻撃です。
以下の例では、フォームを介してユーザーIDとステータスを更新する操作がありますが、CSRFトークンがないため、攻撃者はユーザーが知らないうちにこのフォームを送信するリクエストを生成することが可能です。
<?php
// このスクリプトはCSRF攻撃に脆弱です。ユーザーがログインしている状態で、悪意のあるリンクをクリックすると、
// ユーザーの知らない間にデータがサーバーに送信されます。
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// POSTリクエストを処理
$user_id = $_POST['user_id'];
$status = $_POST['status'];
// データベース更新処理(ユーザーステータスの更新など)
echo "User ID: $user_id - Status updated to: $status";
}
?>
<form method="post">
<input type="hidden" name="user_id" value="123">
<input type="hidden" name="status" value="active">
<input type="submit" value="Update Status">
</form>
対策方法:
- 重要なアクションを行う際には、トークンベースの検証を行うCSRFトークンを使用する。
- SameSite属性をCookieに設定することで、クロスサイトのリクエストを防ぐ。
- ユーザーが重要な操作を行う際には再認証(例:パスワードの再入力)を求める。
コマンドインジェクション
外部からの入力をそのままコマンドライン実行に使用することで、攻撃者が不正なコマンドを実行させる攻撃です。
<?php
// このスクリプトはユーザー入力を直接システムコマンドに渡しているため、コマンドインジェクションに脆弱です。
if (isset($_GET['filename'])) {
$filename = $_GET['filename'];
// ユーザーからの入力を直接システムコマンドに渡す(脆弱な方法)
$output = shell_exec("cat " . $filename);
echo "<pre>$output</pre>";
}
対策方法:
- 外部からの入力を直接コマンドラインで使用しない。
- エスケープ関数(例:
escapeshellarg()
)を使用して入力をサニタイズする。 - 安全なAPIを使用してコマンドラインの実行を抽象化する。
ファイルアップロードの脆弱性
不適切に構成されたファイルアップロード機能を通じて、攻撃者が悪意のあるファイルをアップロードし、サーバーを乗っ取る攻撃です。
<?php
// このスクリプトはファイルタイプの検証を行わず、アップロードされたファイルを直接保存するため、
// 不正なファイル(スクリプト、マルウェア等)がアップロードされるリスクがあります。
if (isset($_FILES['uploaded_file']) && $_FILES['uploaded_file']['error'] == 0) {
$uploaded_file = $_FILES['uploaded_file']['tmp_name'];
$destination_path = '/var/www/html/uploads/' . $_FILES['uploaded_file']['name'];
// ファイルを指定ディレクトリに移動
move_uploaded_file($uploaded_file, $destination_path);
echo 'File uploaded successfully.';
} else {
echo 'File upload failed.';
}
対策方法:
- アップロードされるファイルのタイプを制限する。
- アップロードされるファイルのサイズを制限する。
- アップロードされたファイルを実行ファイルディレクトリに保存しない。
- サーバー側でファイルのコンテンツを検査する。
セキュリティ設定の誤り
デフォルト設定をそのまま使用したり、セキュリティ設定を誤って行うことで、潜在的な脆弱性を生じさせる問題です。
例えば私は、LaravelでAPP_ENV=local
、APP_DEBUG=true
で本番運用されているWebシステムの改修を請負ったことがあります。
対策方法:
php.ini
,.htaccess
,httpd.conf
などの設定ファイルでセキュリティ設定を適切に行う。- デバッグ情報の表示は開発環境に限定し、本番環境では表示しない。
セッションハイジャック
攻撃者がユーザーのセッションIDを盗み取り、そのユーザーとしてサービスを利用する攻撃です。
対策方法:
- セッションIDはHTTPS経由でのみ送信する。
session.cookie_httponly
をtrue
に設定して、JavaScriptからのアクセスを禁止する。- セッションIDを定期的に再生成する。
セキュリティミス・コンフィギュレーション
不完全、不正確、または不要な設定が原因で発生するセキュリティ上の脆弱性です。
対策方法:
- 定期的な設定レビューとアップデートを実施する。
- 最小限の権限原則を適用する。
- システムをデフォルト設定から適切なセキュリティ設定に変更する。
セキュアな認証システムの構築
不適切な認証メカニズムは、アカウントの乗っ取りや不正アクセスを許す原因になります。
対策方法:
- 強力なパスワードポリシーの実施。
- 生のパスワードは保存しない。
- パスワードはハッシュ化して保存(PHPの
password_hash()
関数などを使用)。 - マルチファクター認証(MFA)を提供する。
- 認証にトライできる回数を制限する
- 管理画面の認証画面にはIPアドレス制限などをかける
セキュアな通信
データのやり取りが盗聴されるリスクを減らすために、セキュアな通信プロトコルを使用します。
対策方法:
- すべての通信にHTTPSを使用する。
- 信頼できるSSL/TLS証明書を使用する。
エラーハンドリングとロギング
不適切なエラーハンドリングは、攻撃者にシステムの内部情報を漏らす可能性があります。
ログは、何が発生したかを把握する上で非常に重要です。しかしながら、ログインログすら取っていないようなWebシステムが実際にはよく見るのも現実です。
対策方法:
- ユーザーには一般的なエラーメッセージのみを表示し、詳細なエラー情報はロギングする。
- ログファイルは安全な場所に保存し、不正アクセスから保護する。
依存関係の管理
使用するライブラリやフレームワークの脆弱性は、アプリケーションのセキュリティリスクとなります。
対策方法:
- 定期的に依存関係を更新し、セキュリティパッチを適用する。
- 信頼できるソースからライブラリを取得する。
セキュアな構成管理
アプリケーションやサーバーのセキュリティ構成を適切に管理します。
対策方法:
- 構成管理ツールを使用して一貫性のある構成を確保する。
- 環境ごと(開発、ステージング、本番)に適切なセキュリティ構成を行う。
インフラストラクチャのセキュリティ
サーバーやネットワーク機器も攻撃対象になりますので、適切に保護する必要があります。サーバーを管理する場合は、非常に重要です。
対策方法:
- ファイアウォール、侵入検知システム(IDS)などのセキュリティ対策を施す。
- システムのパッチを常に最新に保つ。
顧客への提案&コミュニケーション不足からなるセキュリティ
PHPとセキュリティになぜ顧客が出てくるのか……と思われる方もいるかもしれません。
ですが、顧客への提案&コミュニケーションもセキュリティに影響する、というのがフリーランス20年を超えた私の考えです。
これにはすでに挙げた、以下の2項目も関係してきます。
- 依存関係の管理
- インフラストラクチャのセキュリティ
顧客への教育不足
定期的なアップデートが必要なWebアプリケーションを利用することがあります。顧客に運営を任せる場合、定期的にアップデートを行ってこそ堅牢でいられるわけです。
また、堅牢に作られたつもりでも、ログインURLが流出したり、脆弱なパスワードが使われたりすればセキュリティリスクが高まります。
にもかかわらず、納品後の運営方法や・アップデートのことを正しく伝えない場合は、いずれセキュリティホールが生まれるでしょう。
ちゃんと伝えないで顧客のサイトが脆弱になったのなら、それは制作者のせいにされます。
対策方法:
- アップデートが必要な旨を教育する
- パスワードは複雑にするなど最低限の教育を行う(文書で残すなどが良い)
- できるかぎり保守契約を結ぶ
顧客への定期的な連絡・提案
サーバー上のPHPはずっと同じバージョンを使うことができません。数年でPHPのセキュリティサポートは切れることになります。
他には、Laravel等のフレームワークやライブラリ・モジュール等でもサポート期限が決まっていることがほとんどです。
これらのサポート期限が切れた後は、当然セキュリティ上の問題が出る可能性があります。
対策方法:
- 案件着手前に、バージョンの件は説明しておく(できれば契約書などの文書に残しておく)
- 納品時にも説明をする
- 顧客のウェブ上で動作しているPHPなどのバージョンが切れるであろうことに連絡・提案する。
- できるかぎり保守契約を結ぶ
まとめ
PHPはセキュリティ意識が低い人でも開発できる言語です。それでいて、ネットワークで世界中からアクセス(攻撃)されるということから、これまでに多数の問題が発生してきました。
そのためか、PHPerはレベルが低いなどと言われたことがあるのが悔しいところ。
実際に私も誰もが知る大手企業のサイトでSQLインジェクションの発生を見つけてしまったことがあります。すぐ連絡しました。
最後にまとめます。PHPでのセキュリティを向上させるには、
- きちんとしたセキュリティの知識をもって開発すること
- セキュリティを保ち続けること
の2点が重要です。
当初私は1に気を取られていましたが、フリーランス歴が長くなるにつれて2の維持について、顧客とのコミュニケーションの重要さが分かってきました。
実際に作って放置……というWebサイト・Webアプリケーションは本当にたくさんあります。ログさえ取っていないなんてものも目にしてきました。
これを読むあなたが、PHPのセキュリティの重要さを再確認するきっかけになれば幸いです。
コメント