홈으로 돌아가기

Go에서 안전한 파일 업로드: 7가지 공격

이 기사는 Go 파일 업로드의 7가지 공격으로부터 보호를 설명합니다: 매직 바이트를 통한 타입 스푸핑, MaxBytesReader+슬롯을 통한 DoS, 고정 경로를 통한 경로 탐색. 브라우저 확장 프로그램을 위한 계정 없이 디바이스 ECDSA-식별 사용.

Go 파일 업로드에 대한 7가지 공격과 그 보호 방법
Advertisement 728x90

Go에서 파일 업로드 보안: 7가지 일반적인 공격에 대응하는 방법

기존 인증이 없는 시스템에서는 파일 업로드를 위한 요청 출처 식별이 매우 중요합니다. PDF를 마크다운으로 변환하는 브라우저 확장 프로그램의 경우, WebCrypto API를 기반으로 한 ECDSA P-256 모델을 도입했습니다. 첫 실행 시, 내보낼 수 없는 키 쌍이 생성됩니다:

const keyPair = await crypto.subtle.generateKey(
  { name: 'ECDSA', namedCurve: 'P-256' },
  false,  // extractable = false
  ['sign', 'verify']
);

공개키는 서버에 등록되며, device_iddevice_token을 반환합니다. 모든 API 요청에는 다음 정보가 포함됩니다:

  • Authorization: Bearer <token>
  • X-Timestamp (±5분 이내)
  • X-Nonce (재생 공격 방지)
  • X-Body-SHA256
  • X-Signature (METHOD+PATH+TIMESTAMP+NONCE+BODY_HASH에 대한 ECDSA 서명)

서버는 공개키를 사용해 서명을 검증함으로써 다음과 같은 보안을 확보합니다:

Google AdInline article slot
  • 디바이스별 리밋 설정
  • 재생 공격 방어
  • 작업 슬롯 제어 (최대 3개 활성 작업)
  • 등록 제한 (IP당 시간당 최대 5회)

이 모델은 공격 비용을 증가시켜, 전체 ECDSA 서명 구현이 필요하게 만듭니다.

공격 1: 파일 유형 위조

악성 실행 파일을 PDF처럼 위장하는 공격입니다. 방어는 세 단계로 이루어집니다.

프론트엔드: MIME 타입과 확장자 검사:

Google AdInline article slot
const ALLOWED_TYPES = ['application/pdf'];
const ALLOWED_EXTENSIONS = ['.pdf'];

function validateFile(file) {
  const isPdf = ALLOWED_TYPES.includes(file.type) ||
                ALLOWED_EXTENSIONS.some(ext =>
                  file.name.toLowerCase().endsWith(ext));
  if (!isPdf) return { valid: false, error: 'PDF만 허용' };
}

백엔드: Content-Type 기반 라우팅. 핵심: 매직 바이트 확인:

pdfMagicBytes := "%PDF"
header4 := make([]byte, 4)
_, err := file.Read(header4)
if string(header4) != pdfMagicBytes {
    respondError(w, http.StatusBadRequest,
        models.ErrCodeValidationError,
        "유효한 PDF가 아닙니다")
}
file.Seek(0, 0)

헤더나 확장자를 무시하고, 파일의 처음 4바이트를 직접 확인함으로써 위변조를 방지합니다. 다중 포맷 파일(악성 코드를 포함한 유효한 PDF)은 추가 서명이 필요합니다.

공격 2: 디스크 고갈 (DoS)

큰 파일을 통해 서비스 거부 공격을 시도합니다. 대응책은 다음과 같습니다:

Google AdInline article slot
  • http.MaxBytesReader(w, r.Body, MaxFileSize + 1MB) — 읽기 제한치에서 즉시 중단
  • 디바이스별 작업 슬롯: 최대 3개의 활성 작업 (queued, processing, ready, error 상태)
  • 등록 및 요청에 대한 리밋 설정

디바이스 식별이므로 IP 또는 토큰 회전 전략은 효과 없음.

공격 3: 경로 탐색 (Path Traversal)

../../../etc/passwd와 같은 경로를 이용한 공격입니다. 해결책: 고정된 경로 {UUID}/input.pdf. 사용자 입력은 경로에 전혀 반영되지 않습니다.

프론트엔드 정제 (추가 보안 계층):

function sanitizeFileName(name) {
  return name
    .replace(/[/\\?%*:|"<>]/g, '-') 
    .replace(/^\.+/, '')
    .replace(/\.$/, '')
    .substring(0, 255)
    .trim();
}

백엔드는 파일명과 무관하게 자체적으로 경로를 생성합니다.

공격 4: URL 업로드를 통한 SSRF

"URL에서 업로드" 기능을 위해 다음 사항을 차단합니다:

  • 다운로드 전 DNS 해석 금지
  • 리디렉션 차단
  • 개인 IP 주소 차단 (차단 목록: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)

공격 5: 재생 공격 (Replay Attacks)

X-Nonce + X-Timestamp + 서명. Nonce는 디바이스별로 저장되며, 타임스탬프는 ±5분 범위 내에서만 유효합니다.

공격 6: 디바이스 위조 (Impersonation)

extractable: false 설정으로 비밀키가 외부로 추출 불가. device_token을 탈취해도 유효한 서명 없이는 무용지물입니다.

공격 7: 애플리케이션 계층 오남용

리밋 + 슬롯 제어 + 암호화 서명 적용. 최대 3개의 슬롯, IP당 시간당 최대 5회 등록 제한.

핵심 요약

  • 암호화된 디바이스 식별이 리밋 설정을 위한 계정 시스템 대체
  • 파일 유형 검증은 매직 바이트(%PDF)가 유일한 신뢰할 수 있는 방법
  • MaxBytesReader + 슬롯 관리로 DoS 공격 방어
  • 고정 경로로 경로 탐색 위험 완전 제거
  • ECDSA P-256 + nonce/timestamp로 재생 및 위조 공격 차단

— Editorial Team

Advertisement 728x90

다음 읽기