NGINX 튜토리얼: SQL 인젝션으로부터 Kubernetes 앱 보호하기

NGINX | 2022년 3월 22일

이 튜토리얼은 다음 자료의 개념을 실제로 적용해 보는 네 가지 튜토리얼 중 하나입니다. Microservices March 2022: Kubernetes 네트워킹:

더욱 다양한 Kubernetes 네트워킹 사용 사례에서 NGINX를 활용하는 방법에 대한 자세한 안내가 필요하신가요? 무료 전자책 NGINX를 활용한 Kubernetes 트래픽 관리: 실용 가이드를 다운로드하세요.

여러분은 베개부터 자전거까지 다양한 상품을 판매하는 인기 지역 매장의 IT 부서에서 근무하고 있습니다. 이 매장은 첫 온라인 스토어 출시를 앞두고 있지만, 공개하기 전에 보안 전문가에게 사이트 침투 테스트를 의뢰했습니다. 안타깝게도 보안 전문가가 문제를 발견했습니다! 온라인 스토어가 SQL 인젝션에 취약했던 것입니다. 보안 전문가는 이 취약점을 악용해 사용자 이름과 비밀번호를 비롯한 데이터베이스의 민감한 정보를 탈취할 수 있었습니다.

팀은 이 위기를 해결하기 위해 Kubernetes 엔지니어인 여러분을 찾아왔습니다. 다행히 여러분은 Kubernetes 트래픽 관리 도구를 사용하면 SQL 인젝션을 비롯한 여러 취약점을 완화할 수 있다는 사실을 알고 있습니다. 이미 앱을 외부에 노출하기 위한 Ingress 컨트롤러를 배포했으므로, 단 한 번의 구성만으로 이 취약점이 악용되지 않도록 할 수 있습니다. 이제 온라인 스토어를 예정대로 출시할 수 있습니다. 잘하셨습니다!

실습 및 튜토리얼 개요

이 블로그는 2022년 3월 마이크로서비스 시리즈의 유닛 3 실습인 Kubernetes의 마이크로서비스 보안 패턴과 함께 제공되며, NGINX와 NGINX Ingress Controller를 사용하여 SQL 인젝션을 차단하는 방법을 보여줍니다.

이 튜토리얼을 진행하려면 다음이 설치된 머신이 필요합니다:

  • CPU 2개 이상
  • 2GB의 여유 메모리
  • 20GB의 여유 디스크 공간
  • 인터넷 연결
  • Docker, Hyperkit, Hyper-V, KVM, Parallels, Podman, VirtualBox 또는 VMware Fusion/Workstation과 같은 컨테이너 또는 가상 머신 관리자
  • minikube 설치됨
  • Helm 설치됨
  • 브라우저 창을 실행할 수 있는 구성. 이것이 불가능한 경우 브라우저를 통해 관련 서비스에 액세스할 방법을 찾아야 합니다.

실습과 튜토리얼을 최대한 활용하려면 시작하기 전에 다음 사항을 준비하는 것이 좋습니다:

 

이 튜토리얼에서는 다음 기술을 사용합니다:

각 챌린지의 안내에는 앱을 구성하는 데 사용되는 YAML 파일의 전체 내용이 포함되어 있습니다. 또한 당사의 GitHub 저장소에서 해당 내용을 복사할 수도 있습니다. 각 YAML 파일의 내용과 함께 GitHub 링크가 제공됩니다.

이 튜토리얼에는 네 가지 과제가 포함되어 있습니다:

  1. 클러스터 및 취약한 앱 배포
  2. 앱 해킹하기
  3. NGINX 사이드카 컨테이너를 사용하여 특정 요청 차단
  4. 요청을 필터링하도록 NGINX Ingress Controller 구성

과제 1: 클러스터 및 취약한 앱 배포

이 챌린지에서는 minikube 클러스터를 배포하고 보안 취약점이 있는 샘플 앱으로 Podinfo를 설치합니다.

Minikube 클러스터 생성

Deploy a minikube 클러스터를 배포합니다. 몇 초 후 배포가 완료되었다는 메시지가 표시됩니다.

$ minikube start 
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

취약한 앱 설치

여기서는 두 개의 마이크로서비스로 구성된 간단한 전자상거래 앱을 배포합니다:

  • MariaDB 데이터베이스
  • 데이터베이스에 연결하여 데이터를 가져오는 PHP 마이크로서비스

