DevOps/Security

개발자를 위한 회사 보안 검사 대비하기 - 하드코딩 없애고 안전한 인증 시스템 구축하는 법

포도쿵야 2026. 4. 23. 10:03

🚨 이런 경험 있으신가요?

DB 비밀번호를 코드에 그대로 박아두셨나요?
.env 파일이 Git에 올라간 적 있으신가요?
회사 보안 검사에서 이런 부분들이 적발되면 프로젝트가 중단될 수 있습니다.

이 글에서는 실무에서 바로 적용 가능한 안전한 인증 시스템 구축 방법을 다룹니다.


📌 목차

  1. 현재 개발 현장의 문제점
  2. 인증 솔루션 비교 - 어떤 걸 선택해야 할까?
  3. Vault 인증 방식 비교
  4. Vault란 무엇인가?
  5. Python에서 Vault 도입하기
  6. 실무 적용 예제

1️⃣ 현재 개발 현장의 문제점

😱 일반적인 개발 현장의 모습

# 하드코딩 방식
DB_HOST = "192.168.1.100"
DB_USER = "admin"
DB_PASSWORD = "P@ssw0rd123!"  # 😱 코드에 그대로 노출

# .env 파일 방식
DB_PASSWORD=P@ssw0rd123!
API_KEY=sk-1234567890abcdef
JWT_SECRET=my-super-secret-key

⚠️ 이 방식이 위험한 이유

1) 코드 유출 = 비밀번호 유출

  • Git에 실수로 커밋
  • 퇴사자의 로컬 저장소
  • 협력사/외주 개발자 공유

2) 암호화의 함정

  • 암호화 키는 어디에 저장? → 결국 같은 문제 반복
  • "암호화 키를 암호화하는 키"의 무한 반복

3) 권한 관리 불가능

  • 서비스 A는 DB만, 서비스 B는 API만 접근해야 하는데
  • 모든 서비스가 모든 정보에 접근 가능

4) 감사 추적 불가

  • 누가, 언제, 어떤 비밀번호를 사용했는지 모름

2️⃣ 인증 솔루션 비교 - 어떤 걸 선택해야 할까?

🔍 주요 솔루션 한눈에 비교

솔루션 특징 장점 단점 추천 상황
Spring Security 코드 내장형 완전한 커스터마이징 구현 복잡도 높음 백엔드 직접 개발
Keycloak 인증 서버 분리 SSO, 표준 준수 서버 운영 필요 여러 서비스 통합
Auth0 SaaS 외주형 빠른 구현 비용, 외부 의존 스타트업 빠른 출시
Vault 비밀 관리 중심 Secret 관리 특화 인증은 부가 기능 민감 정보 관리

💡 솔루션별 역할 정리

Spring Security → 사용자 로그인 처리
Keycloak       → 통합 인증 서버 (SSO)
Vault          → 비밀 정보 관리 (DB 비밀번호, API 키)

👉 이들은 함께 사용 가능! (상호보완)

🎯 상황별 추천

단일 서비스, 직접 구현하고 싶다면
Spring Security

여러 서비스에서 통합 로그인 필요
Keycloak

개발 시간 단축, 비용 OK
Auth0

DB 비밀번호, API 키 등 민감 정보 관리
Vault (이 글의 주제)


3️⃣ Vault 인증 방식 비교

🔐 Vault에서 지원하는 인증 방식들

방식 적용 대상 장점 단점 언제 쓸까?
Token 개발/테스트 단순함 보안 취약 로컬 개발용
AppRole 서버 앱 머신 인증 최적 SecretID 배포 필요 ⭐ 일반 서버/VM
Kubernetes K8s Pod 자동 인증 가능 K8s 환경 필수 ⭐ 쿠버네티스 환경
JWT/OIDC SSO 연동 기존 인증 활용 설정 복잡 SSO 연동 필요 시
LDAP 운영자 사내 계정 연동 앱 인증 부적합 운영자 로그인용
더보기

💡 각 인증 방식 쉽게 이해하기

1️⃣ Token 방식

"이미 발급받은 출입증으로 바로 들어가기"

 
 
python
# Vault가 미리 만들어준 토큰
vault_token = "s.1234567890abcdef"

# 바로 접속 (로그인 과정 없음)
client = hvac.Client(url='http://vault:8200', token=vault_token)

언제 쓰나요?

  • 로컬 개발할 때
  • 빠른 테스트할 때

단점:

  • 토큰 유출되면 끝
  • 운영 환경에서는 위험

