블로그 | NGINX

API 게이트웨이로 NGINX 배포, 2부: 백엔드 서비스 보호

NGINX-F5-수평-검정-유형-RGB의 일부
리엄 크릴리 썸네일
리암 크릴리
2021년 1월 20일 게시

이는 NGINX 오픈 소스와 NGINX Plus를 API 게이트웨이로 배포하는 방법에 대한 시리즈의 두 번째 블로그 게시물입니다.

  • 1부에서는 여러 사용 사례에 대한 자세한 구성 지침을 제공합니다.

  • 이 게시물은 해당 사용 사례를 확장하고 프로덕션에서 백엔드 API 서비스를 보호하고 보안하는 데 적용할 수 있는 다양한 보호 장치를 살펴봅니다.

    이 게시물은 원래 2018년에 게시되었으며, 규칙을 다시 작성하는 대신 중첩된 위치 블록을 사용하여 요청을 라우팅하는 등 API 구성에 대한 최신 모범 사례를 반영하도록 업데이트되었습니다 .

  • 3부에서는 NGINX 오픈 소스와 NGINX Plus를 gRPC 서비스에 대한 API 게이트웨이로 배포하는 방법을 설명합니다.

메모 : 별도로 명시된 경우를 제외하고 이 게시물의 모든 정보는 NGINX 오픈 소스와 NGINX Plus에 모두 적용됩니다. 읽기 편하도록 블로그의 나머지 부분에서는 간단히 "NGINX"라고 언급합니다.

속도 제한

브라우저 기반 클라이언트와 달리 개별 API 클라이언트는 API에 엄청난 부하를 줄 수 있으며, 심지어 다른 API 클라이언트가 효과적으로 차단될 정도로 시스템 리소스를 많이 소모할 수도 있습니다. 악의적인 클라이언트만 이러한 위협을 가하는 것은 아닙니다. 잘못 작동하거나 버그가 있는 API 클라이언트는 백엔드를 과부하시키는 루프에 빠질 수 있습니다. 이를 방지하기 위해, 우리는 각 클라이언트의 공정한 사용을 보장하고 백엔드 서비스의 리소스를 보호하기 위해 속도 제한을 적용합니다.

NGINX는 요청의 모든 속성에 따라 속도 제한을 적용할 수 있습니다. 일반적으로 클라이언트 IP 주소가 사용되지만 API에 대한 인증이 활성화된 경우 인증된 클라이언트 ID가 더 안정적이고 정확한 속성입니다.

속도 제한 자체는 최상위 API 게이트웨이 구성 파일에서 정의되며 이를 전역적으로, API별로 또는 URI별로 적용할 수 있습니다.

 

이 예에서 4번째 줄의 limit_req_zone 지시문은 각 클라이언트 IP 주소( $binary_remote_addr )에 대해 초당 10개의 요청이라는 속도 제한을 정의하고, 5번째 줄의 지시문은 인증된 각 클라이언트 ID( $http_apikey )에 대해 초당 200개의 요청이라는 제한을 정의합니다. 이는 적용되는 위치에 관계없이 여러 개의 요금 한도를 정의할 수 있는 방법을 보여줍니다. API는 동시에 여러 개의 속도 제한을 적용할 수도 있고, 다른 리소스에 대해 각기 다른 속도 제한을 적용할 수도 있습니다.

그런 다음 다음 구성 스니펫에서는 limit_req 지시문을 사용하여 1부<.htmla>에 설명된 "Warehouse API"의 정책 섹션에서 첫 번째 속도 제한을 적용합니다. 기본적으로 NGINX는 다음을 보냅니다. 503 (서비스 없는) 속도 제한을 초과했을 때의 응답입니다. 하지만 API 클라이언트가 속도 제한을 초과했다는 사실을 명확하게 알고 있으면 동작을 수정할 수 있어 도움이 됩니다. 이를 위해 우리는 다음을 사용합니다. 제한_요청_상태 보내라는 지시 429 (도 많은 요청) 대신 응답하세요.

 

limit_req 지시문에 추가 매개변수를 사용하여 NGINX가 속도 제한을 적용하는 방식을 미세 조정할 수 있습니다. 예를 들어, 한도를 초과했을 때 요청을 완전히 거부하는 대신 대기열에 넣어 요청 비율이 정의된 한도 아래로 떨어질 수 있도록 시간을 벌 수 있습니다. 속도 제한을 미세 조정하는 것에 대한 자세한 내용은 블로그의 NGINX 및 NGINX Plus를 사용한 속도 제한을 참조하세요.

