security

47 posts
비밀번호를 안전하게 저장하고 검증하기

비밀번호를 안전하게 저장하고 검증하기

회원 가입 API를 만들다 보면 데이터베이스의 비밀번호 열을 어떻게 채워야 할지 고민하게 됩니다. 평문을 그대로 넣으면 안 된다는 사실은 알겠는데, AES로 암호화해야 할까요? 아니면 SHA-256으로 해시하면 충분할까요? 둘 다 안전한 답은 아닙니다. 비밀번호 저장은 일반 데이터 암호화나 파일 무결성 검사와 다른 위협을 상대하기 때문인데요. 공격자가 데이터베이스를 통째로 가져간 뒤에도 사용자의 원래 비밀번호를 알아내기 어렵게 만들어야 합니다. 이번 글에서는 비밀번호를 저장하고 검증하는 기본 흐름부터 우리에게 익숙한 범용 해시 알고

CIMD(Client ID Metadata Documents)

CIMD(Client ID Metadata Documents)

OAuth로 로그인을 붙일 때, 클라이언트 등록은 보통 이렇게 시작합니다. 인가 서버 콘솔에 앱을 미리 등록하고 client_id를 발급받아 코드에 박아 두는 거죠. 클라이언트와 서버가 서로를 아는 고정된 관계라 이 방식으로 충분했습니다. 그런데 이 전제가 통하지 않는 세계가 있습니다. 클라이언트와 서버가 처음 만나는 게 일상인 개방형 생태계예요. "Bluesky로 로그인"하는 서드파티 앱이 사용자마다 다른 서버에 붙고, AI 에이전트가 그날 처음 발견한 도구 서버에 연결하는 식이죠. 미리 등록할 시간도, 모두를 아우르는 중앙 등록

SSRF(Server-Side Request Forgery) 공격과 방어

SSRF(Server-Side Request Forgery) 공격과 방어

요즘 웹 서비스에는 서버가 사용자 대신 외부 URL로 요청을 보내는 기능이 흔합니다. 링크를 붙여넣으면 미리보기 카드를 만들어 주고, 이미지 주소를 넣으면 썸네일을 대신 받아오고, 웹훅 주소를 등록해 두면 이벤트가 생길 때마다 서버가 그 주소로 알림을 쏴 줍니다. 편리하죠. 그런데 "사용자가 건넨 주소로 서버가 대신 요청을 보낸다"는 이 평범한 동작이, 잘못 다루면 서버를 통째로 공격자의 손발로 내주는 통로가 됩니다. 이번 글에서 다룰 SSRF(Server-Side Request Forgery) 이야기입니다. 이름부터 CSRF와 헷

Cloudflare Access 신원 공급자(IdP) 연동 제대로 이해하기

Cloudflare Access 신원 공급자(IdP) 연동 제대로 이해하기

Cloudflare Access로 내부 대시보드를 막아두고 나면, 자연스럽게 다음 질문이 따라옵니다. "그래서 로그인은 대체 누가 처리하는 거지?" Access 설정 화면 어디에도 비밀번호를 저장하는 곳은 없고, 사용자 목록을 직접 만드는 메뉴도 보이지 않거든요. 그런데도 @mycompany.com 직원만 통과시키고, 인턴 그룹은 제외하고, MFA를 강제하는 정책이 멀쩡히 돌아갑니다 🤔 비밀은 Access가 인증을 직접 하지 않는다는 데 있습니다. 대신 외부 신원 공급자(Identity Provider, 이하 IdP)에게 "이 사

DNS 리바인딩(DNS Rebinding) 공격과 방어

DNS 리바인딩(DNS Rebinding) 공격과 방어

집에 있는 공유기 관리자 페이지(192.168.0.1)에 로그인해 본 적 있으시죠? 보통은 같은 와이파이에 연결된 기기에서만 접근할 수 있으니까 비밀번호를 admin/admin으로 그대로 두는 분들도 적지 않을 텐데요. 그런데 만약 인터넷 어딘가의 평범해 보이는 광고 배너 한 번 클릭한 것만으로 그 공유기에 누군가 마음대로 들어올 수 있다면 어떨까요? 😱 말도 안 되는 소리 같지만 실제로 이런 일을 가능하게 만드는 공격 기법이 있습니다. 바로 DNS 리바인딩(DNS Rebinding)인데요. 동일 출처 정책이 있으니까 외부 사이트

