10. 보안 & 시스템 설계 기초

규미·2025년 11월 22일

CS-study

목록 보기
10/10

1. 해시 vs 암호화

  • 해시(Hash)
    • 입력(메시지)을 고정 길이의 출력(해시값)으로 변환. 같은 입력은 항상 같은 출력, 아주 작은 입력 변경도 출력 크게 변화.
    • 단방향 함수: 원래 메시지로 복원할 수 없음(복호화 불가).
    • 용도: 비밀번호 저장(솔트와 함께), 무결성 검사(파일 무결성, 서명 전 해시), 해시 키(해시맵, 캐싱) 등.
    • 주의: 단순 해시는 충돌(다른 입력이 같은 해시값)이 가능하므로 민감정보(비밀번호)는 반드시 솔트+비밀번호 해싱 알고리즘 사용.
  • 암호화(Encryption)
    • 데이터를 다른 형태로 변환하여 복원 가능하게 만드는 과정. 복호화(Decrypt) 가능.
    • 대칭키 암호화(AES 등): 암/복호화에 같은 키 사용. 빠르고 대용량 데이터 암호화에 적합.
    • 비대칭키 암호화(RSA 등): 공개키(Public Key)로 암호화, 개인키(Private Key)로 복호화 — 키 분배 문제를 해결하고, 디지털 서명에 사용.

2. 주요 알고리즘 원리

SHA (SHA-1, SHA-2, SHA-3)

  • 분류: 해시 함수(단방향).
  • 특징:
    • SHA-1은 취약(충돌 가능) → 사용 권장하지 않음.
    • SHA-2(SHA-256 등), SHA-3는 현재 널리 사용.
  • 용도: 데이터 무결성, 디지털 서명의 메시지 다이제스트, HMAC 등.
  • 주의: 비밀번호 저장에는 단순 SHA-256만 사용하지 말 것 — 반드시 PBKDF2 / bcrypt / scrypt / Argon2 같은 키 스트레칭 알고리즘 + 솔트 사용.

AES (Advanced Encryption Standard)

  • 분류: 대칭키 블록 암호. (주로 AES-128/192/256)
  • 원리(요약): 고정 블록(128비트)을 여러 라운드로 치환·섞기(서브바이트, 시프트행, 믹스컬럼 등).
  • 운영모드: ECB(비권장), CBC, GCM(인증 암호화: 암호화 + 무결성) 등.
  • 실무 팁: 데이터 암호화 시 GCM 같은 인증 모드를 써서 무결성(탬퍼 여부)도 확보. 키 관리(KMS)를 꼭 고려.

RSA (Rivest–Shamir–Adleman)

  • 분류: 비대칭 암호화(공개키/개인키)
  • 원리(요약): 큰 소수의 곱과 모듈러 산술의 어려움을 기반으로 함. 공개키로 암호화 → 개인키로 복호화. 또한 개인키로 서명 → 공개키로 검증.
  • 용도: 소규모 데이터 암호화(실무에서는 대칭 키(예: AES 키)를 RSA로 암호화 해서 전송), 디지털 서명, TLS 핸드쉐이크(대칭키 교환) 등.
  • 실무 팁: RSA는 큰 데이터 암호화에 비효율적(성능/사이즈). 최신 시스템은 RSA 대신 ECDSA/ECDH(타원곡선 알고리즘)를 사용하기도 함.

3. 인증(Authentication) vs 인가(Authorization)

  • 인증(Authentication): 사용자가 누구인지 확인하는 과정(로그인).
  • 인가(Authorization): 인증된 사용자가 특정 리소스/행위를 할 권한이 있는지 판단(권한 체크).

둘은 서로 보완적이며 보안 설계에서 둘 모두 올바르게 구현되어야 합니다.

세션(Session)과 쿠키(Cookie)

  • 쿠키: 클라이언트(브라우저)에 저장되는 작은 키-값. 세션 ID, 토큰 등을 저장하는 데 사용.
    • 설정 고려사항: HttpOnly, Secure, SameSite 속성으로 XSS/CSRF 완화.
  • 세션: 서버가 사용자 상태(로그인 상태 등)를 관리하는 방법. 흔히 세션 ID를 쿠키로 클라이언트에 넣고, 서버는 세션 스토어(Redis 등)에 상태를 저장.
    • 장점: 토큰 탈취 시 서버에서 바로 무효화 가능.
    • 단점: 서버 상태(스케일링/세션 동기화) 부담.

예시 흐름 (쿠키+세션)

  1. 사용자 로그인 → 서버는 세션 생성(서버 메모리/Redis에 저장), 세션 ID를 쿠키로 발급.
  2. 브라우저는 이후 요청에 쿠키 포함. 서버는 세션 ID로 세션을 조회해 인증 확인.