다음 단계를 수행하세요:

  1. 원하는 텍스트 편집기를 사용하여 다음 내용으로 1-app.yaml이라는 YAML 파일을 만듭니다(또는 GitHub에서 복사합니다).
apiVersion: apps/v1 kind: Deployment 
metadata: 
  name: app 
spec: 
  selector: 
    matchLabels: 
      app: app 
  template: 
    metadata: 
      labels: 
        app: app 
    spec: 
      containers: 
        - name: app 
          image: f5devcentral/microservicesmarch:1.0.3 
          ports: 
            - containerPort: 80 
          env: 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 
            - name: DATABASE_HOSTNAME 
              value: db.default.svc.cluster.local 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: app 
spec: 
  ports: 
    - port: 80 
      targetPort: 80 
      nodePort: 30001 
  selector: 
    app: app 
  type: NodePort 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: db 
spec: 
  selector: 
    matchLabels: 
      app: db 
  template: 
    metadata: 
      labels: 
        app: db 
    spec: 
      containers: 
        - name: db 
          image: mariadb:10.3.32-focal 
          ports: 
            - containerPort: 3306 
          env: 
            - name: MYSQL_ROOT_PASSWORD 
              value: root 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 

--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: db 
spec: 
  ports: 
    - port: 3306 
      targetPort: 3306 
  selector: 
    app: db 
  1. 앱과 API를 배포합니다:
$ kubectl apply -f 1-app.yaml deployment.apps/app created 
service/app created 
deployment.apps/db created 
service/db created 
  1. Podinfo 파드가 배포되었는지 확인합니다. 배포가 완료되면 Running 값이 STATUS 열에 표시됩니다. 완전히 배포되는 데 30~40초가 걸릴 수 있으므로 두 파드의 상태가 모두 Running이 될 때까지 기다린 후 다음 단계로 진행합니다(필요한 경우 명령을 다시 실행합니다).
$ kubectl get podsNAME                  READY   STATUS    RESTARTS   AGE 
app-d65d9b879-b65f2   1/1     Running   0          37s 
db-7bbcdc75c-q2kt5    1/1     Running   0          37s 
  1. 브라우저에서 앱을 엽니다:
$ minikube service app|-----------|------|-------------|--------------| 
| NAMESPACE | NAME | TARGET PORT |     URL      | 
|-----------|------|-------------|--------------| 
| default   | app  |             | No node port | 
|-----------|------|-------------|--------------| 
😿  service default/app has no node port 
🏃  Starting tunnel for service app. 
|-----------|------|-------------|------------------------| 
| NAMESPACE | NAME | TARGET PORT |          URL           | 
|-----------|------|-------------|------------------------| 
| default   | app  |             | http://127.0.0.1:55446 | 
|-----------|------|-------------|------------------------| 
🎉  Opening service default/app in default browser... 

챌린지 2: 앱 해킹하기

샘플 애플리케이션은 비교적 단순합니다. 홈페이지에는 상품(예: 베개) 목록이 표시되며, 각 상품 페이지에서는 설명과 가격 등의 세부 정보를 확인할 수 있습니다. 데이터는 MariaDB 데이터베이스에 저장됩니다. 페이지가 요청될 때마다 데이터베이스에 SQL 쿼리가 실행됩니다.

  • 홈페이지에서는 데이터베이스의 모든 항목을 가져옵니다.
  • 제품 페이지에서는 ID를 기준으로 해당 항목을 가져옵니다.

When you open the pillows 제품 페이지를 열면 URL이 /product/1로 끝나는 것을 확인할 수 있습니다. 여기서 1은 제품의 ID입니다. 악성 코드가 SQL 쿼리에 직접 삽입되는 것을 방지하려면 요청을 백엔드 서비스로 전달하기 전에 사용자 입력을 정제하는 것이 좋습니다. 하지만 앱이 올바르게 구성되지 않아 입력값이 데이터베이스에 대한 SQL 쿼리에 삽입되기 전에 이스케이프 처리되지 않는다면 어떻게 될까요?

익스플로잇 1

앱이 입력값을 올바르게 이스케이프하는지 확인하려면 ID를 데이터베이스에 존재하지 않는 값으로 변경하는 간단한 실험을 해 보세요.