Let's Encrypt: 무료 인증서가 HTTPS의 문턱을 어떻게 낮췄나

Let's Encrypt: 무료 인증서가 HTTPS의 문턱을 어떻게 낮췄나

HTTPS를 처음 켜본 사람이라면 한 번쯤 같은 장면을 거칩니다. 인증서를 사려고 검색하다 보니 1년에 몇 만 원짜리부터 수십만 원짜리까지 등급이 나뉘어 있고, 어떤 걸 골라야 할지부터 막막한데요. 사이드 프로젝트 하나 띄워보자고 비싼 인증서를 사기는 부담스럽지만, HTTP로 두자니 브라우저가 "안전하지 않음"이라고 경고를 띄웁니다. TLS 인증서의 구조와 신뢰 체인이 궁금하다면 인증서 자체를 다룬 글에서 먼저 살펴볼 수 있습니다. 이 진입 장벽을 거의 무너뜨린 것이 Let's Encrypt입니다. ISRG(Internet Secu

Web Crypto API로 브라우저에서 암호화 다루기

Web Crypto API로 브라우저에서 암호화 다루기

자바스크립트로 무언가를 만들다 보면 의외로 자주 마주치는 순간이 있습니다. 세션 토큰을 만들거나, 비밀번호를 해시하거나, 파일 무결성을 검증하거나, 쿠키에 서명을 넣어야 할 때인데요. 예전 같으면 별도의 라이브러리를 깔아야 했지만, 요즘은 브라우저와 서버 런타임 모두 이런 일을 표준 API로 해낼 수 있습니다. 바로 Web Crypto API입니다. 이 글에서 Web Crypto API의 사용법을 먼저 다루지만, 그 뒤에 깔린 대칭키와 비대칭키 암호화의 원리가 궁금하다면 별도 글에서 따로 풀어 두었습니다. 이름만 들으면 브라우저 전

MCP Authentication: OAuth 2.1 기반 인증 제대로 이해하기

MCP Authentication: OAuth 2.1 기반 인증 제대로 이해하기

MCP 서버를 직접 만들어 보신 적 있으신가요? 처음에는 로컬에서 STDIO로 붙여서 쓰다가, 원격으로 배포하려는 순간 고민이 시작되는데요. "아무나 내 MCP 서버를 호출하면 안 되는데, 누가 접근할 수 있는지 어떻게 가리지?"라는 질문이 떠오르거든요 🤔 이 질문에 대한 공식 답변이 바로 MCP 스펙의 Authorization 섹션입니다. 2025년 11월 25일 개정판에서는 OAuth 2.1을 기반으로 여러 RFC를 조합한 인증 방식을 정리했는데요. 얼핏 보면 OAuth 2.1, RFC 7591, RFC 8414, RFC 87

Cloudflare Access로 내부 애플리케이션에 Zero Trust 적용하기

Cloudflare Access로 내부 애플리케이션에 Zero Trust 적용하기

회사 내부용 대시보드나 관리자 페이지를 외부에 공개해야 할 때 어떻게 하시나요? 사내 VPN으로 감싸자니 외부 협업자에게 계정을 만들어 줘야 하고, IP 화이트리스트로 막자니 재택근무 환경에서 자꾸 IP가 바뀌어 곤란하죠. 그렇다고 아예 공개 URL로 두고 로그인 화면을 직접 구현하자니 인증 로직을 매번 새로 붙여야 하니 배보다 배꼽이 더 큽니다 😅 Cloudflare Access는 이런 고민을 엣지에서 해결해 주는 ZTNA(Zero Trust Network Access) 서비스입니다. 원본 애플리케이션에 손대지 않고도 요청이 C

SSL 인증서 수동 관리 입문: 발급부터 설치, 갱신까지

SSL 인증서 수동 관리 입문: 발급부터 설치, 갱신까지

요즘은 HTTPS를 켜는 일이 너무 쉬워졌습니다. Cloudflare 뒤에 사이트를 두거나 Caddy로 서버를 띄우면 인증서라는 단어를 한 번도 마주치지 않고 자물쇠가 켜지는데요. 그러다 회사 사내 서버나 공유 호스팅처럼 자동화가 없는 환경을 만나면 갑자기 낯선 단어들이 쏟아집니다. CSR을 만들라고 하고, 파일이 세 개 오고, 중간 인증서를 붙이라고 하고, 만료 30일 전부터만 갱신이 된다고 합니다. 이 낯섦은 능력의 문제가 아니라 세대 차이입니다. Let's Encrypt 이전 시대에는 인증서가 자동으로 흐르는 인프라가 아니라