JWT (JSON Web Token)

  • 구성: header.payload.signature (Base64Url 인코딩)
  • 특징:
    • 자체 포함(self-contained): 토큰 안에 사용자 정보(클레임)를 포함 → 서버에서 별도 세션 저장소 없이 인증 가능.
    • 서명(signature)을 검증해서 변조 여부 확인. 서명은 대칭(HS256) 또는 비대칭(RS256) 방식 사용.
    • 토큰 자체에 만료(exp) 클레임 포함 가능.
  • 장점: 무상태(stateless) API에 적합, 확장성 좋음(서버 간 세션 동기화 필요 없음).
  • 단점 & 주의:
    • 토큰 탈취 시 만료까지 유효 → 리프레시 토큰, 짧은 액세스 토큰 만료 시간, 토큰 블랙리스트(또는 토큰 버전링)로 대처 필요.
    • 토큰 재사용 공격, XSS 취약점에 의한 토큰 탈취 위험 — HttpOnly 쿠키로 저장하거나 스토리지 사용 시 주의 필요.
    • 중요한 권한 변경 시(예: 관리자 권한 회수) 서버에서 즉시 반영하려면 추가 관리 필요.

JWT 발급/검증 (간단 예시, Node.js 스타일 - 의사코드)

// 발급
const payload = { sub: userId, role: 'user', iat: now, exp: now + 15*60 }; // 15분
const token = jwt.sign(payload, SECRET_KEY, { algorithm: 'HS256' });

// 검증 (미들웨어)
try {
  const payload = jwt.verify(token, SECRET_KEY);
  req.user = payload;
} catch (err) {
  // 토큰 만료/변조 처리
}

OAuth2 (및 OpenID Connect)

  • 목적: 제3자(클라이언트)가 리소스 소유자(사용자)의 자원에 접근하도록 “권한”(Authorization)을 부여하게 해주는 표준.
  • 흐름(대표적):
    • Authorization Code Grant: 서버사이드 앱에서 안전하게 사용. 클라이언트가 인증서버(예: Google)로부터 authorization code를 받고, 서버가 이를 토큰으로 교환.
    • Implicit, Resource Owner Password, Client Credentials 등 여러 흐름이 있으나 보안성에 따라 선호도가 다름.
  • OpenID Connect (OIDC): OAuth2 위에 인증(Authentication)을 위한 표준을 덧붙임(아이디 토큰 ID Token 제공).
  • 실무 포인트:
    • 외부 로그인(구글, 카카오 등)은 OAuth2/OIDC 사용.
    • 브라우저 앱(싱글 페이지 앱)은 Authorization Code + PKCE(Proof Key for Code Exchange)를 권장.

4. 시스템 설계 예시: URL Shortener

요구사항(요약)

  • 기능: 긴 URL을 짧게 변환, 원래 URL로 리다이렉트
  • 비기능: 초당 트래픽(예: 1000 RPS) 대응, 충돌 없음(또는 충돌 최소화), 통계(클릭 수), 단축 URL 예측 방지

기본 컴포넌트

  • API 서버 (POST /shorten, GET /{short})
  • 데이터베이스: RDB(MySQL) 또는 NoSQL(Cassandra)
  • 캐시: Redis (빠른 리다이렉션, 통계 집계)
  • 단축키 생성 서비스: 해시/베이스 인코딩 또는 고유 ID 기반

데이터 모델 (간단)

Table urls (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  short_key VARCHAR(10) UNIQUE,
  original_url TEXT,
  created_at TIMESTAMP,
  expire_at TIMESTAMP NULL,
  owner_id BIGINT NULL
);
Table clicks (
  id BIGINT AUTO_INCREMENT,
  url_id BIGINT,
  created_at TIMESTAMP,
  ip VARCHAR(45),
  user_agent TEXT
);

short_key 생성 전략

  1. Auto-increment ID -> Base62 인코딩
    • 장점: 간단, 충돌 없음.
    • 단점: 예측 가능(쉽게 유추 가능). 해결: ID에 random salt/offset 적용 또는 다른 매핑 사용.
  2. 랜덤 문자열(충돌 체크)
    • 장점: 예측 어려움.
    • 단점: 충돌 가능성(짧을수록 충돌 확률↑) → 충돌 시 재시도.
  3. Hash(original_url) 일부 사용 + 충돌 해결
    • 충돌을 DB에서 검사하고 충돌 시 다른 방식 적용.

API 예시

  • POST /shorten 요청: { "url": "https://example.com/long/...", "expire_in_days": 30 } 응답: { "short_url": "https://s.example/abc123" }
  • GET /{short_key} 동작: 캐시 -> DB 조회 -> 클릭 카운트(비동기 처리 가능) -> 301 리다이렉트

성능·확장 전략

  • 캐시 우선: 자주 요청되는 short_key는 Redis에 원본 URL 캐시.
  • CDN/엣지 리다이렉트: 초고속 리다이렉트를 위해 엣지에 단축키 매핑을 배포 가능.
  • 통계 집계: 클릭 로깅은 메시지 큐(Kafka)로 비동기 전송 후 배치 처리로 집계.
  • 데이터 파티셔닝: DB 쓰기 병목 시 파티셔닝 또는 샤딩 적용.