URL의 마지막 요소를 수동으로 1에서 -1로 변경합니다. Invalid product id "-1" 오류 메시지는 제품 ID가 이스케이프 처리되지 않고 문자열이 쿼리에 직접 삽입된다는 뜻입니다. 해커가 아니라면 결코 좋은 일이 아닙니다!

데이터베이스 쿼리가 다음과 같다고 가정해 보겠습니다:

SELECT * FROM some_table WHERE id = "1"

입력값을 이스케이프하지 않아 발생하는 취약점을 악용하려면 1을(를) -1" <malicious_query> -- //(으)로 바꿔 다음과 같이 만듭니다:

  • 따옴표(")는 -1 뒤에서 첫 번째 쿼리를 완성합니다.
  • 따옴표 뒤에 악의적인 쿼리를 직접 추가할 수 있습니다.
  • The -- // 시퀀스는 쿼리의 나머지 부분을 무시합니다.

예를 들어 URL의 마지막 요소를 ‑1" 또는 1 -- //로 변경하면 쿼리는 다음과 같이 컴파일됩니다:

SELECT * FROM some_table WHERE id = "-1" OR 1 -- //" 
                                     -------------- 
                                     ^ injected ^ 

이렇게 하면 데이터베이스의 모든 행이 선택되므로 해킹에 유용합니다. 실제로 그런지 확인하려면 URL 끝부분을 ‑1"로 변경하세요. 그러면 표시되는 오류 메시지에서 데이터베이스에 관한 더 유용한 정보를 얻을 수 있습니다:

Fatal error: Uncaught mysqli_sql_exception: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '"-1""' at line 1 in /var/www/html/product.php:23 Stack trace: #0 /var/www/html/product.php(23): mysqli->query('SELECT * FROM p...') #1 {main} thrown in /var/www/html/product.php on line 23

이제 삽입된 코드를 조작하여 데이터베이스 결과를 ID순으로 정렬해 볼 수 있습니다:

-1" OR 1 ORDER BY id DESC -- //

결과로 데이터베이스의 마지막 항목에 대한 제품 페이지가 표시됩니다.

익스플로잇 2

데이터베이스가 결과를 정렬하도록 강제하는 것은 흥미롭지만, 해킹이 목적이라면 그다지 유용하지는 않습니다. 데이터베이스에서 사용자 이름과 비밀번호를 추출하는 편이 훨씬 더 가치 있습니다.

데이터베이스에 사용자 이름과 비밀번호가 저장된 사용자 테이블이 있다고 봐도 무방합니다. 하지만 제품 테이블에서 사용자 테이블로 접근 범위를 확장하려면 어떻게 해야 할까요?

정답은 다음과 같은 코드를 삽입하는 것입니다:

-1" UNION SELECT * FROM users -- //

여기서

  • ‑1"은 첫 번째 쿼리가 빈 집합을 반환하도록 강제합니다.
  • UNION은 두 데이터베이스 테이블을 강제로 결합합니다. 이 경우에는 products와 users 테이블을 결합하여 원래의 products 테이블에는 없는 정보(비밀번호)를 가져올 수 있게 합니다.
  • SELECT * FROM users는 users 테이블의 모든 행을 선택합니다.
  • 악성 쿼리 뒤의 모든 내용을 무시합니다.-- // 시퀀스는

URL 끝에 삽입된 코드가 오도록 수정하면 새로운 오류 메시지가 표시됩니다:

Fatal error: Uncaught mysqli_sql_exception: The used SELECT statements have a different number of columns in /var/www/html/product.php:23 Stack trace: #0 /var/www/html/product.php(23): mysqli->query('SELECT * FROM p...') #1 {main} thrown in /var/www/html/product.php on line 23

이 메시지는 products 테이블과 users 테이블의 열 개수가 서로 달라서 UNION 명령을 실행할 수 없음을 보여 줍니다. 하지만 열(필드 이름)을 하나씩 SELECT 명령의 매개변수로 추가해 보는 시행착오를 통해 열 개수를 알아낼 수 있습니다. users 테이블의 필드 이름으로 추측해 볼 만한 것은 password이므로, 이를 시도해 보세요:

# select 1 column-1" UNION SELECT password FROM users; -- //
# select 2 columns 
-1" UNION SELECT password,password FROM users; -- //
# select 3 columns 
-1" UNION SELECT password,password,password FROM users; -- /
# select 4 columns 
-1" UNION SELECT password,password,password,password FROM users; -- //
# select 5 columns
-1" UNION SELECT password,password,password,password,password FROM users; -- //

마지막 쿼리가 성공하면(이는 users 테이블에 열이 5개 있다는 뜻입니다) 사용자 비밀번호가 표시됩니다:

이 시점에서는 이 비밀번호에 해당하는 사용자 이름을 알 수 없습니다. 하지만 users 테이블의 열 개수를 알고 있으므로, 이전과 같은 유형의 쿼리를 사용해 해당 정보를 알아낼 수 있습니다. 관련 필드 이름이 username이라고 가정해 보겠습니다. 실제로 이 가정은 맞았으며, 다음 쿼리는 users 테이블에서 사용자 이름과 비밀번호를 모두 노출합니다. 이 앱이 여러분의 인프라에서 호스팅되고 있지만 않다면 아주 좋은 일이죠!

-1" UNION SELECT username,username,password,password,username FROM users where id=1 -- //

과제 3: NGINX 사이드카 컨테이너를 사용하여 특정 요청 차단하기

온라인 스토어 앱 개발자는 사용자 입력을 안전하게 처리하는 데(예: 매개변수화된 쿼리 사용) 더 많은 주의를 기울여야 합니다. 하지만 Kubernetes 엔지니어도 공격이 앱에 도달하지 못하도록 차단하여 SQL 인젝션을 방지하는 데 도움을 줄 수 있습니다. 이렇게 하면 앱에 취약점이 있더라도 그로 인한 위험을 크게 줄일 수 있습니다.

애플리케이션을 보호하는 방법은 다양합니다. 이 실습의 나머지 부분에서는 다음 두 가지 방법에 중점을 둡니다:

  • 이 과제에서는 모든 트래픽을 프록시하고 URL에 사이드카 컨테이너를 파드에 삽입하여 URL에 UNION이 포함된 모든 요청을 거부합니다. 먼저 NGINX Open Source를 사이드카로 배포한 다음 악성 쿼리가 필터링되는지 테스트합니다. 참고: 이 기법은 설명을 위한 용도로만 사용합니다. 실제로 프록시를 사이드카로 직접 배포하는 것은 최선의 해결책이 아닙니다(이에 대해서는 나중에 자세히 설명합니다).
  • In 과제 4에서는 NGINX Ingress Controller를 사용하여 클러스터로 들어오는 모든 트래픽을 필터링합니다.

NGINX Open Source를 사이드카로 배포

  1. 다음 내용으로 YAML 파일을 생성하고 이름을 2-app-sidecar.yaml로 지정합니다(또는 GitHub에서 복사합니다). 구성에서 중요한 부분은 다음과 같습니다.
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: app 
spec: 
  selector: 
    matchLabels: 
      app: app 
  template: 
    metadata: 
      labels: 
        app: app 
    spec: 
      containers: 
        - name: app 
          image: f5devcentral/microservicesmarch:1.0.3 
          ports: 
            - containerPort: 80 
          env: 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 
            - name: DATABASE_HOSTNAME 
              value: db.default.svc.cluster.local 
        - name: proxy # <-- sidecar 
          image: "nginx" 
          ports: 
            - containerPort: 8080 
          volumeMounts: 
            - mountPath: /etc/nginx 
              name: nginx-config 
      volumes: 
        - name: nginx-config 
          configMap: 
            name: sidecar 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: app 
spec: 
  ports: 
    - port: 80 
      targetPort: 8080 # <-- the traffic is routed to the proxy 
      nodePort: 30001 
  selector: 
    app: app 
  type: NodePort 
--- 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: sidecar 
data: 
  nginx.conf: |- 
    events {} 
    http { 
      server { 
        listen 8080 default_server; 
        listen [::]:8080 default_server; 

        location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" { 
            deny all; 
        } 

        location / { 
            proxy_pass http://localhost:80/; 
        } 
      } 
    } 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: db 
