ぽかぽかコード日和

とっても暑い夏の日にプログラミングはじめました☀️

【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.プレースホルダーとプリペアドステートメントで実装する
  • プレースホルダーとは、

    • SQL文の変数の部分を「?」や「:name」などの記号で置き換えたもの。
    • これによりSQL構文と値が別々にデータベースに送信され、
      悪意のある値であってもSQLとして解釈されず安全に実行される。
  • 静的プレースホルダ

    • プリペアドステートメントは、
      プレースホルダを含むSQL文を先に準備し、後からデータベース側でバインドして実行する仕組み。
    • 同じSQL構文で何度も実行できるため、効率的に使える。
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.直接メモリ操作ができない言語で記述する

PHPPerlJavaなど、メモリ管理を自動で行ってくれる言語を使う。

2.直接メモリ操作ができる言語で記述する部分を最小限にする

C、C++アセンブラなどの言語のときは、その部分をできるだけ少なくして集中的にチェックをする。

3.脆弱性が修正されたバージョンを使用する

最新のバージョンのライブラリを使用する。

Laravelでの実装例

1.ほぼ発生しない

LaravelではPHPを使っており、メモリ管理を自動で行っている言語のため。

2.入力値のバリデーション

ユーザー入力には必ずバリデーションをかけておく。

$validated = $request->validate([
    'name' => 'required|string|max:255',
]);

☀︎255文字以下の文字列のみ通過できる。


参考にしたサイト

【Security】クリックジャッキング+Laravel実装

概要

正規サイトにログインした状態で、
攻撃者が罠サイトにユーザーを誘導しiframeや透明なレイヤーを使ってリンクやボタンを重ねて配置し、ユーザーに意図しない操作をさせる攻撃手法。

ユーザーが一見無害に見えるサイトのボタンをクリックすると、
実際には背後にあるiframe内のボタンをクリックさせられている。


発生しうる脅威
  • 金銭的被害
    不正送金やクレジットカードの不正利用、ECサイトで意図しない商品購入など

  • 登録情報の漏洩・改ざん
    利用者情報の公開範囲の変更や退会処理

  • SNSやサービスの不正利用
    SNSでの「いいね」やフォロー、投稿などの不正操作をさせられ、情報拡散やアカウントの悪用

  • プライバシー侵害
    カメラやマイクへのアクセス許可をユーザーに気づかれずにONにさせる

原因

  • ログイン後の重要な操作や設定を、クリック操作のみで実行可能にしている仕様

  • iframeで埋め込めるページに、重要な操作の処理ができる機能の実装

対策

根本的解決

1.frameやiframeによるページの読み込みを制限する

HTTPレスポンスヘッダにX-Frame-Options: DENY(またはSAMEORIGIN)と設定すると、
X-Frame-Optionに対応したブラウザでは、frame要素やiframe要素によるページの埋め込みができなくなる。
IE7など対応していないブラウザもあるため、CSPとあわせて対策するといい。

☀︎X-Frame-Optionsヘッダの設定値☀︎
DENY    ・・・すべてのウェブページでフレーム内の表示を禁止
SAMEORIGIN ・・・同一オリジンのときのみ表示を許可する
ALLOW-FROM・・・指定したオリジンのみ表示を許可する
☀︎オリジンとは、 URLのスキーム、ホストドメイン、ポート番号が同じ組み合わせのこと
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) 攻撃」と呼ぶ。


発生しうる脅威
  • XSS攻撃と同じ

    • 重要情報の漏洩
    • Cookie情報の漏洩
    • ブラウザに任意のCookieを保存させられる
    • 偽情報の流布による混乱
  • キャッシュサーバのキャッシュ汚染
    HTTPレスポンス分割攻撃で、
    悪意あるレスポンスをキャッシュサーバに保存されると、 他のユーザーも偽ページや不正スクリプトを閲覧し続けてしまう。

原因

入力値のバリデーション不足

対策

ユーザーから入力される値から、空白コードや改行コードを削除する。

根本的解決

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アプリケーションのセッション管理に脆弱性があり、
ユーザーがログインした状態で攻撃者の罠リンクを踏むことで、
ユーザーの知らないうちに攻撃者がユーザーになりすまして、不正な操作を実行する攻撃手法。

  • Cookieによるセッション管理、Basic認証SSLクライアント認証を実装しているサイトは特に注意する。
    ネットバンキング、会員専用サイトなど金銭処理やログイン機能を持つもの。


発生しうる脅威
  • 個人情報の漏洩
    ユーザーの権限範囲内の情報しか閲覧できないが、
    パスワード変更などが成功した場合は、その後なりすまし攻撃につながる。

  • 金銭的被害
    不正送金やクレジットカードの不正利用、ECサイトで意図しない商品購入など

  • 登録情報の漏洩・改ざん
    各種設定の変更や退会処理、掲示板への不適切な書き込みなど

原因

Webアプリケーションが、
本来は拒否すべき他サイトからのリクエストを受信し処理してしまうこと。

対策

ログイン中のユーザーができる操作に対して対策をする。

根本的解決

1.CSRFトークンを発行する
  • リクエストが本当にユーザー本人の意思によるものかどうか確認する。
  • ユーザーの入力内容を確認する画面で、サーバーからCSRFトークンを発行しhiddenパラメータに出力する。
    フォーム送信するときにhiddenパラメータのトークンとサーバーに保持しているCSRFトークンを比較し、一致しないときは処理を中止する。
  • フォーム送信はPOSTメソッドでする。
2.処理の直前に再度パスワードを入力させる
  • 重要な操作の直前のページでユーザーに再度パスワードを入力させ、 そのパスワードが正しい場合のみ処理をする。
  • CSRFトークンより実装が簡単でコストも下がるが、画面設計の仕様変更が必要となる。
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」と書くだけで追加の作業は不要。


参考にしたサイト