Varlock으로 환경 변수를 안전하게 관리하기

Varlock으로 환경 변수를 안전하게 관리하기

프로젝트를 진행하다 보면 .env 파일에 API 키, 데이터베이스 비밀번호 같은 민감한 정보를 저장하게 됩니다. dotenv를 사용해서 이 값들을 불러오는 건 이제 거의 표준처럼 자리 잡았는데요. 그런데 이런 경험 한 번쯤 있지 않으신가요? 새로운 팀원이 프로젝트를 클론받고 나서 .env.example을 보고 .env를 만들었는데, 필수 환경 변수 하나를 빼먹어서 런타임에 에러가 나는 상황. 아니면 AI 코딩 도우미에게 코드를 맡겼더니 .env 파일의 실제 비밀값까지 컨텍스트에 포함돼 버리는 아찔한 순간. 😅 Varlock은 바로

OAuth 2.0 메타데이터와 엔드포인트 동적 발견

OAuth 2.0 메타데이터와 엔드포인트 동적 발견

OAuth 2.0 스펙은 /authorize나 /token 같은 엔드포인트가 존재해야 한다고는 말하지만, 실제로 어떤 경로에 두어야 하는지는 정하지 않습니다. 그래서 한동안 관행은 단순했어요. "구글은 accounts.google.com/o/oauth2/v2/auth, GitHub는 github.com/login/oauth/authorize" 같은 정보를 사람이 공식 문서에서 읽어다가 클라이언트 코드에 박아 넣는 방식이었죠. 이 방식은 금방 한계를 드러냈어요. 새로운 신원 공급자(Identity Provider, 이하 IdP)를 추

OAuth Dynamic Client Registration 제대로 이해하기

OAuth Dynamic Client Registration 제대로 이해하기

OAuth 연동을 만들 때 보통 가장 먼저 하는 일은 인가 서버(Authorization Server, 이하 AS)에 애플리케이션을 등록하는 것입니다. 관리 콘솔에 들어가 앱 이름, 리다이렉트 URI, 로고, 권한 범위를 입력하고 client_id와 client_secret을 발급받죠. 구글이나 GitHub처럼 정해진 제공자 한두 곳만 상대한다면 이 방식으로 충분합니다. 그런데 클라이언트와 AS가 미리 서로를 알 수 없는 환경이라면 이야기가 달라집니다. 사용자가 처음 보는 MCP 서버에 AI 클라이언트를 붙이거나, 여러 테넌트가 각

OAuth Client Credentials: 사용자 없는 M2M 인증 제대로 이해하기

OAuth Client Credentials: 사용자 없는 M2M 인증 제대로 이해하기

지금까지 OAuth를 이야기할 때는 늘 사용자가 주인공이었습니다. 인가 코드(Authorization Code) + PKCE 흐름에서 봤듯이, 사용자가 로그인하고 동의하면 그 사용자를 대신할 토큰이 발급되는 식이었죠. 그런데 현실에는 로그인할 사람이 아예 없는 통신도 많습니다. 새벽 3시에 도는 결제 배치 작업이 결제 API를 호출하고, 백엔드 서비스 A가 내부 서비스 B의 API를 두드리고, CI 파이프라인이 배포 API를 부르는 상황을 떠올려 보세요. 여기엔 브라우저도, 동의 화면도, 사용자도 없습니다. 이렇게 서버가 서버를 부

OAuth 2.0 엔드포인트 제대로 이해하기

OAuth 2.0 엔드포인트 제대로 이해하기

OAuth 2.0을 처음 배울 때는 "인가 코드(authorization code)를 받아서 액세스 토큰(access token)으로 바꾼다"는 큰 그림만 머리에 담아두어도 충분한데요. 하지만 실제로 연동을 구현하거나 디버깅하는 단계로 넘어가면, 어떤 엔드포인트에 어떤 파라미터를 어떤 조건으로 보내야 하는가가 성패를 좌우합니다. "동의 화면까지는 뜨는데 토큰 교환에서 invalid_grant가 뜬다", "리다이렉트가 자꾸 invalid_redirect_uri로 거부된다" 같은 이슈는 거의 전부 특정 엔드포인트의 파라미터 규약을 놓쳐