특정 요청 방법 적용

RESTful API의 경우 HTTP 메서드(또는 동사)는 각 API 호출의 중요한 부분이며 API 정의에 매우 중요합니다. 예를 들어 Warehouse API의 가격 책정 서비스를 살펴보겠습니다.

  • GET /api/warehouse/pricing/item001 은 item001의 가격을 반환합니다.
  • PATCH /api/warehouse/pricing/item001 은 item001의 가격을 변경합니다.

Warehouse API에서 URI 라우팅 정의를 업데이트하여 가격 책정 서비스에 대한 요청에서 이 두 가지 HTTP 메서드만 허용하고(재고 서비스에 대한 요청에서는 GET 메서드만 허용)할 수 있습니다.

 

이 구성을 적용하면 22번째 줄에 나열된 방법 외의 방법을 사용하여 가격 책정 서비스에 대한 요청(및 13번째 줄에 나열된 방법 외의 인벤토리 서비스에 대한 요청)은 거부되고 백엔드 서비스로 전달되지 않습니다. NGINX는 다음을 보냅니다. 405 (방법 아니다 허용된) 다음 콘솔 추적에서 볼 수 있듯이, API 클라이언트에게 오류의 정확한 성격을 알리는 응답입니다. 최소 공개 보안 정책이 필요한 경우 error_page 지시문을 사용하여 이 응답을 대신 덜 유익한 오류로 변환할 수 있습니다. 예를 들어,400 (잘못된 요청입니다.)

$ curl https://api.example.com/api/warehouse/pricing/item001 {"sku":"item001","price":179.99} $ curl -X DELETE https://api.example.com/api/warehouse/pricing/item001 {"status":405,"message":"허용되지 않는 메서드"}

세분화된 액세스 제어 적용

이 시리즈의 1부에서는 API 키 , JSON 웹 토큰(JWT) 등의 인증 옵션을 활성화하여 API를 무단 액세스로부터 보호하는 방법을 설명했습니다. 인증된 ID나 인증된 ID의 속성을 사용하여 세분화된 액세스 제어를 수행할 수 있습니다.

여기서 우리는 그러한 두 가지 예를 보여드리겠습니다.

물론, HTTP 기본 인증OAuth 2.0 토큰 내부 검사 와 같은 다른 인증 방법도 이러한 샘플 사용 사례에 적용할 수 있습니다.

특정 리소스에 대한 액세스 제어

Warehouse API 인벤토리 서비스의 감사 리소스에 "인프라 클라이언트"만 액세스하도록 허용하려고 한다고 가정해 보겠습니다. API 키 인증이 활성화되면 블록을 사용하여 인프라 클라이언트 이름의 허용 목록을 생성하여 변수 $is_infrastructure가 다음으로 평가되도록 합니다.1 해당 API 키가 사용되는 경우.

 

창고 API 정의에서 재고 감사 리소스에 대한 위치 블록을 추가합니다( 15~20행 ). if 블록은 인프라 클라이언트만 리소스에 액세스할 수 있도록 보장합니다.

 

15번째 줄의 위치 지시문은 = (등호) 수정자를 사용하여 감사 리소스에서 정확한 일치를 수행합니다. 정확한 일치는 다른 리소스에 사용된 기본 경로 접두사 정의보다 우선합니다. 다음 추적은 이 구성을 적용하면 허용 목록에 없는 클라이언트가 인벤토리 감사 리소스에 액세스할 수 없는 방식을 보여줍니다. 표시된 API 키는 client_two ( 1부 에 정의된 대로)에 속합니다.

$ curl -H "apikey: QzVV6y1EmQFbbxOfRCwyJs35" https://api.example.com/api/warehouse/inventory/audit {"status":403,"message":"금지됨"}

특정 방법에 대한 액세스 제어

위에서 정의한 대로, 가격 책정 서비스는 GETPATCH 메서드를 허용합니다. 각각 클라이언트가 특정 항목의 가격을 얻고 수정할 수 있도록 합니다. (또한 가격 데이터의 전체 수명 주기 관리를 제공하기 위해 POSTDELETE 메서드를 허용할 수도 있습니다.) 이 섹션에서는 해당 사용 사례를 확장하여 특정 사용자가 발급할 수 있는 메서드를 제어합니다. Warehouse API에 JWT 인증이 활성화되면 각 클라이언트에 대한 권한이 사용자 지정 클레임으로 인코딩됩니다. 가격 데이터를 변경할 수 있는 권한이 있는 관리자에게 발급된 JWT에는 "admin":true 클레임이 포함됩니다. 이제 관리자만 변경 작업을 수행할 수 있도록 액세스 제어 논리를 확장했습니다.

 

