Skip to main content
友田 陽大
Application-layer security
セキュリティ
脆弱性診断
OWASP
Next.js
DevSecOps

How to Do Web-App Vulnerability Assessment [2026 Edition] — A Practical Guide to Automating with the OWASP Official Methods (Top 10:2025 / WSTG / ASVS) and ZAP・SAST

A hands-on for practicing web-app vulnerability assessment from 'the range you can do yourself.' Faithful to the official methods of OWASP Top 10:2025・WSTG v4.2・ASVS 5.0, it explains SCA・SAST(Semgrep)・secret scanning・DAST(ZAP)・CI/SARIF integration with real code, and honestly shows the limits of automation (authorization/IDOR・business logic) and the boundary where a professional audit is needed.

Published
Reading time
23 min read
Author
友田 陽大
Share
Contents

Let me state the conclusion first. Vulnerability assessment splits into "horizontal holes you can comprehensively crush with tools" and "vertical risks you can plug only with a design review." And the most cost-effective is this order — first sweep the horizontal holes with free official tools, and route to humans only the vertical risks whose correctness machines can't judge.

"I want to outsource vulnerability assessment, but I don't know what and how much I can do myself," "I hear the word OWASP but don't know the concrete how-to" — this article runs through, end to end, the "range you can do yourself" that precedes that, with a hands-on faithful to the OWASP official methods. SCA・SAST・secret scanning・DAST (ZAP)・CI integration, all shown with actually-working code.

Let me note the most important thing first. No automated tool guarantees "completely safe." Even with a perfect scan, the access-control (authorization) flaw that causes the most leaks can slip right through. This article draws, as honestly as possible, the boundary — how far is free and automatable, and where on does the professional's audit realm begin.

This article's target readers: individual developers / startup developers / those in a position to make technology choices, on modern web stacks like Next.js / Supabase. The code examples use Next.js (App Router) as the subject, but the assessment thinking and tools are framework-independent.


0. ⚠️ Most Important: Confirm "Permission" Before Starting Assessment

Before the technology, be sure to pin down law and ethics first. Skip this and a well-intentioned assessment can become an illegal act.

  • You may assess only an asset you manage, or a target for which you have explicit written permission. Don't fire scans at others' services or unauthorized production systems.
  • Especially DAST (dynamic assessment)'s "active scan" actually sends attack requests. In Japan, there's a risk of violating the Act on the Prohibition of Unauthorized Computer Access, etc. Never do an active scan against an unauthorized third party.
  • When assessing on the cloud, confirm each provider's test policy in advance. AWS / Google Cloud / Azure each publish rules on "permitted security testing." In a shared-tenant environment (serverless, etc.), out-of-scope services and bandwidth / DoS-ish load can be restricted.
  • Avoid an active scan against production in principle, and do it on staging/local. The ZAP baseline scan (passive) alone is safe even in production, as described later, but remember the full scan (active) is test-environment only.

Uphold just this one line and everything henceforth is a legitimate procedure for "diagnosing your own app, safely, yourself." Those who want to systematically grasp the boundary of law and ethics, read alongside White Hackers and the Law [Definitive Edition] (the Unauthorized Access Act, active cyber defense, the correct way to report a vulnerability).


1. Organizing Terms — What Vulnerability Assessment Is, and Isn't

To judge the validity of an estimate or proposal for a commission, let me pin down the difference from nearby words.

TermThe main questionThe how-toTreatment in this article
Vulnerability Assessment (VA)Are any known vulnerabilities left?Tool-centric scanning (broad, automatic)The subject of this article. Can be done yourself
Penetration testCan an attacker actually break in?Manual attack from the attacker's viewpoint (deep, narrow, demonstrative)The professional realm. A touch in this article
Security auditAre the design and implementation done "correctly"?Auto-detection + design review + remediation designVertical risk. The boundary shown in §9
Code reviewAre there flaws in this change?Human reading (scope = diff/PR)Daily quality assurance

Vulnerability Assessment is the activity of "comprehensively and repeatedly surfacing whether known vulnerability patterns are left." The portion automatable with tools is large, which is exactly why it's the realm you should do yourself first. On the other hand, a penetration test or audit requires "human creativity / design understanding," so it's high-cost and can be run only at limited frequency.

