Skip to content

๐Ÿ”ด[P0] fix(api): admin upload file-serve route has no authentication checkย #65

Description

@teddylee777

์ปจํ…์ŠคํŠธ ๋ธ”๋ก

Key Value
Category api
Checklist ISS-API-06 โ€” Admin API routes missing role-check middleware
Priority P0 ๐Ÿ”ด
Scan Date 2026-04-16
Flagged By @api-explorer

์š”์•ฝ

  • WHAT: GET /api/admin/upload/[filename] ๋ผ์šฐํŠธ์— ์ธ์ฆ ๊ฒ€์‚ฌ๊ฐ€ ์ „ํ˜€ ์—†์–ด, ํŒŒ์ผ๋ช…๋งŒ ์•Œ๋ฉด ๋ˆ„๊ตฌ๋‚˜(๋กœ๊ทธ์ธ ์—†์ด๋„) ์—…๋กœ๋“œ๋œ admin ๋ธŒ๋žœ๋”ฉ/๋กœ๊ณ  ์ž์‚ฐ์„ ์ง์ ‘ ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ
  • WHY: ๋‹ค๋ฅธ ๋ชจ๋“  /api/admin/** ํ•ธ๋“ค๋Ÿฌ๋Š” auth() + isAdmin() ๊ฐ€๋“œ๋ฅผ ๊ฑฐ์น˜์ง€๋งŒ ์ด ํŒŒ์ผ ์„œ๋ธŒ ๋ผ์šฐํŠธ๋งŒ ๋ฌด๋ฐฉ๋น„ โ€” ๊ด€๋ฆฌ์ž์šฉ ๋น„๊ณต๊ฐœ ํŒŒ์ผ(๋กœ๊ณ  ์ดˆ์•ˆ, ๋‚ด๋ถ€ ์ด๋ฏธ์ง€)์ด ์ธํ„ฐ๋„ท์— ๋…ธ์ถœ๋จ
  • WHERE: frontend/src/app/api/admin/upload/[filename]/route.ts:24 โ€” GET ํ•ธ๋“ค๋Ÿฌ ๋ณธ๋ฌธ
  • SEVERITY: CRITICAL โ€” ์ธ์ฆ ์™„์ „ ๋ถ€์žฌ + admin ๋„๋ฉ”์ธ ๋ฆฌ์†Œ์Šค ๊ณต๊ฐœ ์ ‘๊ทผ

Evidence

# File Line Finding Flagged By Confidence
1 frontend/src/app/api/admin/upload/[filename]/route.ts 24 GET ํ•ธ๋“ค๋Ÿฌ์— auth() ํ˜ธ์ถœ ์—†์Œ, ํŒŒ์ผ ์‹œ์Šคํ…œ์—์„œ ์ฆ‰์‹œ ์ฝ์–ด์„œ ๋ฐ˜ํ™˜ @api-explorer High
2 frontend/src/app/api/admin/upload/route.ts 35 POST(์—…๋กœ๋“œ)๋Š” isAdmin ์ฒดํฌ ์กด์žฌ โ€” ์—…๋กœ๋“œ๋Š” ๋ณดํ˜ธ, ๋‹ค์šด๋กœ๋“œ๋Š” ๋ฏธ๋ณดํ˜ธ (๋น„๋Œ€์นญ) @api-explorer High

์˜ํ–ฅ ๋ถ„์„

์˜ํ–ฅ ๋ฒ”์œ„

  • ๋ชจ๋“  /api/admin/upload/* ๊ฒฝ๋กœ๋กœ ์ €์žฅ๋œ ์ž์‚ฐ (๋กœ๊ณ , ๋ธŒ๋žœ๋”ฉ ์ด๋ฏธ์ง€, admin-์ „์šฉ ์•„ํŠธ์›Œํฌ)
  • ํƒ€์ž„์Šคํƒฌํ”„ ๊ธฐ๋ฐ˜ ํŒŒ์ผ๋ช…์€ ์ถ”์ธก ๊ฐ€๋Šฅ ({timestamp}-{original}.ext ํŒจํ„ด)
  • ๋ธŒ๋ฃจํŠธํฌ์Šค ๋˜๋Š” ์™ธ๋ถ€ ๋ ˆํผ๋Ÿฌ ๋…ธ์ถœ(๋ธŒ๋ผ์šฐ์ € ์บ์‹œ, ๋กœ๊ทธ ํŒŒ์ผ)๋กœ ํŒŒ์ผ๋ช…์ด ์œ ์ถœ๋˜๋ฉด ์ฆ‰์‹œ ์™ธ๋ถ€ ์ ‘๊ทผ ๊ฐ€๋Šฅ

์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค

  1. ๊ด€๋ฆฌ์ž๊ฐ€ admin ์ฝ˜์†”์—์„œ ๋ฏธ๊ณต๊ฐœ ๋กœ๊ณ  ์‹œ์•ˆ์„ ์—…๋กœ๋“œ (POST /api/admin/upload โ†’ ํŒŒ์ผ๋ช… ๋ฐ˜ํ™˜)
  2. ๋ฐ˜ํ™˜๋œ ํŒŒ์ผ๋ช…์ด ์–ด๋–ค ๊ฒฝ๋กœ(๋ธŒ๋ผ์šฐ์ € ํžˆ์Šคํ† ๋ฆฌ, ์„œ๋ฒ„ ๋กœ๊ทธ, CDN ์บ์‹œ)๋กœ๋“  ์™ธ๋ถ€์— ์œ ์ถœ
  3. ๋น„๋กœ๊ทธ์ธ ๊ณต๊ฒฉ์ž๊ฐ€ GET /api/admin/upload/{filename}๋กœ ๋ฐ”๋กœ ๋‹ค์šด๋กœ๋“œ ์„ฑ๊ณต
  4. ๋น„๊ณต๊ฐœ ์ž์‚ฐ ์œ ์ถœ, ๊ฐ์‚ฌ ๋กœ๊ทธ ๋ฏธ๊ธฐ๋ก โ†’ ํƒ์ง€ ์–ด๋ ค์›€

๊ธด๊ธ‰๋„

์ œ์•ˆ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

์ ‘๊ทผ ๋ฐฉ๋ฒ•

frontend/src/app/api/admin/upload/[filename]/route.ts:24 GET ํ•ธ๋“ค๋Ÿฌ ์„ ๋‘์— admin ๊ฐ€๋“œ ์‚ฝ์ž…:

import { auth } from \"@/lib/auth\";
import { isAdmin } from \"@/lib/auth/is-admin\";

export async function GET(req: NextRequest, { params }: { params: { filename: string } }) {
  const session = await auth();
  if (!session?.user || !isAdmin(session.user)) {
    return NextResponse.json({ error: \"Unauthorized\" }, { status: 401 });
  }
  // ... existing file serve logic
}

์ถ”๊ฐ€๋กœ filename์— ๋Œ€ํ•œ path-traversal ๋ฐฉ์–ด (์ด๋ฏธ sanitize ๋˜์–ด ์žˆ๋Š”์ง€ ๊ฒ€์ฆ โ€” .. / ์ ˆ๋Œ€๊ฒฝ๋กœ ๊ฑฐ๋ถ€).

๋Œ€์•ˆ

  • ๊ณต๊ฐœ ํ—ˆ์šฉ + obfuscated ํŒŒ์ผ๋ช…: UUID v4 ๊ธฐ๋ฐ˜ ๋žœ๋ค ํŒŒ์ผ๋ช…์œผ๋กœ ์ถ”์ธก ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค๊ณ„ โ†’ ๋ณด์•ˆ by obscurity๋กœ ์•ˆํ‹ฐํŒจํ„ด, ์ธ์ฆ ์—†์Œ์„ ์ •๋‹นํ™”ํ•˜์ง€ ์•Š์Œ
  • Next.js middleware์—์„œ ์ผ๊ด„ ์ฒ˜๋ฆฌ: /api/admin/* ์ „์ฒด ๋งค์ฒ˜๋กœ middleware์—์„œ ์ธ์ฆ ๊ฐ•์ œ โ†’ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ธฐ์กด ํ•ธ๋“ค๋Ÿฌ๋“ค์ด ์ด๋ฏธ ํ•ธ๋“ค๋Ÿฌ ๋ ˆ๋ฒจ์—์„œ ๊ฐ€๋“œํ•˜๋ฏ€๋กœ ์ผ๊ด€์„ฑ ์œ„ํ•ด ํ•ธ๋“ค๋Ÿฌ ๋ ˆ๋ฒจ ์œ ์ง€ ๊ถŒ์žฅ

์ˆ˜์šฉ ๊ธฐ์ค€

  • ๋น„๋กœ๊ทธ์ธ ์ƒํƒœ๋กœ GET /api/admin/upload/any-file.png ํ˜ธ์ถœ ์‹œ 401 ์‘๋‹ต
  • ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž(role=user) ๋กœ๊ทธ์ธ ์ƒํƒœ๋กœ ํ˜ธ์ถœ ์‹œ 403 ์‘๋‹ต
  • admin ๋กœ๊ทธ์ธ ์ƒํƒœ์—์„œ๋งŒ 200 + ํŒŒ์ผ ๋ฐ˜ํ™˜
  • ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€: frontend/e2e/ ๋˜๋Š” frontend/src/app/api/admin/upload/[filename]/route.test.ts์—์„œ 3๊ฐ€์ง€ ์ผ€์ด์Šค ๊ฒ€์ฆ
  • ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ปค๋งจ๋“œ: cd frontend && pnpm test ํ†ต๊ณผ

์ฐธ์กฐ

์žฌํ˜„ ๋ฐฉ๋ฒ•

์‚ฌ์ „ ์กฐ๊ฑด

  • dev ์„œ๋ฒ„ ์‹คํ–‰ ์ค‘ (cd frontend && pnpm dev)
  • admin ์ฝ˜์†”์—์„œ ํŒŒ์ผ 1๊ฐœ ์—…๋กœ๋“œ (๋ฐ˜ํ™˜๋œ filename ๊ธฐ๋ก)

๋‹จ๊ณ„

  1. ๋ธŒ๋ผ์šฐ์ € private ์ฐฝ ์—ด๊ธฐ (์ธ์ฆ ์ฟ ํ‚ค ์ œ๊ฑฐ)
  2. http://localhost:3000/api/admin/upload/{filename} ์ ‘์†
  3. ์‘๋‹ต ํ™•์ธ

๊ธฐ๋Œ€ ๊ฒฐ๊ณผ

401 Unauthorized

์‹ค์ œ ๊ฒฐ๊ณผ

200 OK + ํŒŒ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ˜ํ™˜

๊ด€๋ จ ์ฝ”๋“œ ์ปจํ…์ŠคํŠธ

File Role Relevance
frontend/src/app/api/admin/upload/[filename]/route.ts admin ์—…๋กœ๋“œ ํŒŒ์ผ ์„œ๋ธŒ ์—”๋“œํฌ์ธํŠธ ์ˆ˜์ • ๋Œ€์ƒ
frontend/src/lib/auth/require-auth.ts ์ธ์ฆ ํ—ฌํผ ์žฌ์‚ฌ์šฉ
frontend/src/app/api/admin/users/route.ts ๋™์ผ ๋„๋ฉ”์ธ ์ฐธ์กฐ ํ•ธ๋“ค๋Ÿฌ auth()+isAdmin() ํŒจํ„ด ์ฐธ๊ณ 

Detected by oh-my-braincrew `omb:issue` scan
Category: api | Scan date: 2026-04-16
`omb-issue-scan category=api checklist=ISS-API-06`

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions