Supabase DB둜 엔진도 κ΅μ²΄ν–ˆκ³ , μ΄ˆμ„± 검색에 ISR μΊμ‹±κΉŒμ§€ λ‹¬μ•„λ†“μœΌλ‹ˆ μ‚¬μ΄νŠΈλŠ” κ·Έμ•Όλ§λ‘œ λ‚ μ•„λ‹€λ…”μŠ΅λ‹ˆλ‹€. πŸš€ "μ•„, λ‚˜ μ’€ 개발 μž˜ν•˜λŠ”μ§€λ„?"라며 λ””μžμ΄λ„ˆμ˜ μžμ•„μ™€ 개발자의 μžμ•„κ°€ λ™μ‹œμ— μƒ΄νŽ˜μΈμ„ ν„°λœ¨λ¦¬λ˜ 5μ›” μ€‘μˆœμ˜ μ–΄λŠ λ‚ , 이메일 μ•Œλ¦Ό ν•˜λ‚˜κ°€ μŠ€λ§ˆνŠΈν°μ„ κ²©λ ¬ν•˜κ²Œ μšΈλ ΈμŠ΅λ‹ˆλ‹€. πŸ“±

[Vercel Alert] Your account has been suspended due to bandwidth usage... πŸ›‘

정신이 번쩍 λ“€μ—ˆμŠ΅λ‹ˆλ‹€. 메일함을 μ—΄μ–΄λ³΄λ‹ˆ ν•˜λ£¨ λ§Œμ— public/logos 디렉터리에 있던 μžμ‚°λ“€μ΄ μ™ΈλΆ€μ—μ„œ λ¬΄μ°¨λ³„μ μœΌλ‘œ 핫링크(Hotlinking) 및 슀크랩(Scraping)을 λ‹Ήν•΄ μ•½ 130GB의 λŒ€μ—­ν­μ„ μ†Œλͺ¨ν–ˆλ‹€λŠ” κ²λ‹ˆλ‹€! 🀯 κ·Έ κ²°κ³Ό Vercel νŒ€ 계정이 μΌμ‹œ μ€‘λ‹¨λ˜λŠ” 초유의 μ‚¬νƒœκ°€ λ²Œμ–΄μ‘ŒμŠ΅λ‹ˆλ‹€.

ν‰ν™”λ‘­λ˜ μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈμ— μ°Ύμ•„μ˜¨ κ±°λŒ€ν•œ λ³΄μ•ˆ μœ„κΈ°. μ €λŠ” μ¦‰μ‹œ νŒ”μ„ 걷어뢙이고 인프라 방어선을 κ΅¬μΆ•ν•˜κΈ° μœ„ν•΄ μ½”λ“œλ₯Ό 뜯기 μ‹œμž‘ν–ˆμŠ΅λ‹ˆλ‹€. πŸ› οΈ

1. 1단계 λ°©μ–΄: λΆˆν•„μš”ν•œ μžμ‚° λ‹€μ΄μ–΄νŠΈ 🧹

μœ„κΈ°λ₯Ό λ§ˆμ£Όν•˜κ³  κ°€μž₯ λ¨Όμ € ν•œ 일은 μ μ§„μ˜ νƒ€κ²Ÿμ΄ 된 μ†ŒμŠ€(Source) 자체λ₯Ό μ •λΉ„ν•˜λŠ” κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€.

public/logos/ 디렉터리λ₯Ό μ—΄μ–΄λ³΄λ‹ˆ, μ˜ˆμ „μ— ν”„λ‘œν† νƒ€μž…μ„ λ§Œλ“€ λ•Œ μž„μ‹œλ‘œ λ„£μ–΄λ‘” placeholder μžμ‚°(mcst.svg)μ΄λ‚˜ 슀크래치 파일(mcst-partial.txt) λ“± μ‹€μ œ ν”„λ‘œλ•μ…˜κ³Ό 아무 관계 μ—†λŠ” 유령 μžμ‚°λ“€μ΄ λ‹€μˆ˜ λ°©μΉ˜λ˜μ–΄ μžˆλ”κ΅°μš”. πŸ‘» μ™ΈλΆ€ μŠ€ν¬λž©λ΄‡λ“€μ€ 이런 ν‹ˆμƒˆλ₯Ό κΈ°λ§‰νžˆκ²Œ μ°Ύμ•„λ‚΄ λΉ¨λŒ€λ₯Ό κ½‚μ•˜λ˜ κ²ƒμž…λ‹ˆλ‹€.