"What an audit is / when it's needed / the cost feel" is detailed in a separate article, What a security audit looks at — the range where automation suffices, and the range an audit is needed. This article, before that, goes all-in on "implementing auto-assessment."


2. Fix OWASP's Official "3 Pillars" at Their Latest Versions

The world standard of vulnerability assessment is published free by OWASP (Open Worldwide Application Security Project). What to reference in practice is 3 official documents with different roles. Using them distinctly without conflating is the first step to systematizing assessment.

Official documentThe question it answersLatest version (as of June 2026)When to use
OWASP Top 10What to look for (the priority of risks)Top 10:2025 (official version January 2026)Decide the assessment's target scope
OWASP WSTGHow to test (test procedures)v4.2 (v5.0 in progress)The concrete test method for each item
OWASP ASVSHow far to satisfy (the achievement criteria)v5.0 (published May 2025)Requirements definition / acceptance criteria

Use them in the flow "size it up with Top 10 → move your hands with WSTG → measure pass/fail with ASVS."

2-1. OWASP Top 10:2025 — What Changed

OWASP Top 10:2025, whose official version came out in January 2026, is the latest "TOP 10 most-critical web-app risks" analyzing over 175,000 CVEs and 248 CWEs. Let me correctly grasp the changes from the 2021 edition.

RankCategory (2025)Change from 2021
A01Broken Access ControlHolds 1st. SSRF merged here
A02Security Misconfiguration5th → rose to 2nd
A03Software Supply Chain FailuresExpanded the scope and renamed from "vulnerable dependencies"
A04Cryptographic FailuresDescended
A05InjectionDescended. Includes XSS
A06Insecure DesignHeld
A07Authentication FailuresRenamed from "Identification and Authentication Failures"
A08Software or Data Integrity FailuresHeld
A09Security Logging & Alerting FailuresThe emphasis moved from "Monitoring" to "Alerting"
A10Mishandling of Exceptional ConditionsCompletely new (error handling, abnormal cases)

Practical points:

  • A01 is still 1st, and that SSRF is merged into A01 is this edition's biggest structural change. As described later, A01's core — authorization (IDOR/BOLA) — is the realm automated tools are weakest at. Here is the heart of vertical risk.
  • With A03 expanded to the whole supply chain, not just dependency libraries but the build pipeline・the integrity of CI/CD are included in the assessment target. Solidify the foundation with the later SCA + secret scanning.
  • A10 (exceptional conditions) is new. Missed error handling and information exposure in abnormal cases became an independent category. Type-safe languages and strict error handling work.

2-2. OWASP WSTG v4.2 — The Map of the "How-To" of Testing

Web Security Testing Guide v4.2 is the official handbook comprehensively covering actual test procedures. If Top 10 is "what," WSTG shows "how," in the following 12 categories (4.1–4.12).

  1. 4.1 Information Gathering
  2. 4.2 Configuration and Deployment Management Testing
  3. 4.3 Identity Management Testing
  4. 4.4 Authentication Testing
  5. 4.5 Authorization Testing (authorization — IDOR is here)
  6. 4.6 Session Management Testing
  7. 4.7 Input Validation Testing (SQLi/XSS/SSRF are here)
  8. 4.8 Testing for Error Handling
  9. 4.9 Testing for Weak Cryptography
  10. 4.10 Business Logic Testing (business logic)
  11. 4.11 Client-side Testing (DOM XSS is here)
  12. 4.12 API Testing

Of these, most of 4.1–4.4・4.6–4.9・4.11・4.12 are coverable by automation. On the other hand, 4.5 (authorization) and 4.10 (business logic) are extremely hard to mechanize — these 2 are the "vertical risk" handled in the article's latter half. When designing an assessment, it's organized to think of color-coding "the range automation can paint" and "the range painted by hand" on top of this map first.

2-3. OWASP ASVS 5.0 — The Yardstick of "How Far to Pass"

ASVS (Application Security Verification Standard) 5.0 (published May 2025) is the "checklist of achievement criteria" organizing about 350 verification requirements into 17 chapters. Apply it in stages at 3 levels.

  • L1 (basic): the minimum all apps should satisfy. The range verifiable by an external black-box assessment.
  • L2 (standard): the recommended level for many apps handling confidential data. A SaaS handling personal info / billing targets here.
  • L3 (advanced): systems needing the highest level, like medical, finance, critical infrastructure.

