🔒 보안

ISO 27001

Information Security Management System (정보보안 관리체계)

ISO 27001은 정보보안 관리체계(ISMS)에 대한 국제 표준으로, 조직의 정보 자산을 체계적으로 보호하기 위한 요구사항을 정의합니다. 위험 기반 접근법과 PDCA(Plan-Do-Check-Act) 사이클을 통해 지속적인 보안 개선을 추구합니다.

📖 상세 설명

ISO 27001은 국제표준화기구(ISO)와 국제전기기술위원회(IEC)가 공동으로 개발한 정보보안 관리체계 표준입니다. 2005년 처음 발표되었고, 2013년과 2022년에 주요 개정이 이루어졌습니다. ISO 27001:2022는 최신 버전으로, 클라우드 보안, 개인정보 보호, 위협 인텔리전스 등 현대적 보안 요구사항을 반영합니다. 이 인증은 고객과 파트너에게 조직의 정보보안 역량을 객관적으로 증명하는 글로벌 표준입니다.

ISO 27001의 핵심은 위험 기반 접근법(Risk-based Approach)입니다. 조직은 먼저 정보 자산을 식별하고, 각 자산에 대한 위협과 취약점을 분석하여 위험을 평가합니다. 그 다음 위험 수용, 완화, 전가, 회피 중 적절한 처리 방법을 선택합니다. 이 과정은 일회성이 아니라 PDCA 사이클을 통해 지속적으로 반복됩니다. Plan(계획) - Do(실행) - Check(점검) - Act(개선)의 순환은 보안 수준의 지속적 향상을 보장합니다.

표준은 크게 두 부분으로 구성됩니다. 본문(Clauses 4-10)은 ISMS 수립, 구현, 유지, 개선에 대한 필수 요구사항을 정의합니다: 조직 상황 파악, 리더십, 계획, 지원, 운영, 성과 평가, 개선. Annex A는 93개의 보안 통제(Control) 목록을 제공합니다(2022년 버전 기준). 조직은 위험 평가 결과에 따라 적용 가능한 통제를 선택하고, 적용성 보고서(SoA: Statement of Applicability)에 문서화합니다.

ISO 27001 인증은 공인된 인증 기관(CB: Certification Body)의 심사를 통해 획득합니다. 1단계 심사는 문서 검토, 2단계 심사는 현장 실사입니다. 인증 유효기간은 3년이며, 매년 감시 심사(Surveillance Audit)를 받아야 합니다. 인증 준비에는 보통 6개월~1년이 소요됩니다. B2B SaaS, 금융, 의료, 공공 부문에서 특히 중요하며, SOC 2, HIPAA 등 다른 컴플라이언스와 통제 항목이 많이 겹쳐 효율적으로 관리할 수 있습니다.

💻 코드 예제

# ISO 27001 위험 평가 자동화 도구 - Python
import json
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from enum import Enum
from datetime import datetime

class Likelihood(Enum):
    """발생 가능성 등급"""
    RARE = 1        # 매우 낮음
    UNLIKELY = 2    # 낮음
    POSSIBLE = 3    # 보통
    LIKELY = 4      # 높음
    ALMOST_CERTAIN = 5  # 매우 높음

class Impact(Enum):
    """영향도 등급"""
    NEGLIGIBLE = 1  # 무시 가능
    MINOR = 2       # 경미
    MODERATE = 3    # 보통
    MAJOR = 4       # 심각
    CATASTROPHIC = 5  # 치명적

class RiskTreatment(Enum):
    """위험 처리 방법"""
    ACCEPT = "accept"       # 수용
    MITIGATE = "mitigate"   # 완화
    TRANSFER = "transfer"   # 전가 (보험 등)
    AVOID = "avoid"         # 회피

@dataclass
class Asset:
    """정보 자산"""
    id: str
    name: str
    category: str  # hardware, software, data, service, personnel
    owner: str
    classification: str  # public, internal, confidential, restricted
    value: int  # 1-5

@dataclass
class Threat:
    """위협"""
    id: str
    name: str
    category: str  # natural, human, technical
    description: str

@dataclass
class Vulnerability:
    """취약점"""
    id: str
    name: str
    affected_assets: List[str]
    description: str

