NGINX Plus 릴리스 10에서는 JSON 웹 토큰(JWT, “jots”로 발음)을 사용해 웹 및 API 서비스의 인증 처리를 오프로드하는 기능을 지원하기 시작했습니다. R10 출시 이후 새로운 릴리스마다 기능을 지속적으로 확장해 왔습니다.
Starting in NGINX Plus R14부터 NGINX Plus는 중첩된 클레임과 배열 데이터를 포함하는 JWT를 지원합니다. API 게이트웨이 시나리오에서 NGINX Plus는 JWT를 사용하여 백엔드 서비스 및 API 엔드포인트에 대한 연결을 요청하는 클라이언트를 인증할 수 있습니다.
가끔 NGINX Plus를 사용해 JWT를 인증한 다음, JWT 정보를 기반으로 보다 고급 로드 밸런싱 결정을 내리는 기본 구성을 제공해 달라는 요청을 받곤 합니다. 가장 간단한 해결 방법은 인증에 성공하면 서비스에 대한 액세스를 허용하고, 실패하면 연결을 차단하거나 리디렉션하는 것입니다.
이 게시물의 단계별 가이드는 NGINX Plus를 사용한 JWT 인증 및 콘텐츠 기반 라우팅을 처음부터 끝까지 다루는 개념 증명입니다. 최대한 폭넓은 가능성을 다루고 JWT에 대한 사전 지식이나 경험의 필요성을 줄이기 위해, JWT를 전혀 몰라도 예제와 배경 설명을 통해 이 솔루션을 배포할 수 있도록 “JWT 101” 단계별 가이드를 마련했습니다.
이미 JWT를 사용해 본 경험이 있거나 환경에 기존 JWT가 있는 경우, 처음 두 섹션은 건너뛰고 제공된 NGINX Plus 구성 코드 조각을 환경에 맞게 조정하여 JWT 클레임 데이터를 기반으로 고급 부하 분산 결정을 내릴 수 있습니다.
사전 요구 사항
이 문서는 NGINX Plus를 새로 설치했으며 기본 구성 파일이 다음 위치에 있다고 가정합니다:
- /etc/nginx/nginx.conf
- /etc/nginx/conf.d/default.conf
NGINX Plus 설치 및 시작에 대한 자세한 내용은 NGINX Plus 관리자 가이드를 참조하세요.
모든 CLI 명령은 root 권한으로 실행된다고 가정하므로, root가 아닌 사용자는 해당 환경에서 sudo 권한이 있어야 합니다.
추가 배경 정보와 NGINX Plus에서 JWT를 활용하는 다른 사용 사례는 다음 링크를 참조하세요:
JWT 및 관련 서명 키 생성
아래 지침에서는 JWT 클레임을 기본적으로 처리하도록 NGINX Plus를 구성하는 방법을 보여 주기 위해, 이 예제에 맞는 페이로드 데이터를 사용하여 JWT를 처음부터 만드는 과정을 안내합니다. 샘플 JWT 대신 기존 JWT를 사용하는 경우, JWT를 만들 때 사용한 서명 키와 일치하는 Base64URL 인코딩 문자열이 “secrets” 파일에 포함되어 있는지 확인해야 합니다. 또한 JWT 페이로드의 클레임도 수정해야 할 수 있습니다.
참고: JWT를 어떤 방식으로 생성하든 Base64URL 인코딩을 사용해야 합니다. Base64URL 인코딩은 Base64 인코딩에서 일반적으로 사용되는 HTTP 비호환 문자뿐만 아니라 패딩(= 문자로 처리)도 올바르게 처리합니다. 많은 도구가 이를 자동으로 처리하지만, 수동 CLI 기반 Base64 인코더와 일부 JWT 생성 도구는 그렇지 않습니다. Base64URL 인코딩에 대한 자세한 내용은 Brock Allen의 base64url 인코딩 및 RFC 4648을 참조하세요.
이 예에서는 jwt.io의 GUI(Base64URL 인코딩을 올바르게 처리함)를 사용하여 대칭형 HS256 JWT를 생성합니다. 다음 스크린샷은 아래 지침에 명시된 값을 입력하고 서명이 검증된 후의 GUI 모습을 보여 줍니다.