api_gateway.conf 의 하단에 추가된 이 블록은 요청 메서드( $request_method )를 입력으로 받아서 새 변수인 $admin_permitted_method 를 생성합니다. 읽기 전용 메서드는 항상 허용됩니다(62~64행). 하지만 쓰기 작업에 대한 액세스는 JWT의 admin 클레임 값에 따라 달라집니다(65행). 이제 Warehouse API 구성을 확장하여 관리자만 가격을 변경할 수 있도록 했습니다.

 

Warehouse API는 모든 클라이언트가 유효한 JWT(7번째 줄)를 제시하도록 요구합니다. 또한 $admin_permitted_method 변수를 평가하여 쓰기 작업이 허용되는지 확인합니다(25번째 줄). JWT 인증은 NGINX Plus에서만 사용할 수 있습니다.

요청 크기 제어

HTTP API는 일반적으로 백엔드 API 서비스에서 처리할 지침과 데이터를 포함하기 위해 요청 본문을 사용합니다. 이는 XML/SOAP API뿐 아니라 JSON/REST API에도 해당됩니다. 따라서 요청 본문은 백엔드 API 서비스에 공격 벡터를 제공할 수 있으며, 매우 큰 요청 본문을 처리할 때 버퍼 오버플 로 공격에 취약할 수 있습니다.

기본적으로 NGINX는 본문이 1MB보다 큰 요청을 거부합니다. 이미지 처리와 같이 대용량 페이로드를 특별히 처리하는 API의 경우 이 값을 늘릴 수 있지만, 대부분 API에서는 더 낮은 값을 설정합니다.

 

7번째 줄의 client_max_body_size 지시어는 요청 본문의 크기를 제한합니다. 이러한 구성을 적용하면 가격 서비스에 대한 두 가지 서로 다른 PATCH 요청을 수신했을 때의 API 게이트웨이의 동작을 비교할 수 있습니다. 첫 번째 curl 명령은 작은 JSON 데이터를 전송하는 반면, 두 번째 명령은 큰 파일( /etc/services )의 내용을 전송하려고 시도합니다.

$ curl -iX PATCH -d '{"price":199.99}' https://api.example.com/api/warehouse/pricing/item001 HTTP/1.1 204 콘텐츠 없음 서버: nginx/1.19.5 연결: keep-alive $ curl -iX PATCH -d@/etc/services https://api.example.com/api/warehouse/pricing/item001 HTTP/1.1 413 요청 엔터티가 너무 큼 서버: nginx/1.19.5 콘텐츠 유형: application/json 콘텐츠 길이: 45 연결: 닫힘 {"status":413,"message":"페이로드가 너무 큽니다"}

요청 본문 검증