@dataclass
class Risk:
    """위험"""
    id: str
    asset: Asset
    threat: Threat
    vulnerability: Vulnerability
    likelihood: Likelihood
    impact: Impact
    treatment: Optional[RiskTreatment] = None
    controls: List[str] = field(default_factory=list)
    residual_risk: Optional[int] = None

    @property
    def inherent_risk(self) -> int:
        """고유 위험 점수 = 가능성 x 영향도 x 자산가치"""
        return self.likelihood.value * self.impact.value * self.asset.value

    @property
    def risk_level(self) -> str:
        """위험 수준 판정"""
        score = self.inherent_risk
        if score >= 50:
            return "CRITICAL"
        elif score >= 25:
            return "HIGH"
        elif score >= 10:
            return "MEDIUM"
        else:
            return "LOW"


class ISO27001RiskAssessment:
    """ISO 27001 위험 평가 엔진"""

    def __init__(self):
        self.assets: Dict[str, Asset] = {}
        self.threats: Dict[str, Threat] = {}
        self.vulnerabilities: Dict[str, Vulnerability] = {}
        self.risks: Dict[str, Risk] = {}
        self.risk_appetite = 15  # 조직의 위험 수용 임계값

    def add_asset(self, asset: Asset) -> None:
        """자산 등록"""
        self.assets[asset.id] = asset

    def add_threat(self, threat: Threat) -> None:
        """위협 등록"""
        self.threats[threat.id] = threat

    def identify_risk(self, asset_id: str, threat_id: str,
                      vulnerability_id: str, likelihood: Likelihood,
                      impact: Impact) -> Risk:
        """위험 식별 및 평가"""
        risk = Risk(
            id=f"RISK-{len(self.risks)+1:04d}",
            asset=self.assets[asset_id],
            threat=self.threats[threat_id],
            vulnerability=self.vulnerabilities[vulnerability_id],
            likelihood=likelihood,
            impact=impact
        )
        self.risks[risk.id] = risk
        return risk

    def treat_risk(self, risk_id: str, treatment: RiskTreatment,
                   controls: List[str] = None) -> None:
        """위험 처리 결정"""
        risk = self.risks[risk_id]
        risk.treatment = treatment
        risk.controls = controls or []

        # 통제 적용 후 잔여 위험 계산 (단순화된 예시)
        if treatment == RiskTreatment.MITIGATE:
            reduction = len(controls) * 0.2  # 통제당 20% 감소
            risk.residual_risk = int(risk.inherent_risk * (1 - min(reduction, 0.8)))
        elif treatment == RiskTreatment.AVOID:
            risk.residual_risk = 0
        elif treatment == RiskTreatment.TRANSFER:
            risk.residual_risk = int(risk.inherent_risk * 0.3)
        else:  # ACCEPT
            risk.residual_risk = risk.inherent_risk

    def generate_risk_register(self) -> List[Dict]:
        """위험 등록부 생성"""
        register = []
        for risk in self.risks.values():
            register.append({
                "risk_id": risk.id,
                "asset": risk.asset.name,
                "threat": risk.threat.name,
                "vulnerability": risk.vulnerability.name,
                "likelihood": risk.likelihood.name,
                "impact": risk.impact.name,
                "inherent_risk": risk.inherent_risk,
                "risk_level": risk.risk_level,
                "treatment": risk.treatment.value if risk.treatment else "PENDING",
                "controls": risk.controls,
                "residual_risk": risk.residual_risk,
                "acceptable": risk.residual_risk <= self.risk_appetite if risk.residual_risk else False
            })
        return register

    def generate_soa(self, annex_a_controls: Dict[str, str]) -> Dict:
        """적용성 보고서(SoA) 생성"""
        applied_controls = set()
        for risk in self.risks.values():
            applied_controls.update(risk.controls)

        soa = {
            "generated_at": datetime.now().isoformat(),
            "total_controls": len(annex_a_controls),
            "applied_controls": len(applied_controls),
            "controls": []
        }

        for control_id, control_name in annex_a_controls.items():
            soa["controls"].append({
                "id": control_id,
                "name": control_name,
                "applicable": control_id in applied_controls,
                "justification": "Risk treatment" if control_id in applied_controls else "Not applicable"
            })

        return soa