spec: 
  selector: 
    matchLabels: 
      app: db 
  template: 
    metadata: 
      labels: 
        app: db 
    spec: 
      containers: 
        - name: db 
          image: mariadb:10.3.32-focal 
          ports: 
            - containerPort: 3306 
          env: 
            - name: MYSQL_ROOT_PASSWORD 
              value: root 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 

--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: db 
spec: 
  ports: 
    - port: 3306 
      targetPort: 3306 
  selector: 
    app: db
  • NGINX Open Source를 실행하는 사이드카 컨테이너가 포트 8080에서 시작됩니다.
  • NGINX는 모든 트래픽을 앱으로 전달합니다.
  • 다른 문자열 중에서도 SELECT 또는 UNION이 포함된 모든 요청은 거부됩니다(ConfigMap 섹션의 첫 번째 location 블록 참조).
  • 앱의 서비스는 모든 트래픽을 먼저 NGINX 컨테이너로 라우팅합니다.
  1. 사이드카를 배포합니다:
$ kubectl apply -f 2-app-sidecar.yaml deployment.apps/app configured 
service/app configured 
configmap/sidecar created 
deployment.apps/db unchanged 
service/db unchanged 

사이드카를 필터로 테스트하기

앱으로 돌아가 SQL 인젝션을 다시 시도하여 사이드카가 트래픽을 필터링하는지 테스트합니다. NGINX는 요청이 앱에 도달하기 전에 차단합니다!