OAuth Authorization Code + PKCE 흐름 단계별로 이해하기

OAuth Authorization Code + PKCE 흐름 단계별로 이해하기

OAuth를 배우다 보면 결국 이 문장을 자주 만나게 됩니다. 사용자 로그인이 있으면 인가 코드(Authorization Code) + PKCE를 쓰세요. 맞는 말인데, 처음 구현할 때는 막상 손에 잘 잡히지 않습니다. code는 어디서 오고, code_verifier는 누가 들고 있어야 하고, state는 PKCE와 다른 건지 헷갈리거든요. 동의 화면까지는 잘 뜨는데 콜백에서 invalid_grant가 나거나, 토큰 교환에서 redirect_uri가 다르다고 거부되는 일도 흔합니다. 이 글에서는 인가 코드 + PKCE 흐름을 요청

OAuth Grant Type 한 번에 정리하기

OAuth Grant Type 한 번에 정리하기

OAuth를 공부하다 보면 "인가 코드 흐름(Authorization Code Flow)을 써라", "클라이언트 자격 증명(Client Credentials)은 서버 간 통신용이다", "암묵적 흐름(Implicit)은 이제 쓰지 마라" 같은 말을 자주 듣게 됩니다. 처음에는 다 같은 OAuth처럼 보이는데, 왜 이렇게 flow가 여러 개인지 헷갈리기 쉽죠. 사실 스펙 관점에서 더 정확한 이름은 흐름(flow)보다 **그랜트 유형(grant type)**입니다. 클라이언트가 어떤 근거(grant)를 가지고 액세스 토큰(access t

JWK와 JWKS 제대로 이해하기

JWK와 JWKS 제대로 이해하기

JWT를 검증하려면 서명을 확인할 공개 키가 필요한데요. 그런데 그 공개 키, 도대체 어떤 형식으로 표현하고 어떻게 주고받을까요? 전통적으로는 X.509 인증서나 PEM 같은 형식을 썼지만, JSON 기반인 JOSE 생태계에는 키를 위한 전용 형식이 따로 있습니다. 바로 **JWK(JSON Web Key)**입니다. 키 하나를 JWK로 표현하고, 여러 JWK를 묶으면 **JWKS(JWK Set)**가 되는데요. OAuth/OIDC에서 인가 서버가 공개 키를 배포할 때 쓰는 그 꾸러미가 바로 JWKS입니다. 이 글에서는 JWK가 무엇

NestJS에서 API 버전 관리하기(Versioning)

NestJS에서 API 버전 관리하기(Versioning)

이번 글에서는 NestJS에서 API의 버전을 체계적으로 관리하는 방법에 대해서 배워보도록 하겠습니다. API Versioning이란? REST API와 같은 서버 애플리케이션을 운영하다 보면, 부득이하게 클라이언트에 큰 영향을 줄 수 있는 위험한 변경을 해야 할 때가 있는데요. API Versioning, 즉 버전 관리를 통해서, 우리는 서버 측 API 변경에 따른 클라이언트의 영향을 최소화하고, API의 호환성과 안정성을 높일 수 있습니다. 버전 관리가 이루어지는 API는 보통 클라이언트에게 v1, v2, v3... 이런 식으로

가드(Guard)로 NestJS 앱 안전하게 지키기

가드(Guard)로 NestJS 앱 안전하게 지키기

이번 글에서는 가드(Guard)를 활용하여 NestJS 앱을 위험한 요청으로 부터 효과적으로 보호하는 방법에 대해서 배워보도록 하겠습니다. 가드(Guard)란? NestJS에서 가드(guard)란 애플리케이션의 최전선에서 말그대로 애플리케이션을 보호하는 역할을 담당하는데요. NestJS로 들어오는 요청은 컨트롤러(controller) 단에 도달하기 전에 반드시 가드를 거쳐가도록 되어 있습니다. 가드를 이용하면 컨트롤러가 요청을 처리하기 전에 안전하지 않은 요청을 효과적으로 차단할 수 있습니다. 따라서 애플리케이션 보안을 위해서 필수

Discord