# 사용 예시
if __name__ == "__main__":
    ra = ISO27001RiskAssessment()

    # 자산 등록
    ra.add_asset(Asset(
        id="A001", name="고객 데이터베이스", category="data",
        owner="DBA팀", classification="confidential", value=5
    ))

    # 위협 등록
    ra.add_threat(Threat(
        id="T001", name="SQL Injection 공격",
        category="technical", description="웹 애플리케이션 취약점을 이용한 DB 공격"
    ))

    # 취약점 등록
    ra.vulnerabilities["V001"] = Vulnerability(
        id="V001", name="입력값 검증 미흡",
        affected_assets=["A001"],
        description="사용자 입력에 대한 적절한 검증 부재"
    )

    # 위험 평가
    risk = ra.identify_risk(
        asset_id="A001",
        threat_id="T001",
        vulnerability_id="V001",
        likelihood=Likelihood.LIKELY,
        impact=Impact.MAJOR
    )
    print(f"고유 위험 점수: {risk.inherent_risk}, 수준: {risk.risk_level}")

    # 위험 처리 (Annex A 통제 적용)
    ra.treat_risk(risk.id, RiskTreatment.MITIGATE, [
        "A.8.26",  # 애플리케이션 보안 요구사항
        "A.8.28",  # 보안 코딩
        "A.8.29"   # 개발 및 인수 시 보안 테스트
    ])

    # 위험 등록부 출력
    register = ra.generate_risk_register()
    print(json.dumps(register, indent=2, ensure_ascii=False))
# ISO 27001:2022 Annex A 통제 체크리스트 - YAML
# 조직, 인원, 물리, 기술 4개 테마로 분류

iso27001_annex_a:
  version: "2022"
  total_controls: 93
  themes:
    organizational:
      count: 37
      controls:
        - id: "A.5.1"
          name: "정보보안 정책"
          description: "정보보안 정책 및 주제별 정책 수립"
          status: "implemented"
          evidence: "ISMS-POL-001 정보보안정책.docx"
          owner: "CISO"

        - id: "A.5.2"
          name: "정보보안 역할과 책임"
          description: "정보보안 역할 및 책임 정의와 할당"
          status: "implemented"
          evidence: "조직도, 직무기술서"
          owner: "인사팀"

        - id: "A.5.7"
          name: "위협 인텔리전스"
          description: "정보보안 위협 정보 수집 및 분석"
          status: "in_progress"
          evidence: "위협 인텔리전스 피드 구독"
          owner: "보안관제팀"
          gap: "자동화된 위협 피드 연동 필요"

        - id: "A.5.23"
          name: "클라우드 서비스 정보보안"
          description: "클라우드 서비스 이용 시 정보보안 관리"
          status: "implemented"
          evidence: "클라우드보안정책, CSP BAA 계약서"
          owner: "클라우드팀"

        - id: "A.5.30"
          name: "비즈니스 연속성을 위한 ICT 준비"
          description: "ICT 시스템의 비즈니스 연속성 계획"
          status: "implemented"
          evidence: "DR 계획, RTO/RPO 문서"
          owner: "인프라팀"

    people:
      count: 8
      controls:
        - id: "A.6.1"
          name: "심사"
          description: "직원 채용 전 신원조회"
          status: "implemented"
          evidence: "채용 프로세스 문서"
          owner: "인사팀"

        - id: "A.6.3"
          name: "정보보안 인식, 교육 및 훈련"
          description: "직원 보안 인식 교육 프로그램"
          status: "implemented"
          evidence: "교육 이수 기록, 교육 자료"
          owner: "교육팀"
          metrics:
            completion_rate: "95%"
            frequency: "quarterly"

        - id: "A.6.7"
          name: "원격 근무"
          description: "원격 근무 시 정보보안 조치"
          status: "implemented"
          evidence: "재택근무 보안지침"
          owner: "IT팀"

    physical:
      count: 14
      controls:
        - id: "A.7.1"
          name: "물리적 보안 경계"
          description: "보안 구역의 물리적 경계 설정"
          status: "implemented"
          evidence: "시설 도면, 보안구역 정의서"
          owner: "시설팀"

        - id: "A.7.4"
          name: "물리적 보안 모니터링"
          description: "CCTV, 침입탐지 등 물리적 모니터링"
          status: "implemented"
          evidence: "CCTV 운영 기록, 경보 시스템"
          owner: "시설팀"

        - id: "A.7.10"
          name: "저장 매체"
          description: "저장 매체의 관리 및 안전한 폐기"
          status: "implemented"
          evidence: "매체 관리 대장, 폐기 인증서"
          owner: "IT팀"

    technological:
      count: 34
      controls:
        - id: "A.8.2"
          name: "특권 접근 권한"
          description: "관리자 권한의 제한 및 관리"
          status: "implemented"
          evidence: "PAM 시스템, 권한 관리 정책"
          owner: "IT보안팀"

        - id: "A.8.5"
          name: "보안 인증"
          description: "안전한 인증 기술 구현"
          status: "implemented"
          evidence: "MFA 정책, SSO 구성"
          owner: "IAM팀"

        - id: "A.8.9"
          name: "구성 관리"
          description: "시스템 구성의 보안 설정 관리"
          status: "implemented"
          evidence: "하드닝 가이드, 구성 기준선"
          owner: "시스템팀"

        - id: "A.8.15"
          name: "로깅"
          description: "활동 로그 생성 및 보호"
          status: "implemented"
          evidence: "로깅 정책, SIEM 구성"
          owner: "보안관제팀"
          retention: "1 year"

        - id: "A.8.24"
          name: "암호화 사용"
          description: "암호화 키 관리 포함 암호화 구현"
          status: "implemented"
          evidence: "암호화정책, KMS 구성"
          owner: "보안팀"
          algorithms:
            - "AES-256"
            - "RSA-2048"
            - "TLS 1.3"

        - id: "A.8.28"
          name: "보안 코딩"
          description: "소프트웨어 개발 시 보안 코딩 원칙"
          status: "implemented"
          evidence: "시큐어코딩 가이드, SAST/DAST 결과"
          owner: "개발팀"