-1" UNION SELECT username,username,password,password,username FROM users where id=1 -- // 

과제 4: 요청을 필터링하도록 NGINX Ingress Controller 구성

다음과 같이 앱을 보호하는 것은 과제 3 교육적인 경험으로서는 흥미롭지만, 다음과 같은 이유로 프로덕션 환경에서는 권장하지 않습니다:

  • 완전한 보안 솔루션이 아닙니다.
  • 확장성이 없습니다(이 보호 기능을 여러 앱에 쉽게 적용할 수 없습니다).
  • 업데이트가 복잡하고 비효율적입니다.

훨씬 더 나은 해결책은 NGINX Ingress Controller를 사용하여 동일한 보호 기능을 모든 애플리케이션으로 확장하는 것입니다! Ingress Controller를 사용하면 웹 애플리케이션 방화벽(WAF)처럼 요청을 차단하는 기능부터 인증 및 권한 부여에 이르기까지 다양한 보안 기능을 중앙에서 관리할 수 있습니다.

이 챌린지에서는 NGINX Ingress Controller를 배포하고, 트래픽 라우팅을 구성한 후, 필터가 SQL 인젝션을 차단하는지 확인합니다.

NGINX Ingress Controller 배포

NGINX Ingress Controller를 설치하는 가장 빠른 방법은 Helm을 사용하는 것입니다.

  1. Helm에 NGINX 리포지토리를 추가합니다:
$ helm repo add nginx-stable https://helm.nginx.com/stable 
  1. NGINX Open Source 기반 NGINX Ingress Controller를 다운로드하여 설치합니다. 이 컨트롤러는 F5 NGINX에서 유지 관리합니다. enableSnippets=true 매개변수에 유의하세요. 스니펫은 SQL 삽입 공격을 차단하도록 NGINX를 구성하는 데 사용됩니다. 출력의 마지막 줄에서 설치가 성공적으로 완료되었음을 확인할 수 있습니다.
$ helm install main nginx-stable/nginx-ingress \ --set controller.watchIngressWithoutClass=true --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30005 \ --set controller.enableSnippets=true
NAME: main  
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY  
NAMESPACE: default  
STATUS: deployed  
REVISION: 1  
TEST SUITE: None  
NOTES: The NGINX Ingress Controller has been installed.  
  1. NGINX Ingress Controller Pod가 배포되었는지 확인합니다. 배포된 경우 Running 값이 STATUS 열에 표시됩니다.
$ kubectl get pods   NAME                                READY   STATUS  ...
main-nginx-ingress-779b74bb8b-mtdkr 1/1     Running ...

      ... RESTARTS   AGE 
      ... 0          18s

애플리케이션으로 트래픽 라우팅

  1. 다음 내용으로 3-ingress.yaml이라는 YAML 파일을 생성합니다(또는 GitHub에서 복사). 이 파일은 앱으로 트래픽을 라우팅하는 데 필요한 Ingress 매니페스트를 정의합니다(이번에는 사이드카 프록시를 통하지 않음). annotations: 블록에서는 스니펫을 사용하여 NGINX Ingress Controller 구성을 사용자 지정합니다. 이 스니펫에는 챌린지 3의 ConfigMap 정의와 동일한 location 블록이 포함되어 있으며, SELECT 또는 UNION 등의 문자열이 포함된 모든 요청을 거부합니다.
