포스트

[이제와서 시작하는 GitHub 마스터하기 - 고급편 #5] 보안 기능: Dependabot, Secret scanning, 보안 설정

[이제와서 시작하는 GitHub 마스터하기 - 고급편 #5] 보안 기능: Dependabot, Secret scanning, 보안 설정

들어가며

“이제와서 시작하는 GitHub 마스터하기” 시리즈의 열여섯 번째 시간입니다. 이번에는 GitHub의 강력한 보안 기능들을 활용하여 코드베이스를 안전하게 보호하는 방법을 알아보겠습니다. 보안은 개발의 선택이 아닌 필수입니다.

1. GitHub Security 개요

보안 기능 전체 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GitHub Security Features:
  Supply Chain:
    - Dependency graph: 의존성 시각화
    - Dependabot alerts: 취약점 알림
    - Dependabot updates: 자동 업데이트
    
  Code Security:
    - Code scanning: 정적 분석
    - Secret scanning: 비밀 정보 탐지
    - Push protection: 푸시 차단
    
  Access Control:
    - 2FA: 이중 인증
    - SSO: 단일 로그인
    - IP allowlist: IP 제한
    
  Audit & Compliance:
    - Audit log: 감사 로그
    - Security policy: 보안 정책
    - Advisory database: 취약점 DB

Security Overview 대시보드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
저장소 → Security 탭에서 확인 가능:

1. Security Policy
   - 보안 정책 문서
   - 취약점 보고 방법

2. Advisories
   - 보안 권고 사항
   - CVE 발행

3. Dependabot
   - 의존성 업데이트
   - 취약점 알림

4. Code scanning
   - 코드 취약점 분석
   - SAST 결과

5. Secret scanning
   - 노출된 시크릿
   - 토큰 알림

2. Dependabot 설정과 활용

Dependabot 활성화

.github/dependabot.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
version: 2
updates:
  # JavaScript 의존성
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
      time: "09:00"
      timezone: "Asia/Seoul"
    open-pull-requests-limit: 10
    reviewers:
      - "security-team"
      - "lead-developer"
    assignees:
      - "dependency-manager"
    labels:
      - "dependencies"
      - "security"
    commit-message:
      prefix: "chore"
      prefix-development: "chore"
      include: "scope"
    milestone: 4
    ignore:
      # 특정 의존성 무시
      - dependency-name: "lodash"
        versions: ["4.x"]
      # 메이저 버전 업데이트 무시
      - dependency-name: "*"
        update-types: ["version-update:semver-major"]
    allow:
      # 직접 의존성만
      - dependency-type: "direct"
    # 그룹화
    groups:
      development-dependencies:
        dependency-type: "development"
        update-types:
          - "minor"
          - "patch"

  # Python 의존성
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
    labels:
      - "python"
      - "dependencies"
    
  # Docker 이미지
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
    reviewers:
      - "devops-team"
      
  # GitHub Actions
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "ci/cd"
      
  # Go modules
  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"
    commit-message:
      prefix: "deps"
    
  # Terraform
  - package-ecosystem: "terraform"
    directory: "/infrastructure"
    schedule:
      interval: "daily"
    reviewers:
      - "infrastructure-team"

Dependabot 보안 업데이트 자동화

.github/workflows/auto-merge-dependabot.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
name: Auto-merge Dependabot PRs

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: write
  pull-requests: write

jobs:
  auto-merge:
    runs-on: ubuntu-latest
    if: github.actor == 'dependabot[bot]'
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        
      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v1
        with:
          github-token: "$"
          
      - name: Auto-merge patch updates
        if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: $
          GITHUB_TOKEN: $
          
      - name: Auto-merge minor updates for dev dependencies
        if: |
          steps.metadata.outputs.update-type == 'version-update:semver-minor' &&
          steps.metadata.outputs.dependency-type == 'direct:development'
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: $
          GITHUB_TOKEN: $
          
      - name: Comment on major updates
        if: steps.metadata.outputs.update-type == 'version-update:semver-major'
        run: |
          gh pr comment "$PR_URL" --body "
          ⚠️ **Major version update detected!**
          
          This PR updates $ from $ to $.
          
          Please review carefully:
          - [ ] Check breaking changes
          - [ ] Update code if necessary
          - [ ] Test thoroughly
          "
        env:
          PR_URL: $
          GITHUB_TOKEN: $

커스텀 Dependabot 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# .github/dependabot.yml - 고급 설정
version: 2
registries:
  npm-private:
    type: npm-registry
    url: https://npm.company.com
    username: $
    password: $
    
  docker-private:
    type: docker-registry
    url: registry.company.com
    username: $
    password: $

updates:
  - package-ecosystem: "npm"
    directory: "/"
    registries:
      - npm-private
    schedule:
      interval: "daily"
    # 버전 요구사항 증가 전략
    versioning-strategy: increase
    # 또는: increase-if-necessary, lockfile-only, widen
    
  - package-ecosystem: "docker"
    directory: "/"
    registries:
      - docker-private
    schedule:
      interval: "weekly"

3. Secret Scanning 설정

Secret Scanning 활성화 및 설정

1
2
3
4
5
6
7
# Repository Settings → Security & analysis
# Enable:
- Dependency graph ✓
- Dependabot alerts ✓
- Dependabot security updates ✓
- Secret scanning ✓
- Push protection ✓

커스텀 패턴 정의

.github/secret-scanning.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 커스텀 시크릿 패턴
patterns:
  - name: Company API Key
    pattern: |
      COMP_API_[A-Z0-9]{32}
    
  - name: Internal Token
    pattern: |
      (internal|int)_token[\s]*=[\s]*["']?[a-zA-Z0-9]{40}["']?
    
  - name: Database Connection String
    pattern: |
      (postgres|mysql|mongodb):\/\/[^:]+:[^@]+@[^/]+\/\w+
    
  - name: JWT Secret
    pattern: |
      JWT_SECRET[\s]*=[\s]*["']?[a-zA-Z0-9+/]{32,}={0,2}["']?
    
  - name: Slack Webhook
    pattern: |
      https://hooks\.slack\.com/services/T[A-Z0-9]{8}/B[A-Z0-9]{8}/[a-zA-Z0-9]{24}

# 제외 경로
paths-ignore:
  - "**/*.test.js"
  - "**/test/**"
  - "docs/**"
  - "*.md"

Push Protection 우회 처리

.github/workflows/handle-blocked-push.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
name: Handle Blocked Push

on:
  workflow_dispatch:
    inputs:
      reason:
        description: 'Reason for bypassing'
        required: true
        type: choice
        options:
          - 'False positive'
          - 'Test data'
          - 'Already rotated'
          - 'Public information'

jobs:
  document-bypass:
    runs-on: ubuntu-latest
    steps:
      - name: Log bypass reason
        uses: actions/github-script@v6
        with:
          script: |
            const issue = await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `Secret Scanning Bypass: ${new Date().toISOString()}`,
              body: `
                ## Bypass Information
                
                **User**: $
                **Reason**: $
                **Time**: ${new Date().toISOString()}
                **Commit**: $
                
                ### Justification
                Please provide detailed justification in comments.
              `,
              labels: ['security', 'bypass', 'audit']
            });
            
            console.log(`Created issue #${issue.data.number}`);

4. Code Scanning과 CodeQL

CodeQL 기본 설정

.github/workflows/codeql.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
name: "CodeQL"

on:
  push:
    branches: [ "main", "develop" ]
  pull_request:
    branches: [ "main" ]
  schedule:
    - cron: '0 0 * * 0'  # 매주 일요일

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'javascript', 'python', 'java' ]

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Initialize CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: $
        queries: security-and-quality
        # 추가 쿼리 팩
        # queries: security-extended,security-and-quality
        
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2
      
    # 또는 수동 빌드
    # - name: Build Java
    #   if: matrix.language == 'java'
    #   run: |
    #     ./gradlew build
        
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
      with:
        category: "/language:$"

커스텀 CodeQL 쿼리

.github/codeql/custom-queries.ql:

/**
 * @name Hardcoded credentials
 * @description Finds hardcoded credentials in code
 * @kind problem
 * @problem.severity error
 * @security-severity 8.0
 * @precision high
 * @id custom/hardcoded-credentials
 * @tags security
 *       external/cwe/cwe-798
 */

import javascript
import semmle.javascript.security.dataflow.HardcodedCredentials

from DataFlow::Node source, DataFlow::Node sink, string value
where
  HardcodedCredentials::flow(source, sink) and
  source.asExpr().(StringLiteral).getValue() = value and
  value.regexpMatch(".*[pP]assword.*|.*[sS]ecret.*|.*[kK]ey.*") and
  value.length() > 8
select sink, "Hardcoded credential: " + value

서드파티 도구 통합

.github/workflows/security-scan.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
name: Security Scanning

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  snyk:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: $
        with:
          args: --severity-threshold=high
          
  sonarcloud:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          
      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: $
          SONAR_TOKEN: $
          
  trivy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'
          
      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

5. Security Policy 작성

SECURITY.md

SECURITY.md:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# Security Policy

## Supported Versions

현재 다음 버전들에 대해 보안 업데이트를 제공합니다:

| Version | Supported          |
| ------- | ------------------ |
| 5.1.x   | :white_check_mark: |
| 5.0.x   | :white_check_mark: |
| 4.0.x   | :x:                |
| < 4.0   | :x:                |

## Reporting a Vulnerability

보안 취약점을 발견하셨다면 책임감 있는 공개를 부탁드립니다.

### 보고 방법

1. **GitHub Security Advisory 사용** (권장)
   - [Security 탭 → Report a vulnerability](https://github.com/username/repo/security/advisories/new)
   - 비공개로 안전하게 보고 가능

2. **이메일**
   - security@example.com
   - PGP 키: [링크]

3. **보고 시 포함사항**
   - 취약점 설명
   - 재현 단계
   - 영향 범위
   - 가능한 해결 방법

### 대응 절차

1. **접수 확인**: 24시간 이내
2. **초기 평가**: 72시간 이내
3. **상세 대응**: 7일 이내
4. **패치 릴리즈**: 심각도에 따라 결정
5. **공개**: CVE 발행 및 공지

### 보상 프로그램

현재 버그 바운티 프로그램은 운영하지 않지만, 
기여자는 SECURITY.md에 크레딧을 받습니다.

### 제외 사항

다음은 보안 취약점으로 간주하지 않습니다:
- 서비스 거부 공격 (DoS)
- 스팸
- 소셜 엔지니어링
- 물리적 공격

## Security Features

### 구현된 보안 기능
- [x] 입력 검증
- [x] SQL 인젝션 방지
- [x] XSS 방지
- [x] CSRF 토큰
- [x] Rate limiting
- [x] 암호화된 통신

### 보안 헤더

Content-Security-Policy: default-src ‘self’ X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block Strict-Transport-Security: max-age=31536000; includeSubDomains

1
2
3
4
5
## Contact

보안팀: security@example.com
긴급 연락처: +1-234-567-8900

6. 브랜치 보호와 서명

커밋 서명 강제

1
2
3
4
5
6
7
8
9
10
11
12
13
# GPG 키 생성
gpg --full-generate-key

# GPG 키 목록 확인
gpg --list-secret-keys --keyid-format=long

# Git에 GPG 키 설정
git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true

# GPG 키 GitHub에 추가
gpg --armor --export YOUR_KEY_ID
# GitHub Settings → SSH and GPG keys → New GPG key

브랜치 보호 규칙 자동화

.github/workflows/branch-protection.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
name: Enforce Branch Protection

on:
  repository_dispatch:
    types: [created]
  workflow_dispatch:

jobs:
  protect-branches:
    runs-on: ubuntu-latest
    steps:
      - name: Configure branch protection
        uses: actions/github-script@v6
        with:
          github-token: $
          script: |
            const branches = ['main', 'develop', 'release/*'];
            
            for (const branch of branches) {
              await github.rest.repos.updateBranchProtection({
                owner: context.repo.owner,
                repo: context.repo.repo,
                branch,
                required_status_checks: {
                  strict: true,
                  contexts: ['continuous-integration', 'code-review']
                },
                enforce_admins: true,
                required_pull_request_reviews: {
                  required_approving_review_count: 2,
                  dismiss_stale_reviews: true,
                  require_code_owner_reviews: true,
                  require_last_push_approval: true
                },
                restrictions: null,
                allow_force_pushes: false,
                allow_deletions: false,
                required_conversation_resolution: true,
                lock_branch: false,
                allow_fork_syncing: false,
                required_signatures: true
              });
            }

7. 컨테이너 보안

Dockerfile 보안 스캔

.github/workflows/container-security.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
name: Container Security

on:
  push:
    paths:
      - 'Dockerfile*'
      - '.dockerignore'
  pull_request:
    paths:
      - 'Dockerfile*'

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run Hadolint
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile
          
      - name: Build image
        run: docker build -t $:$ .
        
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: $:$
          format: 'table'
          exit-code: '1'
          ignore-unfixed: true
          vuln-type: 'os,library'
          severity: 'CRITICAL,HIGH'
          
      - name: Run Grype vulnerability scanner
        uses: anchore/scan-action@v3
        with:
          image: $:$
          fail-build: true
          severity-cutoff: high
          
      - name: Container structure test
        run: |
          wget https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 -O container-structure-test
          chmod +x container-structure-test
          ./container-structure-test test \
            --image $:$ \
            --config container-test.yaml

안전한 Dockerfile 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 보안 강화 Dockerfile 예제
FROM node:18-alpine@sha256:abc123... AS builder

# 보안 업데이트
RUN apk update && apk upgrade && apk add --no-cache \
    ca-certificates \
    && rm -rf /var/cache/apk/*

# 비root 사용자 생성
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# 작업 디렉토리
WORKDIR /app

# 의존성 먼저 복사 (캐시 활용)
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 소스 코드 복사
COPY --chown=nodejs:nodejs . .

# 빌드
RUN npm run build

# Production 이미지
FROM node:18-alpine@sha256:abc123...

# 보안 업데이트
RUN apk update && apk upgrade && apk add --no-cache \
    ca-certificates \
    dumb-init \
    && rm -rf /var/cache/apk/*

# 비root 사용자
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# 빌드된 파일만 복사
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules

# 보안 설정
USER nodejs
EXPOSE 3000

# 헬스체크
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

# dumb-init으로 시그널 처리
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]

8. Supply Chain 보안

SBOM 생성과 관리

.github/workflows/sbom.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
name: Generate SBOM

on:
  release:
    types: [published]
  workflow_dispatch:

jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Generate SBOM with Syft
        uses: anchore/sbom-action@v0
        with:
          format: spdx-json
          output-file: sbom.spdx.json
          
      - name: Generate CycloneDX SBOM
        run: |
          npm install -g @cyclonedx/cyclonedx-npm
          cyclonedx-npm --output-format json --output-file sbom.cyclonedx.json
          
      - name: Sign SBOM
        env:
          COSIGN_PASSWORD: $
        run: |
          cosign sign-blob \
            --key cosign.key \
            sbom.spdx.json > sbom.spdx.json.sig
            
      - name: Upload SBOM
        uses: actions/upload-artifact@v3
        with:
          name: sbom
          path: |
            sbom.*.json
            sbom.*.json.sig
            
      - name: Attach SBOM to release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            sbom.*.json
            sbom.*.json.sig

License 검사

.github/workflows/license-check.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
name: License Check

on:
  pull_request:
  schedule:
    - cron: '0 0 * * 1'  # 매주 월요일

jobs:
  license-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Check licenses with license-checker
        run: |
          npm install -g license-checker
          license-checker --summary --excludePrivatePackages \
            --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;BSD-2-Clause;ISC;CC0-1.0' \
            --out licenses.json
            
      - name: FOSSA scan
        uses: fossas/fossa-action@main
        with:
          api-key: $
          
      - name: Check for copyleft licenses
        run: |
          if grep -E "GPL|LGPL|AGPL" licenses.json; then
            echo "::error::Copyleft licenses found!"
            exit 1
          fi

9. 보안 자동화

Security Scoreboard

.github/workflows/security-scorecard.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
name: Security Scorecard

on:
  schedule:
    - cron: '0 0 * * 0'
  push:
    branches: [ main ]

permissions:
  security-events: write
  contents: read
  actions: read

jobs:
  analysis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
          
      - name: Run analysis
        uses: ossf/scorecard-action@v2
        with:
          results_file: results.sarif
          results_format: sarif
          publish_results: true
          
      - name: Upload results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: results.sarif

보안 메트릭 수집

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// security-metrics.js
const { Octokit } = require('@octokit/rest');

async function collectSecurityMetrics(owner, repo) {
  const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
  
  const metrics = {
    timestamp: new Date().toISOString(),
    vulnerabilities: {
      critical: 0,
      high: 0,
      medium: 0,
      low: 0
    },
    codeScanning: {
      openAlerts: 0,
      closedAlerts: 0
    },
    secretScanning: {
      openAlerts: 0,
      closedAlerts: 0
    },
    dependencies: {
      total: 0,
      outdated: 0,
      vulnerable: 0
    }
  };
  
  // Dependabot alerts
  const { data: vulnAlerts } = await octokit.dependabot.listAlertsForRepo({
    owner,
    repo,
    state: 'open'
  });
  
  vulnAlerts.forEach(alert => {
    metrics.vulnerabilities[alert.security_vulnerability.severity]++;
  });
  
  // Code scanning alerts
  const { data: codeAlerts } = await octokit.codeScanning.listAlertsForRepo({
    owner,
    repo
  });
  
  metrics.codeScanning.openAlerts = codeAlerts.filter(a => a.state === 'open').length;
  metrics.codeScanning.closedAlerts = codeAlerts.filter(a => a.state === 'closed').length;
  
  // Secret scanning alerts
  const { data: secretAlerts } = await octokit.secretScanning.listAlertsForRepo({
    owner,
    repo
  });
  
  metrics.secretScanning.openAlerts = secretAlerts.filter(a => a.state === 'open').length;
  
  return metrics;
}

// 대시보드용 데이터 생성
async function generateSecurityDashboard() {
  const metrics = await collectSecurityMetrics('org', 'repo');
  
  const dashboard = {
    title: 'Security Dashboard',
    lastUpdated: metrics.timestamp,
    summary: {
      totalVulnerabilities: Object.values(metrics.vulnerabilities).reduce((a, b) => a + b, 0),
      criticalIssues: metrics.vulnerabilities.critical,
      openAlerts: metrics.codeScanning.openAlerts + metrics.secretScanning.openAlerts
    },
    charts: {
      vulnerabilitiesBySeverity: metrics.vulnerabilities,
      alertsTrend: await getAlertsTrend(),
      dependencyHealth: metrics.dependencies
    }
  };
  
  return dashboard;
}

10. 인시던트 대응

보안 인시던트 대응 워크플로우

.github/workflows/incident-response.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
name: Security Incident Response

on:
  workflow_dispatch:
    inputs:
      incident_type:
        description: 'Type of incident'
        required: true
        type: choice
        options:
          - 'credential-leak'
          - 'vulnerability-exploit'
          - 'dependency-compromise'
          - 'unauthorized-access'
      severity:
        description: 'Severity level'
        required: true
        type: choice
        options:
          - 'critical'
          - 'high'
          - 'medium'
          - 'low'

jobs:
  respond:
    runs-on: ubuntu-latest
    steps:
      - name: Create incident issue
        uses: actions/github-script@v6
        with:
          script: |
            const issue = await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `[SECURITY] ${context.payload.inputs.incident_type} - ${new Date().toISOString()}`,
              body: `
                ## Security Incident Report
                
                **Type**: ${context.payload.inputs.incident_type}
                **Severity**: ${context.payload.inputs.severity}
                **Reported by**: @${context.actor}
                **Time**: ${new Date().toISOString()}
                
                ### Incident Response Checklist
                - [ ] Assess impact
                - [ ] Contain incident
                - [ ] Collect evidence
                - [ ] Eradicate threat
                - [ ] Recover systems
                - [ ] Document lessons learned
                
                ### Actions Taken
                <!-- Document all actions here -->
                
                ### Timeline
                <!-- Document timeline of events -->
              `,
              labels: ['security', 'incident', context.payload.inputs.severity],
              assignees: ['security-team']
            });
            
      - name: Notify security team
        if: inputs.severity == 'critical' || inputs.severity == 'high'
        run: |
          # Send notifications via multiple channels
          # Slack, email, PagerDuty, etc.
          
      - name: Enable additional monitoring
        run: |
          # Enable enhanced logging
          # Increase security scanning frequency
          # Deploy additional security controls
          
      - name: Generate incident report
        run: |
          # Create detailed incident report
          # Include all relevant logs and evidence

마무리

GitHub의 보안 기능들은 현대적인 DevSecOps 파이프라인의 핵심입니다.

핵심 포인트:

  • Dependabot으로 의존성 취약점 자동 관리
  • Secret scanning으로 민감 정보 노출 방지
  • Code scanning으로 코드 취약점 조기 발견
  • 포괄적인 보안 정책 수립
  • 자동화를 통한 지속적인 보안 강화

보안은 한 번 설정하고 끝나는 것이 아닌 지속적인 프로세스입니다. GitHub의 보안 기능들을 적극 활용하여 안전한 코드베이스를 유지하세요.

다음 편에서는 GitHub Packages를 활용한 패키지 관리에 대해 알아보겠습니다.

📚 GitHub 마스터하기 시리즈

🌱 기초편 (입문자)

  1. GitHub 시작하기
  2. Repository 기초
  3. Git 기본 명령어
  4. Branch와 Merge
  5. Fork와 Pull Request

💼 실전편 (중급자)

  1. Issues 활용법
  2. Projects로 프로젝트 관리
  3. Code Review 잘하기
  4. GitHub Discussions
  5. Team 협업 설정
  6. GitHub Pages

🚀 고급편 (전문가)

  1. GitHub Actions 입문
  2. Actions 고급 활용
  3. Webhooks와 API
  4. GitHub Apps 개발
  5. [보안 기능] (현재 글)(/posts/github-advanced-05-security-features/)
  6. GitHub Packages
  7. Codespaces
  8. GitHub CLI
  9. 통계와 인사이트

🏆 심화편 (전문가+)

  1. Git Submodules & Subtree
  2. Git 내부 동작 원리
  3. 고급 브랜치 전략과 릴리스 관리
  4. GitHub GraphQL API
  5. GitHub Copilot 완벽 활용

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.