# 감사 추적
audit_trail:
  last_internal_audit: "2024-09-15"
  next_surveillance_audit: "2025-03-20"
  certification_expiry: "2026-03-19"
  nonconformities:
    major: 0
    minor: 2
    observations: 5
#!/bin/bash
# ISO 27001 컴플라이언스 자동 점검 스크립트
# Annex A 기술적 통제 확인

set -e

echo "=========================================="
echo "ISO 27001:2022 Technical Controls Checker"
echo "=========================================="
echo "실행 시각: $(date)"
echo ""

REPORT_FILE="iso27001_compliance_$(date +%Y%m%d).json"
COMPLIANCE_SCORE=0
TOTAL_CHECKS=0

# 결과를 JSON으로 저장하기 위한 초기화
echo '{"timestamp": "'$(date -Iseconds)'", "checks": [' > $REPORT_FILE

add_check_result() {
    local control_id=$1
    local control_name=$2
    local status=$3
    local details=$4

    if [ $TOTAL_CHECKS -gt 0 ]; then
        echo "," >> $REPORT_FILE
    fi

    echo '{
        "control_id": "'$control_id'",
        "control_name": "'$control_name'",
        "status": "'$status'",
        "details": "'$details'"
    }' >> $REPORT_FILE

    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
    if [ "$status" = "PASS" ]; then
        COMPLIANCE_SCORE=$((COMPLIANCE_SCORE + 1))
    fi
}