In practice, it's effective to build it into the acceptance criteria (Definition of Done) like "our product targets satisfying ASVS L2." Divide roles: assessment is the work of "finding holes," ASVS the yardstick of "measuring whether they're plugged."

[Latest, official caution] The standard DAST tool "ZAP" is no longer an OWASP project. ZAP (Zed Attack Proxy), described later, was long called "OWASP ZAP," but left OWASP in 2023, and since September 2024 development continues under the Checkmarx regime ("ZAP by Checkmarx"). The license remains OSS (Apache-2.0-equivalent) and free, with features and community operation maintained. The Docker image name owasp/zap2docker-stable from old articles is also discontinued, and it's now ghcr.io/zaproxy/zaproxy:stable. Get this wrong and it won't run.


3. The Whole Picture of Assessment — Decomposing Auto-Assessment into "4 Layers"

Auto-doable vulnerability assessment decomposes into 4 kinds of tools that observe different targets. The table mapping which part of OWASP Top 10:2025 each crushes is below. This mapping is the "map" I most want you to take home from this article.

LayerTool typeWhat it seesThe OWASP Top 10:2025 it mainly crushesRepresentative tools (free)
DependenciesSCApackage.json/lockfileA03 (supply chain)npm audit, Dependabot, OSV-Scanner
CodeSASTThe source's data flowA05 (injection), A04, A02Semgrep, CodeQL
SecretsSecret scanningThe repo / the diffA02/A03, A04Gitleaks, GitHub Secret Scanning
BehaviorDASTReal requests to a running appA01/A05/A02ZAP, Nuclei

Line up the 4 "in order of fast / shallow" and the order to build into CI comes into view.

[PR毎・秒〜分]  SCA → シークレットスキャン → SAST    (静的・速い・本番不要)
[夜間・リリース前・分〜時]  DAST baseline → DAST full    (動的・遅い・要デプロイ)

Running "the fast and cheap first" in this order is the DevSecOps standard. Below, per layer, I show actually-working commands and code.


4. Hands-On ① SCA — Known Vulnerabilities in Dependency Libraries (A03)

The cheapest and most effective is here. Even if your code is perfect, if a known CVE sleeps inside node_modules, it's a hole. Now that A03 (supply chain) was promoted in 2025, it's top priority.

4-1. npm audit — First, One Command

# 既知脆弱性のある依存を検出(lockfileベース、ネットワーク不要のオフライン照合)
npm audit

# CIで使う:moderate以上が1件でもあれば exit code 1 で落とす
npm audit --audit-level=moderate

# 自動修正(メジャー更新を伴わない安全な範囲のみ)
npm audit fix

# 機械処理用にJSONで取得
npm audit --json > npm-audit.json

Set the --audit-level threshold to moderate and you can "auto-block a PR that mixed in a moderate-or-higher vulnerability." This alone plugs most of A03.

4-2. Dependabot — From "Detection" to "Auto-PR"

Detecting and stopping there doesn't make operations run. Enable GitHub Dependabot and it auto-generates fix PRs against vulnerable dependencies. Just place this one file in the repository.

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    # セキュリティ修正は即時、通常更新は週次にまとめてノイズを抑える
    open-pull-requests-limit: 10
    groups:
      # マイナー/パッチをまとめて1PRに(レビュー負荷を下げる)
      minor-and-patch:
        update-types: ["minor", "patch"]

A cost-efficiency hint: SCA suffices with the free ecosystem standard (npm audit + Dependabot). You needn't include SCA in a paid vulnerability assessment. Settle this with automation, and route the human budget to §9's vertical risk — that's the right answer.


5. Hands-On ② SAST — Static Analysis of Source Code (A05 etc.)

SAST (Static Application Security Testing) tracks the data flow without running the code, detecting paths like unsanitized input flowing into SQL or dangerouslySetInnerHTML. It's a layer strong on SQLi・command injection・path traversal・open redirect・DOM XSS (A05).

A free and practical one is Semgrep. An OWASP-official ruleset is also provided.

5-1. Semgrep — First, Diagnose the Whole with One Command

# インストール不要で実行(自動でスタックを判定し最適ルールを適用)
npx semgrep scan --config=auto