보안 고려사항

  • 악성 URL(피싱, 악성코드) 필터링(체크 서비스 연동)
  • API rate limiting(브루트포스/대량 단축 방지)
  • short_key 예측 방지: 충분히 랜덤하고 길이 설정
  • 개인 정보: 원본 URL에 민감 정보 포함 시 저장/보관 정책

5. 시스템 설계 예시: 채팅 시스템 (실시간 메시징)

요구사항(요약)

  • 1:1 채팅, 그룹 채팅 지원
  • 실시간 전달(근실시간), 메시지 영속화(대화 기록), 읽음/전달 상태, 오프라인 푸시, 확장성

주요 컴포넌트

  • 클라이언트: 웹(브라우저) — WebSocket, 모바일 앱 — WebSocket or native socket
  • API 서버: 인증, 대화방 관리, 메시지 저장
  • Message Broker / Pub-Sub: Redis Pub/Sub, Kafka, RabbitMQ 등 (서버 간 메시지 분배)
  • WebSocket Gateway: 연결 유지, 메시지 라우팅(사용자 연결 정보 매핑)
  • DB: 메시지 저장(예: PostgreSQL, Cassandra)
  • Presence Service: 누가 온라인인지 관리(예: Redis)
  • Push Service: APNs/FCM 연동 (오프라인 사용자에게 푸시)

메시지 흐름(요약)

  1. 클라이언트 ↔ WebSocket 연결(토큰으로 인증)
  2. A가 메시지 전송 → WebSocket Gateway 수신 → 메시지 검증/저장 → Broker로 게시
  3. Broker가 해당 채팅을 구독 중인 접속 서버들에 메시지 배포
  4. 구독 서버는 연결된 클라이언트에게 메시지 푸시(또는 오프라인이면 Push Service로 푸시)
  5. 읽음/전달 상태는 별도 이벤트로 처리

데이터 모델(간단)

messages (
  id BIGINT,
  room_id BIGINT,
  sender_id BIGINT,
  content TEXT,
  type ENUM('text','image','system'),
  created_at TIMESTAMP,
  delivered BOOL DEFAULT FALSE,
  read_at TIMESTAMP NULL
);
rooms (id, name, type, created_at);
room_members (room_id, user_id, joined_at);

확장성 전략

  • Stateless 서버 + 외부 스토어: WebSocket 연결 메타데이터(유저→서버 매핑)는 Redis에 저장.
  • 샤딩: 사용자 기반으로 WebSocket 게이트웨이 샤딩.
  • 메시지 파티셔닝: 대량 메시지 처리 시 Kafka + 여러 컨슈머 그룹으로 확장.
  • 미들웨어: 메시지 필터(악성 콘텐츠), 사이즈 제한, 파일 업로드 처리(별도 스토리지 S3).

일관성/전달 보장

  • At-most-once: 단순, 중복 허용.
  • At-least-once: 중복 가능성 있으나 재시도 로직 필요(메시지 IDempotency).
  • Exactly-once: 복잡(트랜잭션 등). 대부분 채팅은 at-least-once + idempotency 처리로 충분.

보안 고려사항

  • 인증된 연결만 허용 (JWT/세션 토큰으로 WebSocket 인증)
  • 메시지 암호화: 전송 계층 TLS 사용. 민감한 대화의 경우 E2E 암호화(클라이언트 측 암호화) 고려.
  • 권한 체크: 방 입장 권한, 메시지 삭제 권한 등 서버에서 강제 검증.

6. 실무에서 자주 하는 보안/설계 체크리스트

  • 비밀번호: 솔트 + 적절한 해시 함수(PBKDF2/bcrypt/Argon2) 사용
  • 전송: TLS(HTTPS) 강제 적용
  • 토큰 관리: 액세스 토큰 짧은 만료, 리프레시 토큰 안전 보관 및 관리
  • 세션 보안: HttpOnly, Secure, SameSite 쿠키 설정
  • 입력 검증: XSS/SQL Injection 방지(PreparedStatement, ORM 사용), 출력 이스케이핑
  • 로깅/모니터링: 보안 로그(로그인 실패, 토큰 재발급 등) 수집 및 이상 징후 탐지
  • 키 관리: 암호화 키는 별도 KMS(예: AWS KMS)로 관리
  • 테스팅: 정기적인 취약점 스캔, 펜테스트

4개의 댓글

comment-user-thumbnail
2025년 11월 22일

실무에서 적용하는 체크리스트가 있어 이를 토대로 앞으로 개발할 때 더 신경써서 개발할 수 있을 거 같아요!

답글 달기
comment-user-thumbnail
2025년 11월 24일

정리가 잘 되어 있고 설명이 자세해서 이해하기 좋았습니다! 그동안 고생하셨어요~~

답글 달기
comment-user-thumbnail
2025년 11월 24일

해시부터 채팅 시스템까지 이어지는 설명이 체계적으로 되어 있어서 좋았어요. 특히 AES나 RSA가 실무에서 어떻게 쓰이는지가 적혀 있어서 참고하기 좋았어요.

답글 달기
comment-user-thumbnail
2025년 11월 24일

예시가 잘 되어있어 이해하기 쉬웠습니다~ 도움이 많이 되었어요!

답글 달기