# A.8.2 - 특권 접근 권한 점검
echo "[A.8.2] 특권 접근 권한 점검..."
SUDO_USERS=$(grep -Po '^sudo.+:\K.*$' /etc/group 2>/dev/null || echo "")
ROOT_SSH=$(grep "^PermitRootLogin" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')

if [ "$ROOT_SSH" = "no" ]; then
    add_check_result "A.8.2" "특권 접근 권한" "PASS" "Root SSH 접근 비활성화됨"
    echo "  [PASS] Root SSH 접근 비활성화됨"
else
    add_check_result "A.8.2" "특권 접근 권한" "FAIL" "Root SSH 접근 허용됨"
    echo "  [FAIL] Root SSH 접근 허용됨 - 비활성화 필요"
fi

# A.8.5 - 보안 인증 (비밀번호 정책)
echo "[A.8.5] 보안 인증 점검..."
if [ -f /etc/security/pwquality.conf ]; then
    MIN_LEN=$(grep "^minlen" /etc/security/pwquality.conf 2>/dev/null | awk -F= '{print $2}' | tr -d ' ')
    if [ "$MIN_LEN" -ge 12 ] 2>/dev/null; then
        add_check_result "A.8.5" "보안 인증" "PASS" "비밀번호 최소 길이: ${MIN_LEN}자"
        echo "  [PASS] 비밀번호 최소 길이: ${MIN_LEN}자"
    else
        add_check_result "A.8.5" "보안 인증" "FAIL" "비밀번호 최소 길이 부족"
        echo "  [FAIL] 비밀번호 최소 길이가 12자 미만"
    fi
else
    add_check_result "A.8.5" "보안 인증" "WARN" "pwquality.conf 없음"
    echo "  [WARN] 비밀번호 정책 설정 파일 없음"
fi

# A.8.9 - 구성 관리 (불필요한 서비스)
echo "[A.8.9] 구성 관리 점검..."
UNNECESSARY_SERVICES=("telnet" "ftp" "rsh" "rlogin")
RUNNING_UNNECESSARY=""

for svc in "${UNNECESSARY_SERVICES[@]}"; do
    if systemctl is-active --quiet $svc 2>/dev/null; then
        RUNNING_UNNECESSARY="$RUNNING_UNNECESSARY $svc"
    fi
done

if [ -z "$RUNNING_UNNECESSARY" ]; then
    add_check_result "A.8.9" "구성 관리" "PASS" "불필요한 서비스 없음"
    echo "  [PASS] 불필요한 서비스 실행 없음"
else
    add_check_result "A.8.9" "구성 관리" "FAIL" "실행 중:$RUNNING_UNNECESSARY"
    echo "  [FAIL] 불필요한 서비스 실행 중:$RUNNING_UNNECESSARY"
fi

# A.8.15 - 로깅
echo "[A.8.15] 로깅 점검..."
if systemctl is-active --quiet rsyslog || systemctl is-active --quiet syslog-ng; then
    AUDIT_LOG_EXISTS=$(ls -la /var/log/audit/audit.log 2>/dev/null && echo "yes" || echo "no")
    if [ "$AUDIT_LOG_EXISTS" = "yes" ]; then
        add_check_result "A.8.15" "로깅" "PASS" "시스템 로깅 및 감사 로그 활성"
        echo "  [PASS] 시스템 로깅 및 감사 로그 활성"
    else
        add_check_result "A.8.15" "로깅" "WARN" "auditd 미구성"
        echo "  [WARN] 시스템 로깅은 활성이나 auditd 미구성"
    fi
else
    add_check_result "A.8.15" "로깅" "FAIL" "시스템 로깅 비활성"
    echo "  [FAIL] 시스템 로깅 서비스 미실행"
fi

# A.8.24 - 암호화 사용 (디스크 암호화)
echo "[A.8.24] 암호화 사용 점검..."
ENCRYPTED_VOL=$(lsblk -o NAME,TYPE,FSTYPE 2>/dev/null | grep -c "crypt" || echo "0")
if [ "$ENCRYPTED_VOL" -gt 0 ]; then
    add_check_result "A.8.24" "암호화 사용" "PASS" "암호화된 볼륨 ${ENCRYPTED_VOL}개 발견"
    echo "  [PASS] 암호화된 볼륨 ${ENCRYPTED_VOL}개 발견"
else
    add_check_result "A.8.24" "암호화 사용" "WARN" "암호화된 볼륨 미발견"
    echo "  [WARN] 암호화된 볼륨 미발견 - 디스크 암호화 권장"
fi

# A.8.8 - 기술적 취약점 관리 (보안 업데이트)
echo "[A.8.8] 기술적 취약점 관리 점검..."
if command -v apt &> /dev/null; then
    UPDATES=$(apt list --upgradable 2>/dev/null | grep -c "security" || echo "0")
    if [ "$UPDATES" -eq 0 ]; then
        add_check_result "A.8.8" "기술적 취약점 관리" "PASS" "보안 업데이트 최신"
        echo "  [PASS] 보안 업데이트 최신 상태"
    else
        add_check_result "A.8.8" "기술적 취약점 관리" "FAIL" "보안 업데이트 ${UPDATES}개 필요"
        echo "  [FAIL] 보안 업데이트 ${UPDATES}개 필요"
    fi
elif command -v yum &> /dev/null; then
    UPDATES=$(yum check-update --security 2>/dev/null | grep -c "^" || echo "0")
    echo "  [INFO] YUM 기반 시스템 - 수동 확인 필요"
fi

# 보고서 마무리
echo '], "summary": {' >> $REPORT_FILE
echo '"total_checks": '$TOTAL_CHECKS',' >> $REPORT_FILE
echo '"passed": '$COMPLIANCE_SCORE',' >> $REPORT_FILE
PERCENT=$((COMPLIANCE_SCORE * 100 / TOTAL_CHECKS))
echo '"compliance_percentage": '$PERCENT >> $REPORT_FILE
echo '}}' >> $REPORT_FILE

echo ""
echo "=========================================="
echo "점검 완료"
echo "총 점검: $TOTAL_CHECKS"
echo "통과: $COMPLIANCE_SCORE"
echo "준수율: ${PERCENT}%"
echo "보고서: $REPORT_FILE"
echo "=========================================="

🗣️ 실무에서 이렇게 말하세요

💬 ISO 27001 인증 준비 킥오프 회의에서
"인증 범위(Scope)부터 정해야 합니다. 전사 범위로 할지, 특정 사업부나 서비스로 한정할지 결정하고, 경영진의 ISMS 정책 승인을 받아야 해요. 다음으로 자산 식별, 위험 평가를 진행하고, Annex A 93개 통제 중 우리에게 적용되는 항목을 선별해서 적용성 보고서(SoA)를 작성합니다. 인증 심사까지 6개월 정도 예상됩니다."
💬 내부 감사 결과 보고에서
"이번 내부 감사에서 부적합(Nonconformity) 2건이 발견됐습니다. 하나는 A.6.3 보안 인식 교육 이수율이 목표치에 미달한 것이고, 다른 하나는 A.8.15 로그 보관 기간이 정책과 불일치합니다. 시정 조치 계획을 수립하고, 다음 감시 심사 전까지 해결해야 합니다. 관찰사항(Observation)으로는 위험 평가 주기 단축을 권고받았습니다."
💬 고객사 보안 질문 대응에서
"저희는 ISO 27001:2022 인증을 보유하고 있습니다. 인증서와 적용성 보고서(SoA)를 공유드릴 수 있고, 필요하시면 최근 감시 심사 보고서 요약본도 제공 가능합니다. 귀사의 공급업체 보안 설문 항목 대부분은 저희 ISMS 문서에서 증빙 가능합니다. 추가로 SOC 2 Type II 보고서도 있어서 함께 검토하시면 좋겠습니다."

⚠️ 주의사항 & 베스트 프랙티스

문서만 만들고 실제 운영은 안 함

정책 문서는 화려하지만 실제로 지켜지지 않으면 인증 심사에서 부적합 판정을 받습니다. 심사원은 문서와 실제 운영의 일치를 확인합니다. "문서화된 정책 vs 실제 운영 기록"의 갭이 가장 흔한 부적합 원인입니다.

위험 평가 없이 통제 선택

Annex A 93개 통제를 무조건 전부 적용하거나, 반대로 아무 근거 없이 제외하면 안 됩니다. 반드시 위험 평가 결과에 기반해 통제를 선택하고, SoA에 적용/미적용 사유를 명시해야 합니다.

인증 취득 후 방치

인증은 끝이 아니라 시작입니다. 매년 감시 심사, 3년마다 갱신 심사가 있습니다. 정기 내부 감사, 경영 검토, 위험 재평가를 지속해야 합니다. PDCA 사이클을 돌리지 않으면 인증 유지가 어렵습니다.

ISO 27001 베스트 프랙티스

경영진 참여 확보(Management Commitment), 범위 명확화, 자동화된 증거 수집(SIEM, GRC 도구), 정기 교육 및 인식 제고, 지속적 개선 문화 정착이 핵심입니다. SOC 2, GDPR 등 다른 컴플라이언스와 통제를 매핑하면 효율적으로 관리할 수 있습니다.

🔗 관련 용어

📚 더 배우기