パスワードを一切知らないまま、' OR 1=1-- の一文で管理者としてログイン――「動いてしまうけど危ない」を自分の手で体験しました。そのうえで、見つけた脆弱性を Microsoft Defender for Cloud と GitHub のセキュリティ機能でも検出できるのかを確かめるまでを、1本にまとめました。
はじめに
SQL インジェクションやクロスサイトスクリプティング(XSS)という言葉はよく聞くものの、実際にどんな攻撃なのかを自分の手で試したことはありませんでした。そこで、"意図的に脆弱に作られた Web サイト" である OWASP Juice Shop をローカルで動かして、実際に触ってみることにしました。
この記事でやったことは、次の3つです。
- 攻撃者の視点で脆弱性を体験する:Juice Shop に対して SQLi・XSS・アクセス制御の不備などを手で再現し、なぜ起きるのか(根本原因)と直し方まで確認する
- 守る側の視点で検知できるか試す:同じ脆弱性を Microsoft Defender for Cloud と GitHub のセキュリティ機能で機械的に検出できるか確かめる
-
環境構築を新しい方法で試す:Juice Shop はコンテナアプリなので、最近登場した WSL Containers(
wslc) を使い、Docker Desktop なしで検証環境を用意する
本記事の脆弱性は 練習用に公開許可された環境(OWASP Juice Shop) に対してのみ実施しています。実在するサイトへ許可なく同じ操作を行うと 不正アクセス禁止法違反(犯罪) です。攻撃可否は「サイトが脆弱かどうか」ではなく「持ち主が試す許可を出しているか」で決まります。必ず自分のローカル環境の中だけで試してください。
TL;DR(先に結論)
- 手動で見つかる脆弱性は、ツールでも裏が取れる。 SQLi/XSS は CodeQL(SAST) が、依存関係の既知 CVE は Dependabot(24 件) や Microsoft Defender for Cloud(ユニーク 58 CVE) が検出できた(見える範囲が違う・詳細は本文)。
- 守りは1種類では足りない。多層防御が要る。 開発時にコードを見つける(GHAS / SAST・SCA)、デプロイ前にイメージを固める(Defender / CVE スキャン)、運用時に攻撃を止める(Azure WAF)——見える場所もタイミングも違うので、重ねて初めて OWASP Top 10 をカバーできる。
-
環境構築は
wslcが手軽。 WSL 同梱のwslc.exeを使うと、Docker Desktop も Docker Engine の手動導入も無しにwslc runでコンテナが起動できた。しかも127.0.0.1:3000にバインドされるため、localhost:3000に安定して到達できる。
1. Juice Shop とは何か
OWASP Juice Shop は、セキュリティ団体 OWASP が公開する 学習用にわざと脆弱に作られた EC サイトです。Angular(フロント)+ Node.js/Express(API)+ SQLite/MarsDB(データ)という、今どきの Web アプリと同じ3層構成で、各層に OWASP Top 10 相当の脆弱性が意図的に仕込まれています。
見た目はごく普通の EC サイトだが、フロント・API・データの各層に脆弱性が仕込まれている。
Juice Shop は、いまどきの Web サイトによくある 「フロント(見た目)+ API(処理)+ データベース(保存)」 の3層構成です。全部まとめて1つのコンテナに入っています。
- 練習用に作られているため、この環境の中で攻撃を試すのは合法です(開発者が許可を出している)。
- 逆に、実在サイトへ同じ操作をすれば違法になります。分かれ目は「対策の有無」ではなく「持ち主の許可」です。
そもそも OWASP とは?
OWASP(Open Worldwide Application Security Project) は、Web アプリのセキュリティ向上を目的とした 非営利のオープンコミュニティです。誰でも使えるドキュメント・ツール・教材を無償で公開しており、なかでも有名なのが OWASP Top 10 ——「Web アプリで特に危険な脆弱性トップ10」を数年ごとにまとめたランキングです。SQL インジェクションやアクセス制御の不備などは、この Top 10 に載っている代表的なリスクです。Juice Shop は、この OWASP Top 10 を 手を動かして学ぶための公式教材という位置づけです。
出典: OWASP 公式 / OWASP Top 10
OWASP と Azure の関係は?
「OWASP は団体で、Azure はクラウド」なので直接の資本関係はありませんが、Azure のセキュリティ機能は OWASP の成果物を実装のベースに取り込んでいます。代表的なものは次のとおりです。
| Azure の機能 | OWASP との関係 |
|---|---|
| Azure WAF(Web Application Firewall) | 既定のルールセットに OWASP Core Rule Set(CRS) を採用。SQLi や XSS などの攻撃パターンを OWASP のルールで検知・ブロックする |
| Microsoft Defender for Cloud | Web アプリの推奨事項やアラートが OWASP Top 10 のカテゴリを参照している |
| GitHub Advanced Security(CodeQL) | 検出する脆弱性が OWASP Top 10 / CWE にマッピングされている |
つまり、この記事で Juice Shop を使って学ぶ「OWASP Top 10 の脆弱性」は、そのまま Azure の WAF・Defender・GitHub のスキャンが守ろうとしている対象と重なります。攻撃側で学んだ知識が、そのまま守る側(Azure)の理解につながるというわけです。
2. WSL でコンテナを建てる
Docker Desktop も Docker Engine も入れずに、WSL 同梱の新 CLI wslc だけで Juice Shop を起動します。wslc.exe は 2026 年から WSL に組み込みで同梱されるようになったコンテナ CLI で、Docker ライクな操作で Linux コンテナをビルド・実行できます。
2-1. wslc を使えるようにする
wslc は WSL 2.9 以降に同梱されています。まず WSL 本体を更新します。
# WSL 本体を最新(プレリリース含む)へ更新
wsl --update --pre-release
# 掴んでいるプロセスがあれば一度シャットダウンしてから確認
wsl --shutdown
wsl --version # 2.9.x 以上なら OK
wslc --version # -> wslc 2.9.3.0
wslc の実体は C:\Program Files\WSL\wslc.exe にあり、主なサブコマンドは Docker 互換です。
| コマンド | 役割 |
|---|---|
wslc pull <image> |
イメージ取得 |
wslc run <options> <image> |
コンテナ起動 |
wslc container ps |
起動中コンテナ一覧 |
wslc image ls |
イメージ一覧 |
wslc stop / remove <name> |
停止 / 削除 |
補足:
wsl --updateの MSI が終了コード1622を返しても、実際には更新が成功していることがあります。wsl --versionで2.9.xになっていれば問題ありません。
2-2. Juice Shop を起動する
Juice Shop は公式イメージ bkimminich/juice-shop:latest が Docker Hub に公開されています。ソースを git clone したりビルドしたりする必要はありません。wslc run は Docker と同じく、ローカルにイメージが無ければ 自動で Docker Hub からプルしてから起動するので、下のコマンド1つで完結します。
# Juice Shop を起動(-d でバックグラウンド、3000番を公開)
# ローカルにイメージが無ければ Docker Hub から自動でプルされる
wslc run -it --rm -d -p 3000:3000 --name juice-shop bkimminich/juice-shop:latest
# 起動中コンテナの確認
wslc container ps
事前にイメージだけ取得しておきたい場合は
wslc pull bkimminich/juice-shop:latestを先に実行しておくこともできます(必須ではありません)。
wslc run で起動し、wslc container ps で 127.0.0.1:3000->3000/tcp の公開まで一気に確認できます(検証時は WSL 2.9.3.0 / Windows 11 build 26200)。
-p 3000:3000 は 127.0.0.1:3000 にバインドされるため、キープアライブや .wslconfig の細工なしで Windows の http://localhost:3000 に安定して到達できます。ブラウザで開くと Juice Shop のトップページが表示されます。
3. Juice Shop を触ってみる(攻撃者視点)
ここからは実際に脆弱性を突きます。各項で 手順 → 根本原因 → 修正の方向性 → OWASP Top 10 対応 をセットで押さえます。
3-1. SQL インジェクション:パスワード無しでログイン
手順
- ログイン画面のメールアドレス欄に
' OR 1=1--を入力 - パスワード欄は任意の文字列(例:
aaaa) - ログイン → 先頭ユーザー(多くの場合 admin)としてログイン成功
管理者を直接狙うなら次でも成立します。
admin@juice-sh.op'--
根本原因:ユーザー入力を SQL 文字列にそのまま連結しているため、OR 1=1(常に真)が条件を無効化し、パスワード検証を素通りしてしまいます。
修正の方向性:入力は「データ」として扱い、命令に混ぜない。プレースホルダ(パラメータ化クエリ) を使います。
NG / OK のコード例(クリックで展開)
// NG: 入力を文字列連結(インジェクション可能)
db.query(`SELECT * FROM Users WHERE email='${email}' AND password='${hash}'`)
// OK: パラメータ化クエリ(入力は値として束縛される)
db.query('SELECT * FROM Users WHERE email = ? AND password = ?', [email, hash])
OWASP Top 10 対応: A03:2021 – Injection
3-2. クロスサイトスクリプティング(XSS)
手順:検索欄に次をそのまま入力して Enter。
<iframe src="javascript:alert(`xss`)">
検索欄に入れた文字列が「命令」として実行され、alert('xss') のポップアップが発火した。本来「文字として表示するだけ」のものがコードとして動いた証跡になる。
根本原因:ユーザー入力を エスケープせずに DOM へ埋め込み、文字として表示すべきものを「命令」として実行させてしまっています。
修正の方向性:出力時のエスケープ/サニタイズ。フレームワークの安全な出力(Angular の既定バインディングなど)を使い、bypassSecurityTrust* の乱用や innerHTML への生入力を避けます。
OWASP Top 10 対応: A03:2021 – Injection(XSS を包含)
3-3. アクセス制御の不備:機密ファイルの流出
手順:ブラウザで次を開くと、本来見えてはいけないファイル一覧と機密文書が読めます。
http://localhost:3000/ftp
http://localhost:3000/ftp/acquisitions.md
一覧には acquisitions.md や incident-support.kdbx など、本来公開されるべきでないファイルが並ぶ。
ファイルを開くと、"This document is confidential!(社外秘・配布禁止)" と書かれた買収計画そのものが、ログインもせず読めてしまう。
根本原因:公開してはいけないパスに 認可チェック無しでアクセスできる。「URL を知らなければ大丈夫」は成立しません(総当たりで発見されます)。
修正の方向性:公開/非公開ディレクトリを明確に分離し、非公開リソースにはサーバー側で必ず認証・認可を通す。そもそも不要なファイルを置かない。
OWASP Top 10 対応: A01:2021 – Broken Access Control
3-4. 隠しページ:score-board
手順:メニューに無い開発者向けページも、URL 直打ちで開けます。
http://localhost:3000/#/score-board
根本原因:リンク(入口)を消しただけで、ページ本体はアクセス可能なまま。「画面から消す」は「守る」ではありません。
修正の方向性:本当に隠したいものはサーバー側でアクセス制御する。クライアント側で隠すだけは不可。
OWASP Top 10 対応: A01:2021 – Broken Access Control
4. 脆弱性を検知してみる(守る側の視点)
手動で見つけた脆弱性を、守る側のクラウド・開発プラットフォームの機能で機械的に検出できるか確認します。ここでは Microsoft Defender for Cloud(コンテナイメージの CVE) と GitHub Advanced Security(ソースの SAST/SCA) の2つを使います。
4-1. Defender for Containers による脆弱性スキャン
Juice Shop のイメージを Azure Container Registry(ACR)に取り込み、Microsoft Defender for Cloud のコンテナ向け脆弱性評価(agentless、Microsoft Defender 脆弱性管理ベース) で既知 CVE を可視化します。ローカルに Docker が無くても az acr import で Docker Hub から直接取り込めます。
仕組み:Defender for Containers の脆弱性評価は エージェントレスで、Microsoft Defender 脆弱性管理(MDVM) を用います。レジストリ内のイメージを各レイヤーまで解析して、OS パッケージ(Linux/Windows)と言語パッケージ(Linux の npm など) の一覧(ソフトウェアインベントリ)を作り、それを MDVM の脆弱性データベースと突き合わせて既知の CVE を洗い出し、修復推奨として提示します。イメージを実際に動かす必要はなく、レジストリへ push / import すると数時間以内にスキャンされ、以後は日次で再スキャンされます。
ポイント: ここで使った Defender for Containers の脆弱性評価が見つけるのは、「イメージに同梱された、既知 CVE を持つパッケージ(OS・言語パッケージ)」 です。アプリ自身のコードのロジック欠陥(SQLi・XSS など)は検出しません——それは SAST(CodeQL)や DAST・手動テストの領域です。(Defender for Containers にはランタイム脅威検知や構成ミスの検出といった別機能もありますが、いずれも Web アプリの SQLi/XSS を見つけるものではありません。)
ACR 作成→イメージ取込→Defender 有効化のコマンド(クリックで展開)
# 0) 必要なリソースプロバイダーを登録
az provider register -n Microsoft.ContainerRegistry
az provider register -n Microsoft.Security
# 1) RG と ACR(Basic)を作成
az group create -n rg-juiceshop-defender -l japaneast
az acr create -n <acrName> -g rg-juiceshop-defender --sku Basic
# 2) イメージを ACR へ直接インポート(ローカル Docker 不要)
az acr import -n <acrName> --source docker.io/bkimminich/juice-shop:latest --image juice-shop:latest
# 3) Defender for Containers を有効化(課金が発生)
az security pricing create -n Containers --tier Standard
有効化するとエージェントレスのレジストリ脆弱性評価がイメージをスキャンします。反映まで数十分〜数時間かかり、結果はポータルの推奨事項「コンテナー レジストリ イメージは脆弱性の検出結果を解決する必要があります」で確認できます。
CLI で件数を集計したい場合は、REST API(サブアセスメント)を直接叩くのが確実です。
REST で CVE 所見を重大度別に集計する(クリックで展開)
# CVE 所見(サブアセスメント)を REST で取得して重大度別に集計
$sub = "<subscriptionId>"
$j = az rest --method get --url "https://management.azure.com/subscriptions/$sub/providers/Microsoft.Security/subAssessments?api-version=2019-01-01-preview" | ConvertFrom-Json
$j.value | Group-Object { $_.properties.status.severity } | Sort-Object Count -Descending | ForEach-Object { "{0,-10} {1}" -f $_.Name, $_.Count }
補足:
az security sub-assessment listは、コンテナレジストリ脆弱性のサブタイプ(AzureContainerRegistryVulnerability)を CLI がモデルに変換できず結果が 0 件に見えることがあります。上記のように REST で生 JSON を取得すると正しく集計できます。
今回の juice-shop イメージでは、ユニークな CVE で 58 件(延べ 124 件)が検出されました。手動で見た「アプリ自身のロジックの穴」とは異なり、同梱される OSS 依存パッケージ(Node.js ライブラリや OS パッケージ)の既知 CVE が大量に見つかるのが特徴です。
補足: スクショの「脆弱性の合計 124」は、
juice-shop:latestが マルチアーキイメージ(linux/amd64 と linux/arm64 の2プラットフォーム) で、Defender が両方のイメージを別々にスキャンしたためです(約 62 件 × 2)。同じ CVE が両アーキで重複計上されるので、実質はユニークな CVE 58 件が本質的な数字です。マルチアーキのタグを取り込むと、こうして対象イメージが複数になる点は覚えておくと混乱しません。
主な検出 CVE(重大度別の代表例)は次のとおりです。
| 重大度 | 代表的な CVE | どんな脆弱性か |
|---|---|---|
| Critical | CVE-2019-10744(lodash) |
defaultsDeep のプロトタイプ汚染で任意プロパティを注入され、DoS 等に悪用される |
| High | CVE-2021-23337(lodash) |
template の入力サニタイズ不備を突いて任意コマンドを実行される |
| High | CVE-2026-31802 ほか(node-tar) | tar 展開時にシンボリックリンク/ハードリンクで展開先の外へファイルを書き込まれる(パストラバーサル) |
| High | CVE-2024-38355 ほか(Socket.IO) | 細工したパケットで未処理例外やメモリ枯渇を起こし、サーバーを停止させる(DoS) |
| High | CVE-2026-41907(uuid) | バッファ境界チェック不備により範囲外書き込みが起きる |
Defender for Cloud のポータルで、脆弱性の合計・ユニークな CVE 数・重大度別の内訳を一覧できる。今回はマルチアーキのため「スキャン対象イメージ 2」となっている。
個々のイメージ(例: linux/amd64)を開くと、CVE 番号・パッケージ名・インストール中のバージョン・修正版(最終版)まで一覧できます。1イメージあたり 62 件・ユニーク 58 CVE(これが2プラットフォーム分で延べ 124 になる)で、crypto-js・lodash・multer・ws・uuid など同梱された Node.js 依存パッケージの CVE が「Fix Available(修正版あり)」で並びます。
「修正版(最終版)」列があるので、どのバージョンに上げれば直るかがそのまま分かる。パッケージの種類が Language(Node.js 依存)や OS で分類される点も、Defender が OS+言語パッケージの両方を見ていることの裏付けになる。
注意: Defender for Containers プランは有効化すると課金が発生します。検証後に
az security pricing create -n Containers --tier Freeと ACR 削除でクリーンアップします。
4-2. GitHub Advanced Security による脆弱性スキャン
Juice Shop のソースを自分の GitHub リポジトリにフォークすると、ソース側の脆弱性も機械検出できます(公開リポジトリなら無料)。
フォーク→Dependabot / Secret scanning / CodeQL を有効化するコマンド(クリックで展開)
# フォーク
gh repo fork juice-shop/juice-shop --clone=false
# Dependabot アラート・Secret scanning を有効化
gh api -X PUT /repos/<user>/juice-shop/vulnerability-alerts
'{"security_and_analysis":{"secret_scanning":{"status":"enabled"}}}' | gh api -X PATCH /repos/<user>/juice-shop --input -
# CodeQL コードスキャン(既定セットアップ)を有効化 → 解析ワークフローが起動
gh api -X PATCH /repos/<user>/juice-shop/code-scanning/default-setup -f state=configured
有効化すると、リポジトリの Security タブに大きく Dependabot ・ Code scanning ・ Secret scanning の3つが並びます。それぞれ守備範囲が違うので、順に見ていきます。
| 機能 | 何を見るか | 種別 |
|---|---|---|
| Dependabot | 使っている OSS ライブラリ(依存パッケージ)の既知脆弱性 | SCA |
| Code scanning | 自分たちで書いたソースコードの脆弱性 | SAST |
| Secret scanning | コードに紛れ込んだ API キー・パスワードなどの秘密情報 | シークレット検出 |
① Dependabot(依存パッケージのスキャン:SCA)
SCA(Software Composition Analysis) とは、アプリが利用している OSS ライブラリ(依存パッケージ) を洗い出し、その中に既知の脆弱性(CVE)が無いかを照合する手法です。自分で書いたコードではなく「借り物の部品」の安全性をチェックするイメージです。Dependabot はその SCA を GitHub 上で自動実行する機能で、package-lock.json などの依存情報を脆弱性データベースと突き合わせ、危険なバージョンを使っていればアラートを出します。
意図的に脆弱な依存を使う Juice Shop では、Dependabot が 24 件(Critical 2 / High 12 / Medium 10) を検出しました。
| 重大度 | 検出項目(パッケージ) | どんな脆弱性か | 対応する手動手法 |
|---|---|---|---|
| Critical | jsonwebtoken | JWT(ログイン用トークン)の署名検証を回避され、偽のトークンで本人になりすませる | - |
| Critical | marsdb | 外部からの入力を通じて任意のコマンドを実行される(コマンドインジェクション) | 3-1(注入系) |
| High | express-jwt | 認可チェックをすり抜けられ、権限のない操作を許してしまう | 3-3(アクセス制御) |
| High | sanitize-html | 細工した文字列で正規表現処理が暴走し、サービス停止に追い込まれる(ReDoS) | - |
| High | multer | ファイルアップロード処理でメモリリーク等が起き、サービスが停止する(複数の DoS) | - |
| High | @cyclonedx/cyclonedx-npm |
--workspace 引数のサニタイズ不備を突いてシェルコマンドを注入される |
3-1(注入系) |
② Code scanning(ソースコードのスキャン:SAST)
SAST(Static Application Security Testing) は、アプリを動かさずに ソースコードそのものを静的に解析して脆弱性を探す手法です。GitHub では CodeQL というエンジンがこれを担い、「ユーザー入力がどこから来て、どの処理に流れ込むか」を追跡して危険な経路を洗い出します。
CodeQL は Juice Shop の JS/TS を解析し、107 件(Critical 7 / High 59 / Medium 41) を検出しました。手で再現した SQLi や XSS を、コードレベルで裏取りできている点が重要です。
| 重大度 | 検出項目(CodeQL ルール) | どんな脆弱性か | 対応する手動手法 |
|---|---|---|---|
| Critical | Code injection | 外部入力が任意コードとして実行される | 3-1(注入系) |
| Critical | Server-side request forgery | サーバーに任意の宛先へリクエストさせられる(SSRF) | - |
| Critical | Template Object Injection | テンプレートに悪意ある入力を注入される | - |
| Critical | Type confusion through parameter tampering | パラメータ改ざんで型を誤認させ処理を乗っ取る | - |
| High | Database query built from user-controlled sources | ユーザー入力からSQL文を組み立てている | 3-1(SQLi) |
| High | Uncontrolled data used in path expression | 入力でファイルパスを操作される(パストラバーサル) | 3-3(機密ファイル流出) |
| Medium | Incomplete string escaping or encoding | エスケープ漏れで文字列がそのまま出力される | 3-2(XSS) |
検出されたアラートには Copilot Autofix が付き、修正コードの提案までワンクリックで得られます。
③ Secret scanning(秘密情報のスキャン)
Secret scanning は、ソースコードやコミット履歴に紛れ込んだ API キー・トークン・パスワードなどの秘密情報を検出する機能です。うっかりコミットした認証情報は、公開リポジトリなら第三者に悪用される危険があります。
今回の Juice Shop フォークでは、Secret scanning の結果は 0 件(No secrets found) でした。
意外に思えますが、これは Secret scanning の検出範囲を理解する良い例です。Secret scanning のパターンは大きく3種類あり、既定(公開リポジトリで自動・無料)で検出されるのは、主に AWS・GitHub・Stripe など特定サービスが発行するトークンの形式(プロバイダーパターン) です。一方、秘密鍵や DB 接続文字列などの汎用シークレット(generic patterns)や、パスワードなどの非構造な秘密(AI 検出)は既定では対象外で、別途有効化が必要です。
Juice Shop は学習用に RSA 秘密鍵などをハードコードしていますが、これは「汎用シークレット」側に当たるため、既定のスキャンでは拾われず 0 件となりました。
ポイント: 「Secret scanning が 0 件」=「秘密情報が無い」ではありません。秘密鍵や独自形式の値まで捕まえたい場合は、汎用シークレット検出(generic patterns) や カスタムパターンを有効化する必要があります。そもそもハードコードしないのが原則です。
出典: Supported secret scanning patterns | GitHub Docs / About secret scanning | GitHub Docs
4-3. 手動発見 vs スキャン結果の対応
| 手動で見た脆弱性 | イメージ CVE (Defender) | SAST/SCA (GHAS) |
|---|---|---|
| SQLi(3-1) | - | ○ CodeQLが検出 |
| XSS(3-2) | - | ○ CodeQLが検出 |
| アクセス制御不備(3-3) | - | △ |
| 隠しページ(3-4) | - | - |
| 依存関係の CVE | ○ ユニーク58件検出 | ○ Dependabotが24件検出 |
- 見える範囲が違う ことが読み取れる点が重要(イメージ CVE は依存パッケージ、SAST/SCA はソースコード)。依存の CVE は Defender と Dependabot、注入系は CodeQL(SAST)が担う。
- 手動でしか見つからない穴もある。隠しページ(3-4)のように「URL を知っているか」に依存する問題は、どのスキャナも検出しない。ツールと手動の探索は補完関係にある。
-
同じ「依存の CVE」でも見え方が違う。Defender はコンテナイメージ全体(OS パッケージ+ビルド済みの全 Node.js 依存)をスキャンしてユニーク 58 CVE、Dependabot は**リポジトリの
package-lock.json(npm 依存)**を見て 24 件。対象範囲が違うので、両方を併用すると死角が減る。
4-4. Defender for Containers と GitHub Advanced Security の使い分け
この2つは競合するものではなく、見る場所とタイミングが違う補完関係です。GitHub Advanced Security は開発側(ソースコード・リポジトリ) を左シフトで、Defender for Containers は運用側(実際にデプロイするコンテナイメージ) を検査します。
| 観点 | GitHub Advanced Security | Defender for Containers |
|---|---|---|
| 見る対象 | ソースコードとリポジトリ(package-lock.json 等) |
ビルド済みコンテナイメージ(OS+言語パッケージ) |
| 検出できるもの | アプリコードの SQLi/XSS 等(CodeQL)、npm 依存の CVE(Dependabot)、秘密情報(Secret scanning) | イメージ内の既知 CVE(OS・ベースイメージ・同梱依存)、構成ミス、ランタイム脅威 |
| 苦手(見えない) | OS・ベースイメージ層の CVE、デプロイするイメージの実態 | アプリコードのロジック欠陥(SQLi/XSS)、ソースの秘密情報 |
| 当たるタイミング | PR / コミット / CI(開発中・左シフト) | ビルド後〜デプロイ前後(成果物・運用) |
| 主な利用者 | 開発者 | セキュリティ・運用(SecOps) |
使い分けの目安:
- アプリのコードに潜む脆弱性(SQLi/XSS/パストラバーサル等) → GHAS(CodeQL)。Defender では出ない。
-
ベースイメージや OS パッケージの CVE、実際にデプロイするイメージの中身 → Defender for Containers。GHAS は
package-lock.jsonしか見ないので OS 層は見逃す。 - npm 依存の CVE → 両方で見える(GHAS=Dependabot は宣言依存、Defender はビルド済みイメージ内の実体)。見る場所が違うので両方あると確実。
- 秘密情報のハードコード → GHAS(Secret scanning)。
結局は「開発で GHAS、デプロイ・運用で Defender」を重ねるのが定石です。どちらか一方だけでは、コードの穴か OS 層の穴のどちらかが残ります。
出典: Introduction to Microsoft Defender for Containers | Microsoft Learn / GitHub Advanced Security について | GitHub Docs
4-5. 運用時に守る手段(Azure WAF ほか)
ここまでは「開発~デプロイの段階で脆弱性を見つける」視点でしたが、本番で稼働中のアプリを守る手段も別にあります。とくに SQLi/XSS のような攻撃に対しては、運用時の防御が効きます。
- Azure WAF(Application Gateway / Azure Front Door):受信する HTTP リクエストを OWASP Core Rule Set(CRS) で検査し、SQLi・XSS などの攻撃パターンを受信時にブロックします(入口の門番・仮想パッチ)。記事冒頭の「OWASP と Azure の関係」で触れた、Azure が OWASP のルールを取り込んでいる部分です。
- Microsoft Defender for App Service / Defender for APIs:稼働中の Web アプリや API への攻撃の試みを検知してアラートします(ランタイム脅威検知)。
ただし WAF は「攻撃を入口で止める」緩和策(仮想パッチ) であって、コード側の穴そのものは残ります。理想は SAST(CodeQL)でコードを直す + WAF で運用時に守るの多層防御です。「見つける(GHAS)」「イメージを固める(Defender for Containers)」「運用時に止める(WAF)」を重ねるほど、守りは堅くなります。
出典: Azure Web Application Firewall の概要 | Microsoft Learn / Microsoft Defender for App Service の概要 | Microsoft Learn
まとめ
- 手動で見つかる Web 脆弱性は、ツールでも裏が取れる。 Juice Shop を題材に、SQLi/XSS/アクセス制御不備を手で再現し、根本原因と修正コードまで確認した。
- 守りは複数の視点を重ねる。 コードを見つける(GHAS / CodeQL・Dependabot)、イメージを固める(Defender for Containers / CVE)、運用時に止める(Azure WAF)——見える場所とタイミングが違うので、重ねて初めて OWASP Top 10 をカバーできる。
-
wslcで環境構築が手軽に。 WSL 同梱のwslcを使えば Docker Desktop も Docker Engine も不要でコンテナを起動できた。 - セキュリティは「あとで付ける」ものではない。 作る=守る。設計段階からの作り込みが結局いちばん安い。
参考リンク
- OWASP Juice Shop 公式
- OWASP Juice Shop ソースコード(GitHub)
- WSL コンテナー(wslc)| Microsoft Learn
- Microsoft Defender for Containers の概要 | Microsoft Learn
- GitHub Advanced Security について
- 不正アクセス禁止法(e-Gov 法令検索)
本記事は GitHub Copilot および Microsoft Foundry を活用して作成されています。内容の正確性については各公式ドキュメントをご確認ください。