apiVersion: v1 kind: Service 
metadata: 
  name: app-without-sidecar 
spec: 
  ports: 
    - port: 80 
      targetPort: 80 
  selector: 
    app: app 
--- 
apiVersion: networking.k8s.io/v1 
kind: Ingress 
metadata: 
  name: entry 
  annotations: 
    nginx.org/server-snippets: | 
      location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" { 
          deny all; 
      } 
spec: 
  ingressClassName: nginx 
  rules: 
    - host: "example.com" 
      http: 
        paths: 
          - backend: 
              service: 
                name: app-without-sidecar 
                port: 
                  number: 80 
            path: / 
            pathType: Prefix 
  1. Ingress 리소스를 배포합니다:
$ kubectl apply -f 3-ingress.yaml service/app-without-sidecar created 
ingress.networking.k8s.io/entry created 

필터 작동 확인

  1. 일회용 BusyBox 컨테이너를 실행하여 올바른 호스트 이름으로 NGINX Ingress Controller 파드에 요청을 보냅니다.
$ kubectl run -ti --rm=true busybox --image=busybox$ wget --header="Host: example.com" -qO- main-nginx-ingress 
<!DOCTYPE html> 
<html lang="en"> 

<head> 
# ...
  1. SQL 인젝션을 시도합니다. 403 Forbidden 상태 코드를 통해 NGINX가 공격을 차단했음을 확인할 수 있습니다! 
$ wget --header="Host: example.com" -qO- 'main-nginx-ingress/product/-1"%20UNION%20SELECT%20username,username,password,password,username%20FROM%20users%20where%2 0id=1%20--%20//' 
wget: server returned error: HTTP/1.1 403 Forbidden 

다음 단계

Kubernetes는 기본적으로 안전하지 않습니다. Ingress 컨트롤러를 사용하면 SQL 인젝션을 비롯한 여러 취약점을 완화할 수 있습니다. 하지만 방금 NGINX Ingress Controller로 구현한 WAF와 유사한 기능은 실제 WAF를 대체할 수 없으며, 애플리케이션을 안전하게 설계하는 것을 대신할 수도 없습니다. 숙련된 해커라면 코드를 조금만 변경해도 UNION 공격이 여전히 작동하도록 만들 수 있습니다. 이 주제에 대한 자세한 내용은 침투 테스터를 위한 SQL 인젝션(SQLi) 가이드를 참조하세요.

그렇지만 Ingress 컨트롤러는 여전히 대부분의 보안 기능을 중앙 집중화할 수 있는 강력한 도구로, 중앙 집중식 인증 및 권한 부여 사용 사례(mTLS, 싱글 사인온)는 물론 F5 NGINX App Protect WAF와 같은 강력한 WAF까지 지원하여 효율성과 보안을 향상합니다.

애플리케이션과 아키텍처가 복잡한 경우 더욱 세밀한 제어가 필요할 수 있습니다. 조직에 제로 트러스트와 종단 간 암호화가 필요하다면, Kubernetes 클러스터 내 서비스 간 통신(동서 트래픽)을 제어하기 위해 항상 무료로 제공되는 F5 NGINX Service Mesh와 같은 서비스 메시를 고려해 보세요. 서비스 메시는 4단원 고급 Kubernetes 배포 전략에서 살펴봅니다.

NGINX Open Source를 다운로드하고 배포하는 방법에 대한 자세한 내용은 nginx.org에서 확인하세요.

NGINX App Protect가 포함된 NGINX Plus 기반 NGINX Ingress Controller를 사용해 보려면 지금 30일 무료 평가판을 시작하거나 사용 사례에 대해 논의하려면 문의해 주세요.

NGINX Open Source 기반 NGINX Ingress Controller를 사용해 보려면 GitHub 리포지토리에서 NGINX Ingress Controller 릴리스를 확인하거나 DockerHub에서 사전 빌드된 컨테이너를 다운로드하세요.


공유

관련 블로그 게시물

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
NGINX | 2023. 11. 30.

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric

이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
NGINX | 2023. 03. 23.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법

앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
NGINX | 2021. 09. 27.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션

개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX | 2021. 07. 06.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법

NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX | 2021. 06. 30.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리

NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
NGINX | 2015. 03. 25.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유

Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.