# OWASP Top 10 特化ルールセットで診る
npx semgrep scan --config=p/owasp-top-ten

# 複数ルールセットを束ねて、結果をSARIFで出力(GitHub連携用)
npx semgrep scan \
  --config=p/owasp-top-ten \
  --config=p/javascript \
  --config=p/typescript \
  --config=p/react \
  --config=p/secrets \
  --sarif --output=semgrep.sarif

p/owasp-top-ten is a rule collection corresponding to each OWASP Top 10 category, with rules tagged for "what corresponds to which risk." It's directly usable when reporting assessment results mapped to the Top 10.

5-2. A Typical Example SAST Catches — Before and After Fix

SAST sees "structural flaws." For example, the following Next.js Route Handler string-concatenates user input into SQL, and Semgrep warns with high confidence (A05 / WSTG 4.7).

// ❌ Bad: 入力を直接連結 → SQLインジェクション(Semgrepが検出)
// app/api/search/route.ts
import { sql } from "@/lib/db";

export async function GET(request: Request) {
  const q = new URL(request.url).searchParams.get("q") ?? "";
  // q = "'; DROP TABLE users; --" で破滅する
  const rows = await sql(`SELECT id, title FROM posts WHERE title LIKE '%${q}%'`);
  return Response.json(rows);
}
// ✅ Good: パラメータ化クエリ+境界でのZod検証(A05/A10を同時に塞ぐ)
// app/api/search/route.ts
import { z } from "zod";
import { sql } from "@/lib/db";

const SearchQuery = z.object({
  q: z.string().trim().min(1).max(100),
});

export async function GET(request: Request) {
  const parsed = SearchQuery.safeParse(
    Object.fromEntries(new URL(request.url).searchParams),
  );
  // 入力検証の失敗を「異常系」として明示的に処理(A10対策)
  if (!parsed.success) {
    return Response.json({ error: "invalid query" }, { status: 400 });
  }
  // プレースホルダで値を渡す=SQLとデータを分離(A05対策)
  const rows = await sql`SELECT id, title FROM posts WHERE title LIKE ${"%" + parsed.data.q + "%"}`;
  return Response.json(rows);
}

The point is to root-cure the SAST warning with the design of "parameterization + type validation at the boundary." Not ad-hoc escaping, but always validate / neutralize at the trust boundary (values coming from outside the system) — this is OWASP's consistent philosophy, and it lowers A05・A07・A10 together.

5-3. How to Deal with False Positives

SAST tends to over-detect. Suppress, with a reason comment attached, the spots you've judged definitely safe. Unbridled suppression is forbidden — if you can't explain "why it's safe" in one line, that's not suppression but un-addressed.

// nosemgrep: javascript.lang.security.audit.unsafe-formatstring
// 理由: `template` は環境変数由来の固定値で、ユーザー入力は混入しない(境界で検証済み)
const message = format(template, userName);

6. Hands-On ③ Secret Scanning — Key Leakage (A02/A03)

A misplaced NEXT_PUBLIC_, a committed .env, a hardcode during debugging — an API key or DB connection string leaking into the repository is a typical accident. This becomes the entrance to A02 (misconfiguration) and A03 (supply chain).

Prevent it in two stages.

  1. Detect a leak "from the history" — scan the whole history with Gitleaks (OSS).
  2. Stop a leak "before push" — enable GitHub Secret Scanning's Push Protection. Include a secret in a commit and the push is blocked.
# .github/workflows/secret-scan.yml
name: secret-scan
on: [push, pull_request]
jobs:
  gitleaks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # 全履歴を走査するため必須
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Root-cure by design: the proper path is a structure where a secret "can't be written" into code in the first place. Make a server-only secret touchable only by server components / Route Handlers via process.env, and separate it at the type level from values reaching the client (NEXT_PUBLIC_*). The design of validating the env boundary with Zod is covered in the separate article Design to prevent environment-variable leakage.


7. Hands-On ④ DAST — Diagnose a "Running App" from the Attacker's Viewpoint with ZAP (A01/A05/A02)

Here is assessment's flower, DAST (Dynamic Application Security Testing). Where SAST reads the source, DAST sends HTTP requests to an actually-running app and judges vulnerabilities by externally-visible behavior. It's a layer strong on reflected XSS・open redirect・SQLi (boolean/error inference)・missing security headers・SSRF.

The free standard is ZAP (Zed Attack Proxy). As noted, it's now under the Checkmarx regime but OSS / free is maintained. Using it with Docker is the most reproducible, with 3 kinds of scan used distinctly.

ScanScriptBehaviorMay you fire it at production?
Baselinezap-baseline.pyA default-1-minute spider + passive scan (doesn't attack)✅ Safe (CI/production OK)
Fullzap-full-scan.pyA full spider + active scan (actually attacks)❌ Test environment only
APIzap-api-scan.pyActively assesses APIs based on an OpenAPI/GraphQL definition❌ Test environment only

7-1. Baseline Scan — From "Passive" Assessment, Safe Even in Production

First, from the baseline that doesn't fire attacks. It sweeps, in a minute, problems knowable passively like missing security headers・mixed content・cookie attributes.

# 受動スキャン(攻撃リクエストを送らないので本番URLにも安全)
# HTMLとJSONのレポートを出力(カレントをマウント)
docker run --rm -v "$(pwd):/zap/wrk/:rw" \
  -t ghcr.io/zaproxy/zaproxy:stable \
  zap-baseline.py \
    -t https://staging.example.com \
    -r zap-baseline-report.html \
    -J zap-baseline-report.json \
    -m 3                 # スパイダーに3分かける

You can control CI by the exit code (this is the crux of CI integration).

Exit codeMeaningTreatment in CI
0No warnings (success)Pass
1Failure (a FAIL-level detection exists)Block
2Warnings onlyJudge per operation
3Other errorsReview the config

7-2. Full Scan — Active Assessment Only on "Your Own Test Environment"

Against local or staging, diagnose deeply with a full scan that actually sends attack payloads. Never fire it at production (§0).

# 能動スキャン:実際にSQLi/XSS等のペイロードを送る。テスト環境限定。
docker run --rm -v "$(pwd):/zap/wrk/:rw" \
  -t ghcr.io/zaproxy/zaproxy:stable \
  zap-full-scan.py \
    -t http://host.docker.internal:3000 \
    -r zap-full-report.html

7-3. API Scan — Cover APIs Comprehensively from an OpenAPI Definition

An API with no screen can't be traversed by a spider. Feed it an OpenAPI(Swagger)/GraphQL definition and assess the endpoints comprehensively.

# OpenAPI 定義をもとにAPIを能動診断
docker run --rm -v "$(pwd):/zap/wrk/:rw" \
  -t ghcr.io/zaproxy/zaproxy:stable \
  zap-api-scan.py \
    -t http://host.docker.internal:3000/api/openapi.json \
    -f openapi \
    -r zap-api-report.html

7-4. Automation Framework — A Complex Assessment Declaratively in One YAML

The baseline / full scripts are handy, but for an app needing authentication or when you want to finely control the scan procedure, use the Automation Framework that ZAP officially is switching to recommending. Because you write the scan plan declaratively in one zap.yaml, reproducibility, readability, and version-controllability are on a different level.

# zap.yaml — 環境定義 + ジョブ列をコードとして宣言(公式のジョブ型に準拠)
env:
  contexts:
    - name: "my-app"
      urls: ["http://host.docker.internal:3000"]
      includePaths: ["http://host.docker.internal:3000.*"]
      # 認証が要るアプリはここに認証コンテキストを定義(form/json/script等)
  parameters:
    failOnError: true
    progressToStdout: true

jobs:
  - type: passiveScan-config       # 受動スキャンの設定
    parameters: { maxAlertsPerRule: 10 }
  - type: spider                   # 従来型スパイダーでURLを収集
    parameters: { context: "my-app", maxDuration: 5 }
  - type: spiderAjax               # SPA(React)はAjaxスパイダーが必須
    parameters: { context: "my-app", maxDuration: 5 }
  - type: passiveScan-wait         # 受動スキャンの完了を待つ
  - type: activeScan               # 能動スキャン(テスト環境限定)
    parameters: { context: "my-app" }
  - type: report                   # SARIFで出力 → GitHub Code Scanningへ
    parameters:
      template: "sarif-json"
      reportDir: "/zap/wrk/"
      reportFile: "zap-report"
# Automation Framework を実行
docker run --rm -v "$(pwd):/zap/wrk/:rw" \
  -t ghcr.io/zaproxy/zaproxy:stable \
  zap.sh -cmd -autorun /zap/wrk/zap.yaml

A pitfall of SPA (React/Next.js) assessment: an app where client rendering is the main can't have its links traversed by the conventional spider, and coverage plummets. Always combine spiderAjax (the Ajax spider). Misjudging "ZAP finds nothing = safe" without knowing this is the most frequent mistake of self-taught DAST.


8. Hands-On ⑤ CI/CD Integration — Make the 4 Layers "Auto-Gates"

Bundle the 4 layers so far into one pipeline with GitHub Actions, and aggregate the results into GitHub Code Scanning as SARIF. With this, vulnerabilities are "auto-visualized/blocked on every PR," becoming an assessment gate of production-operation quality with observability・resilience・idempotency.

# .github/workflows/security.yml
name: security
on:
  pull_request:
  push:
    branches: [main]
  schedule:
    - cron: "0 18 * * *" # 毎晩(UTC)にDAST baselineも回す

# SARIFアップロードに必要な最小権限(最小権限の原則)
permissions:
  contents: read
  security-events: write

jobs:
  # --- 静的:速い・本番不要。PR毎に回す ---
  sca:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: "24" }
      - run: npm ci
      - run: npm audit --audit-level=moderate

  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Semgrep (SARIF)
        run: |
          npx semgrep scan \
            --config=p/owasp-top-ten --config=p/typescript --config=p/react \
            --sarif --output=semgrep.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: semgrep.sarif }

  secrets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: gitleaks/gitleaks-action@v2
        env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" }

  # --- 動的:遅い・要デプロイ。夜間とリリース前だけ回す ---
  dast-baseline:
    if: github.event_name == 'schedule'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.15.0
        with:
          target: "https://staging.example.com"
          # 誤検知を抑制するルールファイル(理由付きで管理)
          rules_file_name: ".zap/rules.tsv"
          # 警告だけでジョブを失敗させない運用も可(cmd_options: '-I')

