Laravel 演習入門

危険なスクリプト

View の基礎

問題

以下のようなページを作成してください:

  1. POST /not-dangercontent として文字列を送信すると、その文字列がそのまま表示される
    • たとえば、Hello Laravel! を送信すると、 Hello Laravel! が表示される
  2. 上記の POST /not-danger へ、 content として <script>alert('danger')</script> を送信したとき、以下の2つを同時に満たす
    • ブラウザ画面上に、<script>alert('danger')</script>文字列として 表示される
    • JavaScript の alert 関数は、実行されない

問題は、以下の手順で解いてください。

  1. Red:小さいテストを作成し、失敗を確認してください
  2. Green:テストを成功させてください
  3. Refactor:整理・整頓してください
  4. 必要に応じて、1から3を繰り返してください

ヒント

背景知識
便利なアサーションの例

アサーションの調べ方 も合わせてご覧ください。
今回は、以下を使うのではないかと思います。

解答例

続きを読む

実行環境:

Red1: POST で受け取ったコンテンツをそのまま表示する

テストを作成し、失敗を確認します。
bash:

php artisan make:test NotDangerTest

tests/Feature/NotDangerTest.php:

<?php

namespace Tests\Feature;

use Tests\TestCase;

class NotDangerTest extends TestCase
{
    public function test_postで受け取った文字列がそのまま表示される(): void
    {
        $hello = 'Hello Laravel!';
        $response = $this->post('/not-danger', ['content' => $hello]);

        $response->assertStatus(200)
            ->assertSeeText($hello);
    }
}

Green1

このテストだけなら、以下のコントローラーで通ります。
bash:

php artisan make:controller NotDangerController

app/Http/Controllers/NotDangerController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class NotDangerController extends Controller
{
    public function show(Request $request) {
        $content = $request->input('content');
        return $content;
    }
}

routes/web.php:

use App\Http\Controllers\NotDangerController;

Route::post('/not-danger', [NotDangerController::class, 'show']);

Refactor1

テストとしては通るものの、このままでは XSS 脆弱性があるため、実際にはこのような実装を書いてはいけません。
次のテストで修正します。

Red2: エスケープ処理する

テストを追加します。
tests/Feature/NotDangerTest.php:

    public function test_エスケープ処理されているか(): void
    {
        $hello = '<script></script>';
        $response = $this->post('/not-danger', ['content' => $hello]);

        $response->assertStatus(200)
            ->assertDontSee($hello, false);
    }

失敗を確認します。

Green2

コントローラーを修正します。
app/Http/Controllers/NotDangerController.php:

    public function show(Request $request) {
        $content = $request->input('content');
        return htmlspecialchars($content);
    }

Refactor2

Blade を導入し、見やすくします。(いきなり Inertia.js を使っても OK です)
bash:

php artisan make:view not-danger

resources/views/not-danger.blade.php:

<div>{{ $content }}</div>

コントローラーから view を呼びます。
app/Http/Controllers/NotDangerController.php:

    public function show(Request $request) {
        $content = $request->input('content');
        return view('not-danger', ['content' => $content]);
    }

テストが成功していることを確認します。

解説

続きを読む

XSS 脆弱性について

XSS(クロスサイトスクリプティング)脆弱性とは、信頼できない外部入力が HTML 出力に挿入されることで、意図しない(悪意ある)処理が実行されてしまうことです。

たとえば、今回の問題のように、 <script>alert('danger')</script> という文字列をエスケープせずに表示してしまうと、 JavaScript が実行されます。

web アプリケーションを作成する際の脆弱性対策については、「体系的に学ぶ 安全なWebアプリケーションの作り方」(通称「徳丸本」)を参照してください。

View について

あらゆる出力をエスケープ処理することは、基本的なことなので、何も考えずにできる仕組みが必要です。
(何かを考えて実装しなければならない場合、自分や他人の「うっかり」を防げません)

さまざまな仕組みがありますが、Laravel では、Blade や、フロントエンドフレームワーク(React など)で自動的にエスケープしてくれます。

これらは View 層と呼ばれ、コントローラーから呼ばれ、適切な出力を作成します。
最終的にその出力は、 Response オブジェクトとなり、クライアントにレスポンスされます。


<= 問題を読んだ・解いた・理解したなどのチェックにご利用ください。クリックすると、チェックが変化します。
問題一覧に戻る