μ°Έμ‘°λ˜μ§€ μ•ŠλŠ” 63개, μ•½ 26MB λΆ„λŸ‰μ˜ μžμ‚°μ„ μΌκ΄„μ μœΌλ‘œ 영ꡬ μ‚­μ œν–ˆμŠ΅λ‹ˆλ‹€. 그리고 μ‹€μ œ ν”„λ‘œλ•μ…˜ λ°μ΄ν„°λŠ” λͺ¨λ‘ Supabase에 μ•ˆμ „ν•˜κ²Œ λ³΄κ΄€λ˜μ–΄ μžˆμœΌλ―€λ‘œ, public/logos/ μ•ˆμ„ κΉ”λ”ν•˜κ²Œ λΉ„μ›Œλ‘μ–΄ 곡격 ν‘œλ©΄(Attack Surface) 자체λ₯Ό μ΅œμ†Œν™”ν–ˆμŠ΅λ‹ˆλ‹€.

2. 2단계 λ°©μ–΄: 미듀웨어(Middleware)둜 λΆˆλ²• 핫링크 차단 πŸ›‘

μžμ‚°μ„ μ€„μ˜€μœΌλ‹ˆ 이제 λ“€μ–΄μ˜€λŠ” κΈΈλͺ©μ„ 막아야 ν–ˆμŠ΅λ‹ˆλ‹€. μ™ΈλΆ€ μ‚¬μ΄νŠΈλ‚˜ 봇이 우리 둜고 μžμ‚° μ£Όμ†Œλ₯Ό κ·ΈλŒ€λ‘œ κ°€μ Έλ‹€ μ“°λŠ” λΆˆλ²• 핫링크λ₯Ό 막기 μœ„ν•΄, Next.js의 미듀웨어(Middleware) λ ˆμ΄μ–΄μ— λ‹€μΈ΅ 방어선을 κΉ”μ•˜μŠ΅λ‹ˆλ‹€.

[ μ™ΈλΆ€ μš”μ²­ ] ──> Referer 검증 (미듀웨어) ──> ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ 도메인인가?
                                            β”œβ”€β”€> YES: 정상 λ Œλ”λ§ β­•
                                            └──> NO : 403 Forbidden 차단! ❌

μš”μ²­μ΄ λ“€μ–΄μ˜¬ λ•Œ ν—€λ”μ˜ Referer(μ–΄λ””μ„œ 이 링크λ₯Ό 타고 μ™”λŠ”μ§€) 도메인을 ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ λ°©μ‹μœΌλ‘œ κ²€μ¦ν•˜λŠ” λ‘œμ§μ„ μ‹¬μ—ˆμŠ΅λ‹ˆλ‹€. 우리 도메인이 μ•„λ‹Œ λ‚―μ„  off-domainμ—μ„œ 우리 둜고λ₯Ό 이미지 νƒœκ·Έλ‘œ 무단 링크 κ±Έμ–΄ λŒ€μ—­ν­μ„ λΉ¨μ•„λ¨ΉμœΌλ €κ³  ν•˜λ©΄, 미듀웨어가 칼같이 ν†΅κ³Όμ‹œν‚€μ§€ μ•Šκ³  403 Forbidden μ—λŸ¬λ₯Ό λ±‰μœΌλ©° λ¬Έμ „λ°•λŒ€ν•˜λ„λ‘ λ§Œλ“€μ—ˆμ£ ! πŸ™…β€β™‚οΈ