The point is separating the frequency, "static = every PR, dynamic = nightly/pre-release." DAST is slow and needs a deploy, so it's heavy for every PR. Running the fast ones at high frequency and the slow ones at low frequency achieves both feedback speed and cost. The SAST/secret results aggregate into GitHub's "Security" tab via SARIF, becoming a mechanism that permanently prevents regression (the same hole mixed back in).

If you want to deep-dive CI integration on a SARIF premise, see the separate article Building security assessment into CI (SARIF × GitHub Actions).


9. The Limits of Auto-Assessment — What Tools "Fundamentally" Can't Find

By here, most of OWASP Top 10:2025's A02–A05・A08・A10 are auto-plugged. But — even if every scan is green, that's not "safe."

There are vulnerabilities automated tools fundamentally can't find. They are A01 (access control)'s core = authorization and A06/business logic (WSTG 4.5 / 4.10).

Why Machines Can't Judge It

Look at the following code. It's syntactically perfect, and neither SAST nor DAST warns. Yet it's a serious IDOR (Broken Object Level Authorization).

// ❌ 認可の欠落:ツールは「正しいコード」と判定するが、他人の請求書が見える
// app/api/invoices/[id]/route.ts
export async function GET(_req: Request, { params }: { params: { id: string } }) {
  // ログイン済みかは確認している…が、「その請求書が"自分のもの"か」を確認していない
  const invoice = await db.invoice.findUnique({ where: { id: params.id } });
  return Response.json(invoice); // 他人のIDを入れれば、他人の請求書が返る
}
// ✅ 所有権を必ず検証する(リソースを"誰が所有するか"はアプリ固有の事業ルール)
export async function GET(req: Request, { params }: { params: { id: string } }) {
  const userId = await requireUserId(req); // 認証
  const invoice = await db.invoice.findFirst({
    where: { id: params.id, ownerId: userId }, // 認可:所有権でフィルタ
  });
  if (!invoice) return Response.json({ error: "not found" }, { status: 404 });
  return Response.json(invoice);
}

The reason a tool can't warn is clear. The knowledge that "invoice should be filtered by userId" depends on the "meaning" of your business rules (who owns what, and which operations are allowed). A scanner doesn't know your data model, so it can't judge this omission as an "omission." This contrasts with SQL injection being "the same structural flaw in any app."

