【Security】SQL インジェクション+Laravel実装
概要
Webアプリケーションのユーザーの入力領域に
攻撃者が不正なSQL文を注入し、データベースに意図しない命令を実行させる攻撃手法。
- ログインフォームや検索機能などを持つWebサイトは特に注意する。
発生しうる脅威
重要情報の漏洩
データベースに格納されている重要な情報(パスワードやカード情報など)が盗まれる。
企業の信頼を大きく損なうことにもつながる。サイトの改ざん、削除
偽の情報を表示したり、情報を消去することができる。データベースの全消去・破壊
DELETE、DROP文などを使い、データベースごと削除されるシステムの乗っ取りや、攻撃の踏み台にされる
管理者権限を奪われ、サーバーを他の攻撃に使われる。
--脆弱性のあるコード SELECT * FROM users WHERE id='$id' AND pass='$pass'; ↓ --正常な入力 SELECT * FROM users WHERE id='John' AND pass='123pass'; --攻撃者の入力 SELECT * FROM users WHERE id='' or 1=1; -- 'AND pass='123pass';
☀︎攻撃者が$idに「' or 1=1; --」と入力されると、
- $idは空文字または1は1に等しいという条件になり常に真となる。
- --以降はコメント扱いになるためパスワードの条件は無効になる。
→そのため本来は通らないユーザー認証が成功してしまう。
原因
ユーザーからの入力値をそのままSQL文に埋め込むこと
対策
根本的解決
1.プレースホルダーとプリペアドステートメントで実装する
PDOでのプレースホルダー使用例
//プレースホルダー付きSQL文を準備 $stmt = $PDO->prepare("SELECT * FROM users WHERE id = :id AND pass = :pass"); //値をバインド $stmt->bindValue(':id', $id); $stmt->bindValue(':pass', $pass); //実行 $stmt->execute();
2.エスケープ処理
データベースに値を渡す前に、特殊文字を安全な形に変換する。
「’」→「’’」、「¥」→「¥¥」など
PHPでは $mysqli->real_escape_string($input) を使うことができる。
3.hiddenパラメータで直接指定しない。(禁忌)
ユーザーからの入力を直接SQLに組み込むのは避ける
保険的対策
4.エラーメッセージ表示しない
攻撃者に内部構造(テーブル名など)の情報を与えないようにする。
エラーメッセージは表示せず、ログファイルに記録する。
5.データベースの権限を最小限に設定する
DBユーザーには必要最小限の操作だけ許可する。
SELECT操作で十分であれは、INSERTやDELETE文の権限までは付与しない。
Laravelでの実装例
1.Eloquent ORMを使う
Laravelには、「Eloquent ORM」というPDOのプリペアドステートメントを利用した機能が搭載されている。
モデルクラスとしてデータベースのテーブルと連携し、SQL文を直接書かずにデータベースを操作できる。
さらに詳細なクエリや条件を指定したいときは、「クエリビルダー」を使う。
- 生SQLを使うときは、プリペアドステートメントを利用したり、入力値のバリデーションやエスケープ処理を徹底する。
実装の流れ
①あらかじめモデルでデータベーステーブルと連携しておく
Post.phpファイルに
//Postクラスの作成
class Post extends Model
{
//テーブル名を指定
protected $table = 'posts';
}
②コントローラーでPostクラスを使う
PostController.phpファイルに
class PostController extends Controller
{
public function index() {
//postsテーブルのデータを全件取得
$posts=Post::all();
return view('posts.index', compact('posts'));
}
}
③ビューで$postsを使い表示させる
posts/index.blade.phpファイルに
@foreach($posts as $post) <div> <h2>{{ $post->title }}</h2> <p>{{ $post->body }}</p> </div> @endforeach
参考にしたサイト
【Security】アクセス制御や認可制御の欠落+Laravel実装
概要
アクセス制御や認可制御の欠落とは、
認証や認可が不十分なために、攻撃者やユーザーが本来アクセスできないページに入り、情報を閲覧したり操作できてしまう状態のこと。
ログインできた(認証)後、ユーザーを識別して適切な権限を与える(認可)。
認証・認可を組み合わせて対策をする。
認証と認可の違い
認証(Authentication)とは
- ユーザーが本人であるかを確認する仕組み
(パスワード認証、指紋認証、多要素認証(MFA)など) - 「アクセス制御の欠落」・・・認証(ログイン)しなくてもアクセスできてしまう状態。直接URLを指定すると誰でもログインした後のページに入れるなど。
- ユーザーが本人であるかを確認する仕組み
認可(Authorization)とは
- 認証済みのユーザーに、なにをどこまで許可するかの権限を付与する。
(ファイルの閲覧や端末の制限など) - 「認可制御の欠落」・・・本来許可されていないページにアクセスできてしまう状態。他人のデータを閲覧・編集できてしまうなど。
- 認証済みのユーザーに、なにをどこまで許可するかの権限を付与する。
発生しうる脅威
アクセス制御の欠落による脅威
- 情報漏洩、不正操作、データ改ざん
- 本来権限を持たないユーザーによる不正ログイン
- 認証を経ずに直接URLを指定することで、誰でもログイン後のページにアクセスできる
認可制御の欠落による脅威
- 他人の情報を参照・操作(クエリパラメータ改ざんによるIDOR)
- 低権限ユーザーが本来できない操作を実行できる(権限昇格)
☀︎IDOR(Insecure Direct Object Reference)とは、
ユーザーIDなどをURLに直接指定することで、他人のリソースにアクセスできてしまう脆弱性のこと。
原因
認証・認可の実装や設計の不足
対策
根本的解決
1.ユーザー入力値のバリデーションは必ずサーバー側で行う。
クライアント側だけでなく、サーバー側で厳格にバリデーションして不正なデータや攻撃を防ぐ
2.認証にパスワードなどの入力を必要とする
メールアドレスなど公開されやすい情報だけではなく、秘密情報を使って本人確認をする。
3.利用者IDをURLやPOSTパラメータに埋め込まない
他のユーザーにIDを推測されるため、IDを推測困難な値にする。
サーバー側で「そのユーザーがそのリソースにアクセスする権限があるか」を必ず認可チェックする。
4.メニューの表示/非表示だけで制御しない
クライアント側の画面上でメニューを非表示にしても、URLやパラメータを知っていれば権限がなくても操作できてしまう。
サーバー側での認可チェックが必須。
Laravel実装例
1.Laravel Breezeなどのパッケージを使う(認証)
Laravelが提供しているパッケージ使って、
ユーザー登録やログイン、パスワードリセットなどの機能を設定する。
composer require laravel/breeze --dev
php artisan breeze:install
npm install && npm run dev
php artisan migrate
2.パスワードを暗号化する(認証)
Laravelではデフォルトでパスワードを暗号化し、ハッシュ化して保存する。
//パスワードを暗号化してDBに保存
$password = Hash::make('your-password')
//ログイン時にハッシュファザードで認証
if (Hash::check('your-password', $storedPassword)) {
// パスワードが一致しているときの処理
}
3.ポリシーやゲートを使う(認可)
ポリシー(Policy)で、
そのユーザーがそのリソースにアクセスできる権限を持っているか確認する。
モデルのファイルでポリシーを定義する
public function update(User $user, Post $post) {
// ユーザーが投稿のオーナーなら編集可能とする処理
return $user->id === $post->user_id;
}
コントローラーのファイルでポリシーを使用する
class PostController extends Controller
{
$this->authorize('update', $post);
}
- ゲート(Gate)は、シンプルな認可制御。管理者ページではない個人のページへの権限を確認する。
- ポリシーやゲートで認可したあと、authorizeメソッドで判定(認可確認)をする。
参考にしたサイト
【Security】バッファオーバーフロー+Laravel実装
概要
ユーザーの入力領域に
攻撃者が悪意あるコードを含んだ大量のデータを送信してバッファを溢れさせる。
リターンアドレスが悪意のあるコードで上書きされ、処理されることにより
システムに異常動作を発生させる攻撃手法。
「バッファ」 ・・・プログラムが一時的にデータを保存するためのメモリ領域
「リターンアドレス」・・・関数処理後に実行するべきプログラムの位置情報
発生しうる脅威
プログラムの異常終了
意図しないシステム停止により多額の損害賠償や企業の信頼を大きく損なうことにもつながる。重要情報の漏洩やサイトの改ざん
遠隔操作が可能となる悪質なコードを送られ、管理者権限を奪われる。
攻撃の踏み台にされる
DDoS攻撃などに悪用される
原因
ミスによる不適切なメモリ管理
- 入力値の長さをチェックしてから使っていない
- 入力値のバリデーション不足
対策
根本的解決
1.直接メモリ操作ができない言語で記述する
PHP、Perl、Javaなど、メモリ管理を自動で行ってくれる言語を使う。
2.直接メモリ操作ができる言語で記述する部分を最小限にする
C、C++、アセンブラなどの言語のときは、その部分をできるだけ少なくして集中的にチェックをする。
3.脆弱性が修正されたバージョンを使用する
最新のバージョンのライブラリを使用する。
Laravelでの実装例
1.ほぼ発生しない
LaravelではPHPを使っており、メモリ管理を自動で行っている言語のため。
2.入力値のバリデーション
ユーザー入力には必ずバリデーションをかけておく。
$validated = $request->validate([ 'name' => 'required|string|max:255', ]);
☀︎255文字以下の文字列のみ通過できる。
参考にしたサイト
【Security】クリックジャッキング+Laravel実装
概要
正規サイトにログインした状態で、
攻撃者が罠サイトにユーザーを誘導しiframeや透明なレイヤーを使ってリンクやボタンを重ねて配置し、ユーザーに意図しない操作をさせる攻撃手法。
ユーザーが一見無害に見えるサイトのボタンをクリックすると、
実際には背後にあるiframe内のボタンをクリックさせられている。
発生しうる脅威
金銭的被害
不正送金やクレジットカードの不正利用、ECサイトで意図しない商品購入など登録情報の漏洩・改ざん
利用者情報の公開範囲の変更や退会処理プライバシー侵害
カメラやマイクへのアクセス許可をユーザーに気づかれずにONにさせる
原因
ログイン後の重要な操作や設定を、クリック操作のみで実行可能にしている仕様
iframeで埋め込めるページに、重要な操作の処理ができる機能の実装
対策
根本的解決
1.frameやiframeによるページの読み込みを制限する
HTTPレスポンスヘッダにX-Frame-Options: DENY(またはSAMEORIGIN)と設定すると、
X-Frame-Optionに対応したブラウザでは、frame要素やiframe要素によるページの埋め込みができなくなる。
IE7など対応していないブラウザもあるため、CSPとあわせて対策するといい。
SAMEORIGIN ・・・同一オリジンのときのみ表示を許可する
ALLOW-FROM・・・指定したオリジンのみ表示を許可する
2.処理の直前に再度パスワードを入力させる
重要な操作の直前のページでユーザーに再度パスワードを入力させ、 そのパスワードが正しい場合のみ処理をする。
ただし画面設計の仕様変更が必要となる。
保険的対策
3.重要な処理はクリックのみで実行できないような仕様にする
ユーザーに複雑な操作をさせることは難しいため、
重要な操作にキーボード操作や複数の確認ステップを挟む仕様にすることで、攻撃の成功率を下げることができる。
Lalavelでの実装例
1.自作ミドルウェアでX-Frame-Optionsを追加する
レスポンスヘッダにX-Frame-Optionsオプションを指定する。
AddXFrameOptions.phpファイルに
class AddXFrameOptions //AddXFrameOptionsクラスを作成
{
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'deny'); //すべてのウェブページでフレーム内の表示を禁止
return $response;
}
}
☀︎全リクエスト(全ページ)でフレーム内の表示が禁止になる。
参考にしたサイト
【Security】メールヘッダ・インジェクション+Laravel実装
概要
メール送信機能を持つWebアプリケーションで出力処理に脆弱性があると、
攻撃者がメールのヘッダ情報に不正なデータを差し込むことで、不正な操作を実行する攻撃手法。
- お問い合わせページやアンケートなどのサイトで特に注意が必要。
発生しうる脅威
- 攻撃の踏み台として迷惑メールの送信に悪用される
- メールの内容の改ざん
//脆弱性のあるコード To: info@□□.□□ From: $email Subject: お問い合わせ Content-Type: text/plain: charset="ISO-2022-JP" 本文 //Fromの$emailに差し込む support@△△.△△%0d%0aBcc%3a%20○○○○@○○.○○ ↓ //生成されるメール To:<info@□□.□□> From:<support@△△.△△> Bcc:<○○○○@○○.○○> Subject: お問い合わせ 本文
☀︎BCCを追加されて、○○○○@○○.○○にも送信される。
「%0d%0a」は改行コード、「%3a」はコロン、「%20」は空白
- メール文の終わりを示すヌル「%00」を使って、以降の文を削除もできる。
原因
メールヘッダ出力の実装
対策
根本的解決
1.メールヘッダを固定値にする
メールヘッダにユーザーからの入力された値をそのまま使わない実装をする。
2.メールヘッダーの出力用APIを使う
メールヘッダを固定値にできないときは、
実行環境や言語に用意されているメール送信用のAPIを使う。
(PHPMailer、SendGrid、Mailgunなど)
3.HTMLで宛先を指定しない(禁忌)
宛先のメールアドレスをHTMLのhiddenパラメータなどで指定しない。
保険的対策
4.ユーザー入力から改行コードを削除する
ユーザーが入力した値から改行コード(\r, \n など)や制御文字の削除や含まれていたら処理を中止させる。
全ての入力値から改行コードを削除してしまうとメール本文の改行からも削除され、
エラーの原因となるため用途に応じて適切な処理を行う。
Laravelでの実装例
1.Laravelの標準メール送信機能を使う
Mailファザード(Mail::to)または、Mailableクラス(extends Mailable)を使う。
Mail::to($request->input('email'))->send(new ContactFormMail($data));
☀︎メールヘッダの改行コードが自動的に除去される。
エスケープ処理
// メールアドレスのサニタイズ $email = filter_var($request->input('email'), FILTER_SANITIZE_EMAIL); // 件名の改行コードの除去 $subject = str_replace(["\r", "\n", "%0A", "%0D"], "", $request->input('subject')
☀︎PHP関数のfilter_var()やstr_replace()で改行コードを除去している。
バリデーション処理
To, Cc, Subject, Fromなどにユーザー入力を使うときは必ずバリデーションをする
参考にしたサイト
【Security】HTTPヘッダ・インジェクション+Laravel実装
概要
ユーザーからの入力値の処理に脆弱性があると、
攻撃者がHTTPリクエストに罠を仕掛けてレスポンスを生成し、
ユーザーに偽のURLをクリックさせ、スクリプトを実行させたり情報を盗み取る攻撃。
改行コードが原因となることから「CRLFインジェクション」とも呼ぶ。
複数のレスポンスを作り出す攻撃を
「HTTPレスポンス分割(HTTP Response Splitting) 攻撃」と呼ぶ。
発生しうる脅威
原因
入力値のバリデーション不足
対策
ユーザーから入力される値から、空白コードや改行コードを削除する。
根本的解決
1.ヘッダーの出力用APIを使う
HTTPレスポンスヘッダーをプログラムで直接出力せず、
実行環境や言語に用意されているヘッダ出力用のAPIを使う。
HTTPヘッダの構造は複雑なため、実行環境によっては修正パッチや追加の対策も検討する。
2.改行をできないように処理する
APIを使えないときは、
- 改行の後に空白を入れることで継続行として処理する方法
- 改行コード以降の文字を削除する方法
- 改行が含まれていたらWebページ生成の処理を中止する方法
保険的対策
3.ユーザー入力から改行コードを削除する
ユーザーが入力した全ての値から改行コード(\r, \n など)や制御文字を削除する。
しかし、テキストエリアの入力データなど改行を含むかもしれない文字列からも、改行が削除されてしまう。
Laravelでの実装例
1. Laravelの標準レスポンスを使う
HTTPヘッダの改行コードを自動で除去してくれる。
return response()->redirectTo($url);
☀︎response()は、改行文字(\r, \n)を自動的に削除する。
☀︎redirectTo()は、リダイレクト先を動的に設定できる。
2.外部パラメータは必ずバリデーション処理
どうしてもLocationヘッダに外部パラメータやユーザー入力を使用する場合は、
URL形式やスキームなどを必ずチェックする。
3.TrustHostsミドルウェアを使う
LaravelのTrustHostsミドルウェアを使って許可するホスト名(ドメイン)を指定しておき、
それ以外のHostヘッダーのリクエストを拒否する。
参考にしたサイト
【Security】CSRF(クロスサイト・リクエスト・フォージェリ)+Laravel実装
概要
Webアプリケーションのセッション管理に脆弱性があり、
ユーザーがログインした状態で攻撃者の罠リンクを踏むことで、
ユーザーの知らないうちに攻撃者がユーザーになりすまして、不正な操作を実行する攻撃手法。
発生しうる脅威
個人情報の漏洩
ユーザーの権限範囲内の情報しか閲覧できないが、
パスワード変更などが成功した場合は、その後なりすまし攻撃につながる。金銭的被害
不正送金やクレジットカードの不正利用、ECサイトで意図しない商品購入など登録情報の漏洩・改ざん
各種設定の変更や退会処理、掲示板への不適切な書き込みなど
原因
Webアプリケーションが、
本来は拒否すべき他サイトからのリクエストを受信し処理してしまうこと。
対策
ログイン中のユーザーができる操作に対して対策をする。
根本的解決
1.CSRFトークンを発行する
- リクエストが本当にユーザー本人の意思によるものかどうか確認する。
- ユーザーの入力内容を確認する画面で、サーバーからCSRFトークンを発行しhiddenパラメータに出力する。
フォーム送信するときにhiddenパラメータのトークンとサーバーに保持しているCSRFトークンを比較し、一致しないときは処理を中止する。 - フォーム送信はPOSTメソッドでする。
2.処理の直前に再度パスワードを入力させる
3.Refererをチェックする
Refererでそのリクエストが正しいサイトからか罠サイトからなのかを判別することができ、 正しいページのURLからのときのみ処理を実行する。
ユーザーがRefererを送信しない設定(ブラウザやファイアーウォール)にしているときは、 サイトを利用できないなど不都合が起きるので注意する。
保険的対策
4.登録済みのメールアドレスに自動送信する
- CSRF攻撃自体は防げないが、攻撃されたときに利用者が異変に気づくきっかけになる。
- メール本文にはプライバシーに関わる重要な情報は書かない。
Laravelでの実装例
1.フォームに@csrfと書いてトークンを発行する
ビューのblade.phpファイルに
<form method="post" action="{{ route('ルーティング先') }}"> @csrf 処理内容 </form>
☀︎「@csrf」と書くだけでCSRFトークンが生成され、フォームへ埋め込まれトークンの検証までしてくれる。
2.デフォルトでミドルウェア機能が搭載
Laravelでは「VerifyCsrfToken」ミドルウェアがデフォルトで有効なので、
「@csrf」と書くだけで追加の作業は不要。