단, μœ μ €λ“€μ˜ 정상적인 κ³΅μœ λ‚˜ Slack/Discord 같은 λ©”μ‹ μ €μ—μ„œμ˜ 링크 프리뷰, 그리고 *.vercel.app 같은 개발 프리뷰 ν™˜κ²½μ€ 정상 μž‘λ™ν•˜λ„λ‘ μ˜ˆμ™Έ 처리λ₯Ό μ΄˜μ΄˜ν•˜κ²Œ κ³λ“€μ˜€μŠ΅λ‹ˆλ‹€.

3. 3단계 λ°©μ–΄: Config 헀더 μ„€μ •κ³Ό ꡬ글 봇 μ§„μž… ν†΅μ œ πŸ”’

μ—¬κΈ°μ„œ 끝내면 μ™„λ²½ν•œ λ°©μ–΄κ°€ μ•„λ‹ˆμ£ . 더 ν™•μ‹€ν•˜κ²Œ μžμ‚°μ„ 묢어두기 μœ„ν•΄ next.config.ts와 검색엔진 μ„€μ •κΉŒμ§€ 손을 λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

  • λΈŒλΌμš°μ € λ‘±μΊμ‹œ 및 μžμ‚° 보호 헀더 μΆ”κ°€: /logos/* ν•˜μœ„ 경둜둜 λ“€μ–΄μ˜€λŠ” μžμ‚°μ— λŒ€ν•΄ λΈŒλΌμš°μ €κ°€ μ˜€λž«λ™μ•ˆ 캐싱(long-cache)ν•˜λ„λ‘ μ„€μ •ν•΄ λΆˆν•„μš”ν•œ μž¬μš”μ²­ λŒ€μ—­ν­μ„ μ€„μ˜€μŠ΅λ‹ˆλ‹€. 여기에 Cross-Origin-Resource-Policy: same-site와 X-Content-Type-Options 헀더λ₯Ό κ°•μ œν•˜μ—¬ 였직 ν—ˆμš©λœ μ‚¬μ΄νŠΈ(same-site)μ—μ„œλ§Œ μžμ‚°μ„ μ•ˆμ „ν•˜κ²Œ μ†ŒλΉ„ν•  수 μžˆλ„λ‘ 인프라 μˆ˜μ€€μ—μ„œ 락을 κ±Έμ—ˆμŠ΅λ‹ˆλ‹€.
  • robots.txt λ””μŠ€μ–Όλ‘œμš°: κ΅¬κΈ€μ΄λ‚˜ 넀이버 봇이 λ¬΄μ§€μ„±μœΌλ‘œ 둜고 이미지 μžμ‚° 자체λ₯Ό μΈλ±μ‹±ν•˜λŠλΌ 크둀링 λŒ€μ—­ν­μ„ λ‚­λΉ„ν•˜μ§€ μ•Šλ„λ‘, robots.ts 섀정에 /logos/ 경둜λ₯Ό 과감히 λ””μŠ€μ–Όλ‘œμš°(Disallow) μ²˜λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.

4. κ°’μ§„ κ΅ν›ˆ: 미듀웨어 'μ‘°μš©ν•œ μ‹€νŒ¨'의 곡포 πŸ‘»

이 λŒ€μ—­ν­ 폭탄 사건을 ν•΄κ²°ν•˜λ©΄μ„œ, μ €λŠ” μ•„μ£Ό 식은땀 λ‚˜λŠ” 개발적 κ΅ν›ˆμ„ ν•˜λ‚˜ μ–»μ—ˆμŠ΅λ‹ˆλ‹€.

사싀 이전에 μ–΄λ“œλ―Ό λ³΄μ•ˆμ„ κ°•ν™”ν•˜λŠ” κ³Όμ •μ—μ„œ 미듀웨어 νŒŒμΌμ„ λ§Œλ“€μ–΄ λ‘μ—ˆμ—ˆλŠ”λ°, μ‹€μˆ˜λ‘œ 파일λͺ…을 proxy.ts라고 잘λͺ» λ§Œλ“œλŠ” λ°”λžŒμ— Next.jsκ°€ 미듀웨어λ₯Ό μ•„μ˜ˆ μΈμ‹ν•˜μ§€ λͺ»ν•˜κ³  μžˆμ—ˆμŠ΅λ‹ˆλ‹€! πŸ€¦β€β™‚οΈ (Next.jsλŠ” 였직 root의 middleware.ts μ»¨λ²€μ…˜λ§Œ μΈμ‹ν•˜κΈ° λ•Œλ¬Έμ΄μ£ .)

λΉŒλ“œλŠ” 아무 μ—λŸ¬ 없이 μ™„λ²½ν•˜κ²Œ 톡과(Green Light)ν•˜λŠ”λ°, μ •μž‘ μ‹€μ œ μ„œλ²„μ—μ„œλŠ” 인증 κ°€λ“œμ™€ λ³΄μ•ˆ 미듀웨어가 ν†΅μ§Έλ‘œ λΉ„ν™œμ„±ν™”λ˜μ–΄ μž‘λ™ν•˜μ§€ μ•ŠλŠ” 'μ‘°μš©ν•œ μ‹€νŒ¨(Silent Failure)' μƒνƒœμ˜€λ˜ κ²ƒμž…λ‹ˆλ‹€.

파일λͺ… 버그λ₯Ό middleware.ts둜 μ •ν™•ν•˜κ²Œ μˆ˜μ •ν•˜κ³  λ‚˜μ„œμ•Ό λͺ¨λ“  방어선이 μ •μƒμ μœΌλ‘œ μž‘λ™ν•˜κΈ° μ‹œμž‘ν–ˆμŠ΅λ‹ˆλ‹€. λ³΄μ•ˆκ³Ό 인프라 섀정에 μžˆμ–΄μ„œ "μ»¨λ²€μ…˜ 파일λͺ… ν•˜λ‚˜κ°€ μ„œλΉ„μŠ€ 전체λ₯Ό λ§ˆλΉ„μ‹œν‚¬ μˆ˜λ„ μžˆλ‹€"λŠ” λ¬΄μ‹œλ¬΄μ‹œν•œ 사싀을 온λͺΈμœΌλ‘œ 배운 κ°’μ§„ μ„±μž₯의 μˆœκ°„μ΄μ—ˆμŠ΅λ‹ˆλ‹€. πŸ“

올리브의 ν•œ 쀄 μš”μ•½ πŸ’‘

"λ””μžμΈ μ—”μ§€λ‹ˆμ–΄μ˜ 역할은 단지 λΈŒλΌμš°μ €μ— 픽셀을 예쁘게 λΏŒλ¦¬λŠ” λ°μ„œ λλ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ‚΄κ°€ 뿌린 ν”½μ…€(μžμ‚°)이 μ™ΈλΆ€μ˜ μœ„ν˜‘μœΌλ‘œλΆ€ν„° μ•ˆμ „ν•˜κ²Œ λ³΄ν˜Έλ°›κ³  μžˆλŠ”μ§€, μΈν”„λΌμ˜ λΉ„μš© 폭탄을 막아낼 λ‹€μΈ΅ 방어선을 ꡬ좕할 수 μžˆλŠ”κ°€κΉŒμ§€ μ±…μž„μ§€λŠ” 것이 μ§„μ •ν•œ λ©”μ΄μ»€μ˜ μžμ„Έμž…λ‹ˆλ‹€."

μ΄μ–΄μ§€λŠ” [5편(μ΅œμ’…ν™”)]μ—μ„œλŠ”... 이 λͺ¨λ“  ν—˜λ‚œν•œ 여정을 거쳐 μ™„μ„±λœ '둜고착착'의 μ΅œμ’… 운영 편의 κΈ°λŠ₯(ν…”λ ˆκ·Έλž¨ μ•Œλ¦Ό, λ§ˆν€΄ λ°°λ„ˆ)κ³Ό ν•¨κ»˜, ν’€μŠ€νƒ λ””μžμ΄λ„ˆλ‘œμ„œ ν•œ 단계 μ™„μ „νžˆ μ§„ν™”ν•œ 올리브의 사후 회고 및 μ΄ν‰μœΌλ‘œ 전체 μ‹œλ¦¬μ¦ˆλ₯Ό λ©‹μ§€κ²Œ λ§ˆλ¬΄λ¦¬ν•˜κ² μŠ΅λ‹ˆλ‹€! 🏁