This object-level authorization flaw (IDOR/BOLA) has been 1st ever since the first edition of the OWASP API Security Top 10 too — not "a rare advanced attack" but "the most ordinary leak."

Auto-assessment can plug (horizontal holes)Auto-assessment can't plug (vertical risks)
Injection (A05), headers/config (A02)Authorization/IDOR (A01)
Known vulnerable dependencies (A03)Tenant isolation・data crossing
Secret mixing, crypto config (A04)Business-logic abuse (quantity・price・state transitions)
Known-pattern flawsPrivilege escalation・design validity

The detection of IDOR/authorization in Next.js × Supabase, and multi-layer defense with RLS (row-level security), is concretely covered in the separate article Detecting IDOR・authorization flaws (doubling up with RLS).


10. Operational Design — What to Run, in Which Scene, at What Frequency

Once you know "the how-to," finally it's important to land it into a sustainable operation. A one-off assessment goes stale with next week's deploy. I recommend the following frequency design.

TimingWhat to runPurpose
Before commit/pushSecret scanning (Push Protection)Physically stop a secret leak
Every PRSCA・SASTImmediately block a new hole's mixing-in (regression prevention)
NightlyDAST baseline (staging)Detect config drift daily with passive assessment
Pre-releaseDAST full / API scanConfirm deeply with active assessment
Quarterly / before a major releaseManual authorization review・auditVertical risk (IDOR・business logic)
On dependency updatesRe-run SCA + DependabotFollow the supply chain

The way to think about cost: in the table above, everything down to the 5th row can be internalized with free tools. What you should pay for is the bottom row, only the audit of vertical risk that machines can't judge. Do everything by hand and cost balloons; leave everything to tools and the most serious authorization slips through. "Sweep horizontal for free → only vertical paid" is the cheapest, safest budget allocation.

Signs You Should Commission a Professional's Audit

If the following apply, it's the juncture to consider a manual audit of vertical risk while basing it on auto-assessment.

  • Enterprise / RFP・large contract, where you need to show satisfaction of security requirements (ASVS L2-equivalent)
  • SOC2 / ISO 27001 / Privacy Mark and other compliance response
  • Fundraising due diligence where the security posture is questioned
  • Post-incident, where a systematic surfacing of the cause and recurrence prevention is needed
  • Right before a release just after generating a lot of code with AI (authorization・business-logic gaps tend to mix in)

Summary — Maximize "the Range You Can Do Yourself," and Ask Humans Only for What's Beyond

Let me trace this article's map once more.

  1. Confirming permission is top priority. Only an asset you own or a target with written permission. Don't fire an active scan (DAST full) at production.
  2. Fix the official 3 pillars at their latest versions: what to look for = Top 10:2025, how to test = WSTG v4.2, how far to satisfy = ASVS 5.0.
  3. Auto-assessment is 4 layers: SCA (A03) → secret scanningSAST (A05) → DAST/ZAP (A01/A05/A02). To CI in the fast order.
  4. Gate it with CI/SARIF. Static = every PR, dynamic = nightly/pre-release. Permanently prevent regression.
  5. Be honest about the tools' limits. Authorization/IDOR・tenant isolation・business logic can't be judged by machines. Here is the audit realm.

Vulnerability assessment isn't "something to dump wholesale on a professional." Horizontal holes can be swept by the developer themselves with free official tools. On top of that, use money and human time only on the vertical risk machines can't judge — this is the most rational way to fight, securing production-quality security on a limited budget.

I myself have designed and implemented a B2B SaaS that won the Minister of Economy, Trade and Industry Award, and a payment foundation that achieved 0 double charges in production, with one person × generative AI. I've landed the workflow of "defend fast and cheap with automation, keep the design judgments in human hands," solidified in that process, into all the code in this article. First, from the single command npx semgrep scan --config=auto, try diagnosing your app today.

友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

The measures in this article can be automated with a tool

Automate your Next.js / Supabase security controls with the OSS Aegis

Many of the measures here can be mechanically detected and hardened with a single middleware file and static analysis. With the free, MIT-licensed Aegis, you can scan your current project from one command. The vertical risks that need design, I also take on as an audit.

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

Also worth reading