Working in the GUI at jwt.io의 GUI에서 작업하여 오른쪽 Decoded 열의 필드에 표시된 값을 확인하거나 입력한 다음 HS256 JWT를 생성합니다:
- 다음 기본값이 HEADER 필드에 표시되는지 확인하고, 필요한 경우 내용이 일치하도록 수정합니다:
{ "alg": "HS256",
"typ": "JWT"
}- In the VERIFY SIGNATURE 필드에서 상자에 있는 기본값(
secret)을nginx123으로 바꿉니다. (두 단계의 순서를 바꾸면 문제가 발생할 수 있으므로, PAYLOAD 필드에 데이터를 입력하기 전에 이 값을 변경합니다.) - 다음 내용으로 PAYLOAD 필드의 내용을 바꿉니다:
{ "exp": 1545091200,
"name": "Create New User",
"sub": "cuser",
"gname": "wheel",
"guid": "10",
"fullName": "John Doe",
"uname": "jdoe",
"uid": "222",
"sudo": true,
"dept": "IT",
"url": "http://secure.example.com"
}- 참고:
exp클레임은 JWT의 만료 날짜와 시간을 설정하며, 이를 UNIX 에포크 시간(1970년 1월 1일 자정(UTC) 이후 경과한 초 단위 시간)으로 나타냅니다. 예시 값은 2018년 12월 18일 자정(UTC)을 나타냅니다. 만료 날짜를 조정하려면 에포크 시간을 변경하세요. - 필드 아래의 표시줄이 파란색이고 서명 확인됨이라고 표시되는지 확인합니다.
- 왼쪽의 Encoded 열에 있는 값을 파일이나 버퍼에 복사합니다. 이 값은 사용자 jdoe가 http://secure.example.com에 액세스하기 위해 제시해야 하는 JWT의 전체 텍스트이며, 아래 테스트에서 사용합니다. 여기서는 표시를 위해 JWT를 줄바꿈하여 보여 주지만, NGINX Plus에는 한 줄 문자열로 제시해야 합니다.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOi
JDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkI
joiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIy
Iiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGx
lLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4
NGINX Plus 호스트에서 다음 단계에 따라 NGINX Plus가 nginx123(으)로 서명된 JWT를 검증하는 데 사용하는 키 파일을 생성합니다:
- 다음 명령을 실행하여 서명 문자열에 해당하는 Base64URL 인코딩 문자열을 생성합니다. (
tr명령은 Base64URL 인코딩에 필요한 문자를 치환합니다.)
# echo -n nginx123 | base64 | tr '+/' '-_' | tr -d '='bmdpbngxMjM- In the /etc/nginx/ 디렉터리에 NGINX Plus가 JWT 서명을 확인하는 데 사용할 api_secret.jwk라는 키 파일을 만듭니다. 다음 내용을 삽입합니다.
k필드의 값은 이전 단계에서 생성한nginx123을 Base64URL로 인코딩한 형식입니다.
{"keys": [{
"k":"bmdpbngxMjM",
"kty":"oct"
}]
}JWT를 처리하도록 NGINX Plus 구성
이 섹션의 지침에서는 요청에 포함된 JWT를 검증하고, 클라이언트가 인증된 경우 권한이 없는 클라이언트에게 표시되는 기본 페이지 대신 보호된 리소스를 제공하도록 NGINX Plus를 구성합니다. 또한 JWT 관련 정보를 기록하는 새로운 로그 형식을 정의합니다.
JWT 검증 및 콘텐츠 기반 라우팅 구성
이 지침에서는 표준 모범 사례에 따라 default.conf 구성 파일의 이름을 변경하여 NGINX Plus가 해당 파일을 읽지 않도록 하고, 테스트 전용 구성을 새로 만듭니다. 이렇게 하면 테스트를 마쳤거나 테스트 중 문제가 발생했을 때 기본 구성을 쉽게 복원할 수 있습니다.
- 이름을 변경합니다: default.conf:
# mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak- 다음 위치에 jwt-test.conf라는 새 구성 파일을 만듭니다. /etc/nginx/conf.d/. 파일 내용은 다음과 같습니다. 이 구성은 JWT 전용 로깅, JWT 검증 및 콘텐츠 기반 라우팅을 설정합니다(코드 조각 뒤에 전체 분석이 이어집니다).
server {
listen 80;
access_log /var/log/nginx/host.access.log jwt;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
# JWT validation
auth_jwt "JWT Test Realm" token=$arg_myjwt;
auth_jwt_key_file /etc/nginx/api_secret.jwk;
error_log /var/log/nginx/host.jwt.error.log debug;
if ( $jwt_claim_uid = 222 ) {
add_header X-jwt-claim-uid "$jwt_claim_uid" always;
add_header X-jwt-status "Redirect to $jwt_claim_url" always;
return 301 $jwt_claim_url;
}
if ( $jwt_claim_uid != 222 ) {
add_header X-jwt-claim-uid "$jwt_claim_uid" always;
add_header X-jwt-status "Invalid user, no redirect" always;
}
}
}The directives in the location 블록의 지시문은 JWT가 포함된 HTTP 요청을 처리하는 방법을 NGINX Plus에 알려 줍니다. (access_log 지시문으로 정의된 로깅 구성에 대한 자세한 내용은 다음 섹션을 참조하세요.) NGINX Plus는 다음 단계를 수행합니다:
- 요청 문자열의 myjwt 인수에서 JWT를 추출합니다(
auth_jwt지시문의token인수로 지정됨). - 지정된 서명 키를 사용하여 JWT를 디코딩합니다. 서명 키는
auth_jwt_key-file지시문으로 지정합니다(여기서는 api_secret.jwk). 그런 다음 페이로드를 다음과 같이 처리합니다(이러한 작업은 JWT 처리에 내재된 것으로, 이에 해당하는 NGINX Plus 지시문은 없습니다).- JWT가 만료되지 않았는지 확인합니다. 즉, 페이로드의
exp클레임에 지정된 만료 날짜가 과거가 아닌지 확인합니다. - 페이로드의 각 클레임에 대해 키-값 쌍을 생성합니다. 키 이름은
$jwt_claim_claim-name형식의 변수입니다(예:$jwt_claim_uid는uid클레임에 해당).
- JWT가 만료되지 않았는지 확인합니다. 즉, 페이로드의
- 모든 오류를 /var/log/nginx/host.jwt.error.log에
debug수준으로 기록합니다. - Tests whether the value of
$jwt_claim_uid값이222인지 테스트하고(두 개의if지시문에 지정된 대로) 클라이언트에 적절한 응답을 보냅니다. 이와 같이 JWT의 정보를 사용하여 콘텐츠 기반 라우팅을 수행합니다.참고: 변수를 평가하는 데if지시문을 사용하는 것은 일반적으로 모범 사례로 간주되지 않으므로, 보통은 대신map지시문을 사용하는 것이 좋습니다. 하지만 이 간단한 예제에서는if지시문이 의도한 대로 작동합니다.- 값이
222이면 NGINX Plus는 클라이언트를 JWt의url클레임에 지정된 URL로 리디렉션하는 응답(return지시어)을 보냅니다. 디버깅을 위해 응답에 두 개의 헤더(add_header지시어)를 추가합니다. 첫 번째 헤더는uid클레임의 값을 캡처하고, 두 번째 헤더는 클라이언트가 리디렉션되었다는 사실을 기록합니다. - 값이
222가 아니면 NGINX Plus는 동일한location블록의root및index지시문에 정의된 기본 인덱스 페이지를 제공합니다. 이 경우에도 디버깅을 위해uid클레임 값을 캡처하고 클라이언트가 JWT에 지정된 URL에 액세스하지 못했다는 사실을 기록하는 헤더를 추가합니다.
- 값이
실질적으로 이 구성은 권한이 있는 사용자에게만 보호된 리소스에 대한 액세스를 허용합니다. 즉, 유효한 JWT가 있는 사용자는 JWT에 지정된 URL에 액세스할 수 있지만, 유효한 JWT가 없는 사용자는 기본 페이지에 액세스하게 됩니다.
JWT 데이터 로깅
콘텐츠 기반 라우팅을 위한 JWT 처리 구성을 완료하기 위해 jwt라는 로깅 형식을 정의합니다. 이 형식은 jwt-test.conf의 access_log 지시문에서 참조되며, 액세스 로그에 JWT 데이터를 기록합니다.
- 다음
log_format지시어를 /etc/nginx/nginx.conf에 추가합니다:
log_format jwt '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'$jwt_header_alg $jwt_claim_uid $jwt_claim_url';- 이 형식에는 이 연습에서 사용하는 두 가지 JWT 클레임(
uid및url)이 포함되어 있지만, 다음 형식으로 클레임에 해당하는 변수 이름을 사용하면 모든 JWT 클레임 데이터를 로그에 기록할 수 있습니다.$jwt_claim_claim‑name. - Save nginx.conf을 저장한 다음, 다음 명령을 실행하여 새 jwt-test.conf 파일을 포함한 전체 구성의 구문이 올바른지 테스트합니다. 보고된 오류가 있으면 수정합니다.
# nginx -t- NGINX Plus를 다시 로드합니다.
# nginx -s reload구성 테스트
브라우저나 curl과 같은 CLI 도구를 사용하여 NGINX Plus가 JWT를 올바르게 검증하고, 이를 제시하는 클라이언트를 인증하며, 콘텐츠 기반 라우팅을 수행하는지 테스트할 수 있습니다. (콘텐츠 기반 라우팅은 제외하고 인증과 검증만 테스트하려면 jwt-test.conf에서 두 개의 if 블록을 주석 처리하세요.)
테스트를 실행하려면 요청 URL에 myjwt 인수를 포함하고, url 클레임이 222인 JWT의 전체 텍스트를 제공합니다. 여기서도 보기 쉽도록 줄바꿈을 추가했습니다.
http://example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiO
jE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW
1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9
lIiwidWlkIjoiMjIyIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5le
GFtcGxlLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4
이에 해당하는 curl 명령어는 다음과 같습니다(원하는 경우 복사하여 붙여넣을 수 있도록 줄바꿈 없이 표시했습니다):
# curl -v example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMjIyIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.YYQCNvzj17F726QvKoIiuRGeUBl_xAKj62Zvc9xkZb4JWT의 uid 클레임 값이 222이므로 NGINX Plus가 제한된 페이지인 http://secure.example.com의 콘텐츠를 표시할 것으로 예상합니다.
이제 JWT의 url 클레임 값이 222가 아닐 때 NGINX Plus가 제한된 페이지의 콘텐츠를 표시하지 않고 대신 로컬 서버의 index.html 페이지(이 경우 http://example.com/index.html)를 표시하는지 테스트합니다.
먼저 jwt.io에서 uid 클레임이 222가 아닌 다른 JWT를 생성합니다. 이 예에서는 111로 설정합니다. 해당 JWT를 포함한 요청 URL은 다음과 같습니다:
http://example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiO
jE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW
1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9
lIiwidWlkIjoiMTExIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5l
eGFtcGxlLmNvbSJ9.Ch9xqsGzB8fRVX-3CBuCxP1Ia3oGKB1OnO6qwi_oBgg
다음은 curl 명령어입니다:
# curl -v example.com/index.html?myjwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDUwOTEyMDAsIm5hbWUiOiJDcmVhdGUgTmV3IFVzZXIiLCJzdWIiOiJjdXNlciIsImduYW1lIjoid2hlZWwiLCJndWlkIjoiMTAiLCJmdWxsTmFtZSI6IkpvaG4gRG9lIiwidW5hbWUiOiJqZG9lIiwidWlkIjoiMTExIiwic3VkbyI6dHJ1ZSwiZGVwdCI6IklUIiwidXJsIjoiaHR0cDovL3NlY3VyZS5leGFtcGxlLmNvbSJ9.Ch9xqsGzB8fRVX-3CBuCxP1Ia3oGKB1OnO6qwi_oBgg이 경우 NGINX Plus가 다음 페이지를 제공할 것으로 예상합니다. http://example.com/index.html.
두 테스트 조건 모두에서 헤더 검사 도구(예: curl 또는 일부 브라우저에서 제공하는 개발자 도구)를 사용하여 새 헤더인 X-jwt-claim-uid 및 X-jwt-status가 응답에 추가되었는지 확인할 수 있습니다.
테스트 중 문제가 발생하면 다음 위치의 액세스 로그와 오류 로그를 확인하세요. /var/log/nginx/host.jwt* 특히 오류 로그를 통해 검증 문제, 검증 파일 액세스 문제 등을 확인할 수 있습니다.
쿠키로 JWT 전달하기
기본 예제에서 NGINX Plus는 요청 URL의 myjwt 인수에서 JWT를 추출합니다. NGINX Plus는 쿠키를 통한 JWT 전달도 지원합니다(자세한 내용은 NGINX JWT 참조 문서 참고). jwt-test.conf에서 auth_jwt 지시문을 변경하여 token 매개변수의 첫 번째 요소로 $arg 대신 $cookie를 사용합니다:
auth_jwt "JWT Test Realm" token=$cookie_myjwt;JWT를 myjwt라는 쿠키로 전달하려면 적절한 curl 명령어는 다음과 같습니다:
# curl -v --cookie myjwt=JWT-text example.com/index.htmlJWT를 활용한 콘텐츠 기반 라우팅을 직접 사용해 보세요. 지금 NGINX 30일 무료 체험을 시작하거나 사용 사례에 대해 논의하려면 문의해 주세요.
작성자 소개

관련 블로그 게시물
시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.
F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.
Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.
진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.
NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.
Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.