2️⃣ AppRole 방식 (⭐ 가장 많이 씀)

"앱 전용 ID/PW로 로그인하기"

 
 
python
# 앱의 신분증
role_id = "abc-123-def-456"      # 공개 가능
secret_id = "xyz-789-ghi-012"    # 비공개

# 로그인해서 토큰 받기
client.auth.approle.login(role_id=role_id, secret_id=secret_id)

비유:

  • RoleID = 사원증 번호 (누가 봐도 됨)
  • SecretID = 사원증 비밀번호 (절대 비공개)

언제 쓰나요?

  • Python 서버, Java 서버 등
  • VM이나 일반 서버에서 실행되는 앱

3️⃣ Kubernetes 방식

"쿠버네티스가 자동으로 신분 증명해주기"

 
 
python
# Pod 안에서는 자동으로 인증 정보가 있음
# 따로 ID/PW 안 넣어도 됨!

client.auth.kubernetes.login(
    role='my-app-role',
    jwt=open('/var/run/secrets/kubernetes.io/serviceaccount/token').read()
)

비유:

  • 회사 건물(쿠버네티스) 안에 있으면 자동으로 출입 가능
  • 따로 사원증 챙길 필요 없음

언제 쓰나요?

  • Docker 컨테이너로 실행
  • Kubernetes(K8s) 환경

4️⃣ JWT/OIDC 방식

"구글/회사 계정으로 로그인하기"

 
 
python
# 구글/깃허브/회사 SSO 로그인 토큰으로 인증
jwt_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

client.auth.jwt.login(
    role='my-role',
    jwt=jwt_token
)

비유:

  • "구글 계정으로 로그인" 버튼 누르는 것
  • 이미 로그인한 정보 재활용

언제 쓰나요?

  • 회사에 SSO(통합 로그인)가 있을 때
  • GitHub Actions, GitLab CI 같은 곳에서

5️⃣ LDAP 방식

"회사 계정(Active Directory)으로 로그인"

 
 
python
# 회사 이메일/비밀번호로 로그인
client.auth.ldap.login(
    username='hong.gildong@company.com',
    password='my-company-password'
)

비유:

  • 회사 PC 켤 때 쓰는 그 계정

언제 쓰나요?

  • 운영자가 Vault UI에 접속할 때
  • 사람이 직접 로그인할 때
  • 앱 자동 인증에는 부적합

📌 권장 선택 기준

일반 서버/VM에서 Python 앱 실행
AppRole (가장 무난)

Kubernetes 환경
Kubernetes Auth

운영자가 UI 접근
OIDC 또는 LDAP

개발/테스트 환경
Token (임시로만)


4️⃣ Vault란 무엇인가?

🎯 핵심 개념

Vault는 "비밀번호 저장소"가 아니라 "비밀번호를 없애는 시스템"입니다.

❌ 기존 방식: 앱 안에 비밀 정보 포함 → 유출 시 끝

✅ Vault 방식: 비밀은 Vault에만, 앱은 필요할 때만 요청
              → 유출돼도 제한적, 추적 가능

🔄 Vault 동작 구조

┌─────────────┐
│ Python 앱   │ ─────► 1. 로그인 요청 (RoleID + SecretID)
└─────────────┘
                       ▼
              ┌─────────────────┐
              │  Vault Server   │
              └─────────────────┘
                       │
                       │ 2. 인증 성공 → Token 발급
                       │
                       ▼
┌─────────────┐
│ Python 앱   │ ◄───── 3. Secret 반환 (Token으로 요청)
└─────────────┘

              ┌─────────────────┐
              │  DB 비밀번호     │
              │  API Key        │
              │  암호화 키       │
              └─────────────────┘

💎 Vault의 핵심 기능

1) 정적 Secret 관리

  • 현재 사용 중인 비밀번호를 안전하게 보관
  • 버전 관리 지원 (잘못 바꿨을 때 롤백 가능)

2) 동적 Secret 생성 ⭐ 핵심!

  • 필요할 때마다 임시 DB 계정 생성
  • 1시간 뒤 자동 삭제
  • 유출돼도 의미 없음

3) 권한 관리 (Policy)

  • 서비스별로 접근 가능한 Secret 제한
  • 최소 권한 원칙 적용

4) 감사 로그

  • 누가, 언제, 무엇을 가져갔는지 기록
  • 보안 사고 시 추적 가능

5️⃣ Python에서 Vault 도입하기

📝 현재 코드 (Before)

import os
from dotenv import load_dotenv