[ 편집자 - 다음 사용 사례는 NGINX JavaScript 모듈에 대한 여러 사례 중 하나입니다. 전체 목록은 NGINX JavaScript 모듈의 사용 사례를 참조하세요.

백엔드 API 서비스는 대용량 요청 본문을 통한 버퍼 오버플로 공격에 취약할 뿐만 아니라, 잘못되었거나 예상치 못한 데이터가 포함된 본문에도 취약할 수 있습니다. 요청 본문에 올바르게 형식화된 JSON이 필요한 애플리케이션의 경우 NGINX JavaScript 모듈<.htmla>을 사용하여 백엔드 API 서비스로 프록시하기 전에 JSON 데이터가 오류 없이 구문 분석되었는지 확인할 수 있습니다.

JavaScript 모듈이 설치 되면 js_import 지시문을 사용하여 JSON 데이터의 유효성을 검사하는 함수의 JavaScript 코드가 포함된 파일을 참조합니다.

 

js_set 지시어는 parseRequestBody 함수를 호출하여 평가되는 새 변수 $json_validated를 정의합니다.

 

parseRequestBody 함수는 JSON.parse 메서드(6번째 줄)를 사용하여 요청 본문을 구문 분석하려고 시도합니다. 구문 분석이 성공하면 이 요청에 대한 의도된 업스트림 그룹의 이름이 반환됩니다(8번째 줄). 요청 본문을 구문 분석할 수 없는 경우(예외 발생) 로컬 서버 주소가 반환됩니다(11번째 줄). return 지시문은 $json_validated 변수를 채워서 요청을 보낼 위치를 결정하는 데 사용할 수 있습니다.

 

Warehouse API의 URI 라우팅 섹션에서 22번째 줄의 proxy_pass 지시어를 수정합니다. 이전 섹션에서 설명한 Warehouse API 구성에서와 같이 백엔드 API 서비스에 요청을 전달하지만, 이제는 대상 주소로 $json_validated 변수를 사용합니다. 클라이언트 본문이 JSON으로 성공적으로 구문 분석되면 15번째 줄에 정의된 업스트림 그룹으로 프록시합니다. 그러나 예외가 발생한 경우에는 반환된 값인 127.0.0.1:10415를 사용하여 클라이언트에 오류 응답을 보냅니다.

 

요청이 이 가상 서버로 프록시되면 NGINX는 다음을 보냅니다. 415(지원되지 않는 미디어 유형) 클라이언트에 대한 응답입니다.

이렇게 완전한 구성을 완료하면 NGINX는 JSON 본문이 올바르게 형식화된 경우에만 백엔드 API 서비스에 대한 요청을 프록시합니다.

$ curl -iX POST -d '{"sku":"item002","price":85.00}' https://api.example.com/api/warehouse/pricing HTTP/1.1 201 생성됨 서버: nginx/1.19.5 위치: /api/warehouse/pricing/item002 $ curl -X POST -d 'item002=85.00' https://api.example.com/api/warehouse/pricing {"status":415,"message":"지원되지 않는 미디어 유형"}

$request_body 변수에 대한 참고 사항

JavaScript 함수 parseRequestBody는 $request_body 변수를 사용하여 JSON 구문 분석을 수행합니다. 하지만 NGINX는 기본적으로 이 변수를 채우지 않고, 중간 사본을 만들지 않고 단순히 요청 본문을 백엔드로 스트리밍합니다. URI 라우팅 섹션(16번째 줄) 내부의 mirror 지시어를 사용하여 클라이언트 요청의 사본을 생성하고 결과적으로 $request_body 변수를 채웁니다.

 

17번째와 19번째 줄의 지침은 NGINX가 요청 본문을 내부적으로 처리하는 방법을 제어합니다. 요청 본문이 디스크에 기록되지 않도록 client_body_buffer_size를 client_max_body_size 와 같은 크기로 설정합니다. 이렇게 하면 디스크 I/O 작업이 최소화되어 전반적인 성능이 향상되지만, 메모리 활용도는 높아집니다. 요청 본문이 작은 대부분의 API 게이트웨이 사용 사례의 경우 이는 좋은 절충안입니다.

언급한 대로, mirror 지시문은 클라이언트 요청의 사본을 생성합니다. $request_body를 채우는 것 외에는 이 복사본이 필요하지 않으므로 최상위 API 게이트웨이 구성의 server 블록에서 정의한 "막다른 길" 위치( /_get_request_body )로 보냅니다.

 

이 위치는 다음을 보내는 것 외에는 아무것도 하지 않습니다. 204( 내용 없음) 응답. 이 응답은 미러링된 요청과 관련되어 있으므로 무시되고 원래 클라이언트 요청 처리에 무시할 수 있는 오버헤드가 추가됩니다.

요약

NGINX 오픈 소스와 NGINX Plus를 API 게이트웨이로 배포하는 것에 대한 시리즈의 두 번째 블로그 게시물에서는 악성 및 부적절한 동작을 하는 클라이언트로부터 프로덕션 환경에서 백엔드 API 서비스를 보호하는 과제에 초점을 맞추었습니다. NGINX는 현재 인터넷에서 가장 바쁜 사이트에 전력을 공급하고 보호하는 데 사용되는 것과 동일한 기술을 사용하여 API 트래픽을 관리합니다.

이 시리즈의 다른 게시물을 확인하세요.

  • 1부에서는 몇 가지 필수 API 게이트웨이 사용 사례에서 NGINX를 구성하는 방법을 설명합니다.
  • 3부에서는 NGINX를 gRPC 서비스를 위한 API 게이트웨이로 배포하는 방법을 설명합니다.

NGINX Plus를 API 게이트웨이로 사용해 보려면 오늘 무료 30일 평가판을 시작하거나 당사에 문의하여 사용 사례에 대해 논의해 보세요 . 평가판 사용 기간 동안 GitHub Gist repo 에서 전체 구성 파일 세트를 사용하세요.


"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."