← 홈페이지 5강 목록으로
🔒
EPISODE 04
TLS · helmet · .env · 시크릿 유출 대응

HTTPS와 환경변수 관리

HTTP vs HTTPS와 TLS 인증서, Let's Encrypt 무료 발급, helmet으로 보안 헤더 일괄 설정, .env로 시크릿 안전 관리, .env.example 패턴, 그리고 시크릿 유출 시 즉시 대응법까지.

HTTPSTLShelmetdotenv
소요 시간
60분
난이도
📊 중급~고급
선수 조건
🎯 sec-03
결과물
HTTPS와 보안 헤더로 보호된 운영 가능한 Express 앱

이 강의에서 배우는 것

  • 1HTTP/HTTPS와 TLS Handshake의 흐름을 안다
  • 2Let's Encrypt + Certbot 으로 무료 인증서를 발급한다
  • 3HSTS / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / CSP를 설정한다
  • 4helmet 미들웨어로 한 번에 적용한다
  • 5.env로 시크릿을 관리하고 .gitignore + .env.example 패턴을 따른다
  • 6시크릿이 유출됐을 때 즉시 무효화·히스토리 정리·예방을 한다

1. HTTP vs HTTPS

text
HTTP  (평문):  클라이언트 ── 비밀번호:abc123 ─> 서버
                              ↑ 중간자가 읽을 수 있음

HTTPS (암호화): 클라이언트 ── 5h#@x9!K3m... ──> 서버
                              ↑ 암호화 — 해독 불가

TLS Handshake: 서버가 인증서(공개키) 전송 → CA 검증 → 대칭키 안전하게 교환 → 이후 통신은 대칭키로 암호화.

Let's Encrypt — 무료 인증서

bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com

# 자동 갱신 (90일 만료)
sudo certbot renew --dry-run

2. 보안 HTTP 헤더

javascript
app.use((req, res, next) => {
  // HTTPS만 허용 (1년)
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

  // 클릭재킹 방지 (iframe 삽입 차단)
  res.setHeader('X-Frame-Options', 'SAMEORIGIN');

  // MIME 스니핑 방지
  res.setHeader('X-Content-Type-Options', 'nosniff');

  // 참조 URL 정보 제한
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');

  // 리소스 출처 제한 (XSS 방어 보조)
  res.setHeader('Content-Security-Policy', "default-src 'self'");

  next();
});

3. helmet (권장)

bash
npm install helmet
javascript
const helmet = require('helmet');

// 모든 보안 헤더 한 번에
app.use(helmet());

// 또는 개별 설정
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc:  ["'self'", "https://cdn.example.com"],
    },
  },
  hsts: { maxAge: 31536000, includeSubDomains: true },
}));

4. 환경변수로 시크릿 관리

절대 하지 말 것

javascript
// ✗ 하드코딩
const DB_PASSWORD = 'my_secret_password';
const JWT_SECRET = 'jwt-secret-key-123';
const API_KEY = 'sk-abcdefghijk12345';

// ✗ package.json에도 금지
{
  "scripts": {
    "start": "DB_PASSWORD=secret node app.js"
  }
}

.env

text
DATABASE_URL=mongodb+srv://user:pass@cluster.mongodb.net/db
JWT_SECRET=매우긴랜덤문자열여기에
API_KEY=sk-abcdefghijk12345
SESSION_SECRET=또다른랜덤문자열
PORT=3000
javascript
require('dotenv').config();

mongoose.connect(process.env.DATABASE_URL);

const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET) {
  console.error('JWT_SECRET이 설정되지 않았습니다!');
  process.exit(1);  // 시크릿 없으면 서버 시작 거부
}

5. .gitignore + .env.example

text
# .gitignore
.env
.env.local
.env.production
*.pem          # SSL 인증서
node_modules/
text
# .env.example (Git에 포함 — 실제 값 없이 키만)
DATABASE_URL=your_mongodb_connection_string
JWT_SECRET=generate_a_random_string_here
API_KEY=your_api_key_here
SESSION_SECRET=another_random_string
PORT=3000

6. 시크릿 유출 대응

즉시 해야 할 일

  1. 즉시 키 무효화 — API 키, 비밀번호 즉시 변경
  2. Git 히스토리 정리 — 과거 커밋에서 시크릿 제거
  3. 피해 평가 — 유출된 키로 무엇을 할 수 있었는지
bash
# 히스토리에서 시크릿 파일 제거
git filter-branch --force --index-filter \
    'git rm --cached --ignore-unmatch .env' \
    --prune-empty --tag-name-filter cat -- --all

# 또는 BFG Repo Cleaner (더 안전)
bfg --delete-files .env

예방

bash
# pre-commit hook으로 커밋 전 검사
npm install -g git-secrets
git secrets --install
git secrets --register-aws
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