load_dotenv()
DB_PASSWORD = os.getenv('DB_PASSWORD')  # .env에서 읽음

⚙️ Step 1: Vault 서버 준비

# Docker로 개발용 Vault 실행
docker run -d --name=vault \
  -p 8200:8200 \
  --cap-add=IPC_LOCK \
  -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' \
  vault

export VAULT_ADDR='http://localhost:8200'
vault login myroot

📦 Step 2: Secret 저장

vault kv put secret/myapp/db \
  host="db.internal" \
  port="5432" \
  username="myuser" \
  password="MySecurePassword123!"

🔑 Step 3: AppRole 설정

# AppRole 인증 활성화
vault auth enable approle

# Role 생성
vault write auth/approle/role/myapp \
  secret_id_ttl=24h \
  token_ttl=1h \
  token_max_ttl=4h \
  policies="myapp-policy"

# RoleID 확인
vault read auth/approle/role/myapp/role-id

# SecretID 발급
vault write -f auth/approle/role/myapp/secret-id

🐍 Step 4: Python 코드 수정 (After)

import hvac
import os

class VaultClient:
    def __init__(self):
        self.client = hvac.Client(url=os.getenv('VAULT_ADDR'))
        self._authenticate()

    def _authenticate(self):
        """AppRole로 로그인"""
        role_id = os.getenv('VAULT_ROLE_ID')
        secret_id = os.getenv('VAULT_SECRET_ID')

        self.client.auth.approle.login(
            role_id=role_id,
            secret_id=secret_id
        )

    def get_db_credentials(self):
        """DB 접속 정보 가져오기"""
        secret = self.client.secrets.kv.v2.read_secret_version(
            path='myapp/db',
            mount_point='secret'
        )
        return secret['data']['data']

# 사용 예시
vault = VaultClient()
db_config = vault.get_db_credentials()

DB_HOST = db_config['host']
DB_PORT = db_config['port']
DB_USER = db_config['username']
DB_PASSWORD = db_config['password']

🌍 Step 5: 환경변수 정리

# .env 파일 (After) - 민감 정보 제거됨!
VAULT_ADDR=https://vault.company.com
VAULT_ROLE_ID=abc-123-def-456
# VAULT_SECRET_ID는 배포 시스템에서 안전하게 주입

6️⃣ 실무 적용 예제

🎯 시나리오 1: FastAPI + PostgreSQL (동적 계정)

from fastapi import FastAPI
from sqlalchemy import create_engine
import hvac

app = FastAPI()

class VaultDB:
    def __init__(self):
        self.vault = hvac.Client(url='https://vault.company.com')
        self.vault.auth.approle.login(
            role_id=ROLE_ID,
            secret_id=SECRET_ID
        )

    def get_engine(self):
        # Vault가 즉시 생성하는 임시 DB 계정
        creds = self.vault.read('database/creds/myapp-role')

        username = creds['data']['username']  # v-approle-abc123
        password = creds['data']['password']  # 랜덤 생성

        db_url = f"postgresql://{username}:{password}@{DB_HOST}/{DB_NAME}"

        return create_engine(db_url)

vault_db = VaultDB()
engine = vault_db.get_engine()

장점:

  • 유출돼도 1시간 후 자동 삭제
  • 각 앱마다 다른 계정 사용
  • 접근 로그 추적 가능

🌐 시나리오 2: 외부 API 키 관리

import requests

def call_external_api():
    # API 키를 Vault에서 가져오기
    secret = vault_client.secrets.kv.v2.read_secret_version(
        path='external/api-keys'
    )

    api_key = secret['data']['data']['openai_key']

    response = requests.post(
        'https://api.openai.com/v1/chat/completions',
        headers={'Authorization': f'Bearer {api_key}'},
        json=payload
    )

    return response.json()

✅ 마치며

보안은 "귀찮은 규제"가 아니라 "시스템의 신뢰성을 높이는 투자"입니다.

Vault와 같은 도구를 도입하면:

  • ✅ 개발자는 안전하게 개발
  • ✅ 운영팀은 안심하고 운영
  • ✅ 회사는 규정 준수

모두가 win-win할 수 있습니다.

처음엔 복잡해 보이지만, 한 번 구축하면 장기적으로 훨씬 안전하고 관리하기 쉬운 시스템을 운영할 수 있습니다.


📚 참고 자료


#Vault #보안 #Python #DevSecOps #Secret관리 #AppRole #보안검사 #Keycloak #인증시스템 #하드코딩