2026年4月24日、CAMPFIREがGitHubアカウントへの不正アクセスを起点とした個人情報漏洩(最大225,846件・銀行口座情報約82,465件含む)を公表した。4月2日の侵入から約19日間、攻撃者はソースコードを閲覧し、4月21日には顧客管理データベースへの接続を完遂している。
GitHubアカウント1つを奪われ、3週間後に顧客DBが落ちた。この侵害経路はセキュリティ界では「既知の問題」だ。開発現場で繰り返される三つの穴——Secretの誤コミット・広すぎるトークンスコープ・DBのプライベート非隔離——が重なった結果として起きる。本記事はエンジニアがこの穴を塞ぐための実践的な対策を整理する。
今回の侵害はGitHub起点のサプライチェーン型攻撃と同じ構造を持ちます。攻撃手法の全体像は サプライチェーンセキュリティ完全ガイド2026|攻撃手法・防御ツール・実践チェックリスト で解説しています。
侵害の連鎖構造:なぜGitHub侵害がDBに波及するのか
CAMPFIREの事案で公表されている情報を元に、侵害の連鎖を推定する。GitHubへの不正アクセスを起点に内部DBへ到達するまでのステップは多くの場合、以下の流れをたどる。
乗っ取り"] --> B["リポジトリ内の
.env / config
ファイル閲覧"] B --> C["DB接続文字列
APIキー
Internal URLの取得"] C --> D["プライベートDB
or内部APIへ
直接接続"] D --> E["顧客データ
大量取得"] style A fill:#c0392b,color:#fff style E fill:#c0392b,color:#fff B:::vuln C:::vuln D:::vuln classDef vuln stroke:#e74c3c,stroke-width:2px
この連鎖を断ち切るには、それぞれのステップに対する独立した防御が必要だ。どれか一つだけを直しても「多段防御」にならない。
| 侵害ステップ | 根本原因 | 対策カテゴリ |
|---|---|---|
| GitHubアカウント乗っ取り | 2FA未設定・PATの過剰スコープ | 認証強化・最小権限 |
| リポジトリ内のSecret漏洩 | .envの誤コミット・ハードコード |
Secret管理 |
| 内部DBへの直接接続 | DBがパブリック公開・Security Group開放 | ネットワーク隔離 |
| データ大量取得 | 検知・ログ不足・IAMの過剰権限 | ロギング・監視 |
対策①:Secretをリポジトリから追い出す
最初に塞ぐべき穴は「ソースコードにSecretが混入しているか」だ。
.envファイルの誤コミットを防ぐ
.gitignoreに追加するだけでは不十分だ。すでにコミット履歴に入ってしまっているケースを想定した二重の防壁が必要となる。
# .gitignoreに追加(最低限)
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo ".env.*.local" >> .gitignore
echo "*.pem" >> .gitignore
echo "credentials.json" >> .gitignore
echo ".aws/credentials" >> .gitignore
# git-secretsでコミット前チェックを強制する(推奨)
brew install git-secrets
git secrets --install # hooksをローカルリポジトリに設置
git secrets --register-aws # AWS系のパターンを自動検出
# pre-commitフレームワークを使う場合
pip install pre-commit
# .pre-commit-config.yaml(リポジトリルートに置く)
repos:
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
すでにコミット履歴に入ってしまったSecretを削除する
# git filter-repo を使ったSecret履歴削除(BFGより推奨)
pip install git-filter-repo
# 対象ファイルをすべてのコミット履歴から削除
git filter-repo --path .env --invert-paths
# または特定の文字列をコミット履歴から置換
git filter-repo --replace-text replacements.txt
# replacements.txt の内容: AKIAIOSFODNN7EXAMPLE==>REMOVED_AWS_KEY
# 完了後、force push(チームに事前周知が必要)
git push origin --force --all
git push origin --force --tags
コミット履歴からSecretを削除しても、過去にリポジトリをクローンした人のローカルコピーには残ります。漏洩が疑われる場合は削除と同時にSecretを無効化し、新しいキーを発行してください。「削除したから安全」ではありません。
truffleHogで既存リポジトリを全スキャンする
# truffleHogで全コミット履歴をスキャン
pip install trufflehog
trufflehog git https://github.com/your-org/your-repo --only-verified
# ローカルリポジトリをスキャン
trufflehog filesystem /path/to/repo
# GitHub Organization全体をスキャン
trufflehog github --org=your-org --token=ghp_xxxx
Gemini APIキーが不正利用される仕組みと防止策でも解説しているが、一度コミットされたSecretはスキャナーによって秒単位で検出される。truffleHogをCI/CDに組み込み、プッシュのたびに全履歴をスキャンする体制が理想だ。
対策②:GitHub権限を最小化する
GitHubアカウントが侵害されたとき、攻撃者が「何にアクセスできるか」は付与されている権限の幅に依存する。
Classic PATを廃止してFine-grained PATへ移行する
Classic Personal Access Tokenはスコープが粗く、一つのトークンで組織全体のリポジトリに対してrepo権限を持ってしまう。Fine-grained PATではリポジトリ単位・操作種別単位でスコープを絞れる。
# Fine-grained PATの作成手順(GitHub CLI経由)
# GitHub CLIでリポジトリ指定・権限限定のトークンを作成
gh auth refresh -s admin:org
# または GitHub Web UI: Settings > Developer settings >
# Personal access tokens > Fine-grained tokens > Generate new token
# - Repository access: Selected repositories のみ
# - Permissions: 必要な最小権限だけ選択
# (例: Contents: Read-only, Pull requests: Read and write)
| PATの種類 | スコープ粒度 | リポジトリ指定 | 推奨用途 |
|---|---|---|---|
| Classic PAT | スコープ単位(repoで全リポジトリ) |
不可 | 廃止推奨 |
| Fine-grained PAT | 権限種別×リポジトリ単位 | 可能 | 個人・Bot用途全般 |
| GitHub Apps | Installationスコープ | Org/Repo単位 | CI/CD・自動化 |
| GITHUB_TOKEN | ワークフロー単位 | 実行リポジトリのみ | GitHub Actions |
GitHub OrganizationのSSO・SCIM・2FAを強制する
# .github/settings.yml(branch protection rules の例)
# GitHub Managed User / Enterprise でSAML SSOを強制するには
# OrganizationのSecurity設定から有効化
# ブランチ保護ルール(GitHub REST API での設定)
# PUT /repos/{owner}/{repo}/branches/{branch}/protection
{
"required_status_checks": {
"strict": true,
"contexts": ["ci/security-scan"]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true
},
"restrictions": null,
"required_linear_history": true,
"allow_force_pushes": false,
"allow_deletions": false
}
Settings > Security: Two-factor authentication requirement(全メンバーに2FA強制)、SAML SSO(Enterprise)
Settings > Actions > General: Fork pull requestsのワークフロー実行を制限、third-party Actionの許可リスト制御
対策③:GitHub OIDCで静的キーを排除する
GitHub ActionsからAWSやGCPにアクセスするとき、静的なアクセスキー(AWS_ACCESS_KEY_ID等)をSecretsに保存するのはリスクが高い。Secretsに保存されたキーはワークフローのログや環境変数ダンプから露出する可能性がある。
GitHub OIDC + AWS IAMの構成では、ワークフローごとにOIDCトークンが発行され、AWSが検証した上で一時的な認証情報を払い出す。静的キーをGitHubに保存する必要がなくなる。
# .github/workflows/deploy.yml — OIDC認証の実装例
name: Deploy
on:
push:
branches: [main]
permissions:
id-token: write # OIDCトークン発行に必要
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
aws-region: ap-northeast-1
# AWS_ACCESS_KEY_ID は不要。静的キーをSecretsに保存しない
- name: Deploy to S3
run: aws s3 sync ./dist s3://my-bucket/
# Terraform — GitHub OIDC用 IAMロールの設定
resource "aws_iam_role" "github_actions" {
name = "GitHubActionsDeployRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
# mainブランチからのワークフローのみ許可
"token.actions.githubusercontent.com:sub" = "repo:your-org/your-repo:ref:refs/heads/main"
}
}
}]
})
}
AWS Secrets ManagerとParameter Storeの使い分け
OIDCでAWSにアクセスできるようになったら、DBパスワードやAPIキーはAWS Secrets Managerで管理する。ランタイムでSecretを取得し、コードにもCI/CDにもキーを置かない構成が理想だ。
# Python — ランタイムでAWS Secrets Managerからキーを取得
import boto3
import json
def get_secret(secret_name: str, region: str = "ap-northeast-1") -> dict:
client = boto3.client("secretsmanager", region_name=region)
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response["SecretString"])
# 使用例: DBパスワードをコードに書かず、実行時に取得
db_secret = get_secret("prod/myapp/db")
DATABASE_URL = f"postgresql://{db_secret['username']}:{db_secret['password']}@{db_secret['host']}/mydb"
| サービス | 用途 | 自動ローテーション | コスト |
|---|---|---|---|
| AWS Secrets Manager | DBパスワード・APIキーなど機密度の高い値 | あり(RDS自動ローテーション対応) | $0.40/シークレット/月 |
| AWS Parameter Store (Standard) | 設定値・非機密のパラメータ | なし | 無料(標準)〜 |
| AWS Parameter Store (Advanced) | 機密度中程度の設定値 | あり | $0.05/パラメータ/月 |
| HashiCorp Vault | マルチクラウド・オンプレ環境 | あり(動的シークレット) | OSS無料〜Enterprise有料 |
対策④:DBをプライベートネットワークに隔離する
GitHubからDBへの直接接続を防ぐ最も確実な手段は、DBをパブリックインターネットから完全に切り離すことだ。
AWS RDS/Auroraのプライベート配置
# Terraform — RDSをプライベートサブネットに配置する最小構成
resource "aws_db_subnet_group" "private" {
name = "myapp-private-db-subnet"
subnet_ids = aws_subnet.private[*].id # プライベートサブネットのみ
}
resource "aws_security_group" "rds" {
name = "rds-sg"
vpc_id = aws_vpc.main.id
# アプリケーションのSGからのみ接続を許可
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.app.id] # CIDRブロックではなくSGを指定
}
# アウトバウンドは制限しない(必要に応じて絞る)
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_db_instance" "main" {
identifier = "myapp-db"
engine = "postgres"
instance_class = "db.t3.medium"
db_subnet_group_name = aws_db_subnet_group.private.name
vpc_security_group_ids = [aws_security_group.rds.id]
# 重要: パブリックアクセスを明示的に禁止
publicly_accessible = false
# 削除保護を有効化
deletion_protection = true
# 自動バックアップ(7日)
backup_retention_period = 7
}
開発者がローカルからアクセスする方法
DBをプライベートにすると「開発者がローカルからアクセスできなくなる」問題が生じる。SSHトンネルやSSM Session Managerで安全にアクセスできる。
# AWS SSM Session Managerでポートフォワードする(推奨)
# SSHキーが不要でAuditログも残る
# EC2インスタンス経由でRDSにポートフォワード
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{
"host": ["myapp-db.xxxxxx.ap-northeast-1.rds.amazonaws.com"],
"portNumber": ["5432"],
"localPortNumber": ["5432"]
}'
# 別ターミナルで接続(ローカルの5432に転送されている)
psql -h localhost -U dbuser -d mydb
ローカル PC"] Attacker["攻撃者
(GitHub経由)"] end subgraph VPC["VPC(プライベート)"] subgraph PublicSubnet["パブリックサブネット"] Bastion["Bastion
or SSM Endpoint"] end subgraph PrivateSubnet["プライベートサブネット"] App["アプリケーション
(ECS/Lambda)"] RDS[("RDS
publicly_accessible=false")] end end Dev -->|"SSM Port Forward"| Bastion Bastion --> RDS App --> RDS Attacker -->|"❌ 直接接続不可"| RDS style Attacker fill:#c0392b,color:#fff style RDS fill:#27ae60,color:#fff
対策⑤:GitHub Actionsのシークレット漏洩パターンを塞ぐ
CI/CDパイプラインはSecret管理の弱点になりやすい。GitHub Actionsで発生するシークレット漏洩の主なパターンを整理する。
パターン1:echoでSecretがログに露出する
# NG: こう書くとSecretがGitHubのActionsログに出力される
- name: Deploy
run: |
echo "Deploying with key: $" # ← ログに残る
curl -H "Authorization: $API_KEY" https://api.example.com/deploy
# OK: Secretは環境変数経由で渡し、echoしない
- name: Deploy
env:
API_KEY: $
run: |
curl -H "Authorization: $API_KEY" https://api.example.com/deploy
パターン2:フォークPRワークフローでのSecret漏洩
# NG: pull_request_targetイベントで外部コードを実行するとSecretが漏洩する
on:
pull_request_target: # ← フォーク元のSecretsにアクセスできてしまう
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: $ # ← フォーク先のコードを実行
- run: npm test # フォーク先の悪意あるpackage.jsonが実行される
# OK: フォークPRには最小権限のみ付与
on:
pull_request: # pull_request はSecretsにアクセスできない
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read # 必要最小限
steps:
- uses: actions/checkout@v4
- run: npm test
パターン3:サードパーティActionのタグ固定をしていない
# NG: タグ(v4)はいつでも書き換えられる。タグハイジャック攻撃が成立する
- uses: actions/checkout@v4
# OK: コミットハッシュで固定する(イミュータブル)
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Renovate・Dependabotを悪用したサプライチェーン攻撃の仕組みと防御策でも解説しているように、サードパーティの依存関係を信頼しすぎることがサプライチェーン攻撃の起点になる。
GITHUB_TOKENのデフォルト権限を制限する
# .github/workflows/ci.yml — ワークフロー全体で最小権限を宣言
name: CI
on: [push, pull_request]
# デフォルトのpermissionsをread-onlyに設定
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
# ジョブ単位でも最小権限を宣言(ワークフロー宣言を上書き)
permissions:
contents: read
checks: write # テスト結果のAnnotationを書くために必要
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- run: npm ci && npm test
# Organization設定でデフォルト権限を制限する(Settings > Actions > General)
# "Workflow permissions" を "Read repository contents and packages permissions" に設定
# これでGITHUB_TOKENのデフォルトはread-onlyになる
対策⑥:GitHub Actionsセキュリティのチェックリスト
上記の対策を実施状況で整理する。
# actionlintでワークフローファイルの静的解析
brew install actionlint
actionlint .github/workflows/*.yml
# OpenSSF ScorecardでリポジトリのセキュリティスコアをチェックCSS
docker run -e GITHUB_AUTH_TOKEN=ghp_xxx \
gcr.io/openssf/scorecard:stable \
--repo=github.com/your-org/your-repo \
--show-details
| チェック項目 | コマンド・設定場所 | 重要度 |
|---|---|---|
| Classic PATの廃止確認 | Settings > Developer settings > Classic tokens | 🔴 高 |
| Org全メンバーに2FA強制 | Org Settings > Security > Require 2FA | 🔴 高 |
| リポジトリにSecretが混入していないか | trufflehog git <repo> |
🔴 高 |
| DBのpublicly_accessible=falseを確認 | AWS Console / Terraform tfstate | 🔴 高 |
| GitHub OIDC導入(静的AWSキーの削除) | GitHub Secrets一覧 + IAMロール確認 | 🟡 中 |
| サードパーティActionのコミットハッシュ固定 | ワークフローファイル目視 or actionlint | 🟡 中 |
| GITHUB_TOKENのデフォルト権限をread-only | Org Settings > Actions > Permissions | 🟡 中 |
| pre-commitでSecretスキャン | .pre-commit-config.yaml |
🟡 中 |
| ブランチ保護ルールでforce push禁止 | リポジトリ Settings > Branches | 🟢 低 |
| Dependabotでサードパーティ更新を自動化 | .github/dependabot.yml |
🟢 低 |
対策⑦:侵入後に「何をされたか」を把握できる体制を作る
侵入を防ぐ対策と同じくらい重要なのが、侵入された後に何が起きたかを正確に追跡できるようにしておくことだ。CAMPFIREの事案では4月2日の侵入から4月21日のDB接触確認まで約19日間かかった。もしDB監査ログが有効でなければ、攻撃者が「何を読んだか・何を変えたか」の特定はさらに困難になる。
侵入は必ず起きる前提でログを設計する。インシデント対応(DFIR: Digital Forensics and Incident Response)では「ログの不在」が被害範囲の不明確化につながり、報告義務の履行を遅らせる最大の要因になる。
DB監査ログの有効化
AWS RDS/Aurora(PostgreSQL)
-- PostgreSQLの監査拡張: pgauditを有効化
-- RDSパラメータグループに以下を設定:
-- shared_preload_libraries = pgaudit
-- pgaudit.log = 'read,write,ddl,role'
-- pgaudit.log_catalog = on
-- pgaudit.log_parameter = on
-- 有効化確認
SHOW pgaudit.log;
-- 出力例: read,write,ddl,role
-- 記録されるイベントの例
-- SESSION: 接続・切断
-- READ: SELECT文(誰がどのテーブルを読んだか)
-- WRITE: INSERT/UPDATE/DELETE
-- DDL: CREATE/ALTER/DROP
-- ROLE: GRANT/REVOKE
# RDSパラメータグループをAWS CLIで設定
aws rds modify-db-parameter-group \
--db-parameter-group-name myapp-pg-params \
--parameters \
"ParameterName=shared_preload_libraries,ParameterValue=pgaudit,ApplyMethod=pending-reboot" \
"ParameterName=pgaudit.log,ParameterValue=read\,write\,ddl\,role,ApplyMethod=immediate"
# RDSのログをCloudWatch Logsにエクスポート
aws rds modify-db-instance \
--db-instance-identifier myapp-db \
--cloudwatch-logs-export-configuration EnableLogTypes=postgresql,upgrade
AWS RDS(MySQL)
-- MySQL: general_log と audit_log プラグインの有効化
-- RDSパラメータグループで設定:
-- general_log = 1
-- slow_query_log = 1
-- long_query_time = 1 (1秒以上のクエリを記録)
-- audit_logプラグイン(MySQL Enterprise / Percona)
INSTALL PLUGIN audit_log SONAME 'audit_log.so';
SET GLOBAL audit_log_policy = 'ALL';
SET GLOBAL audit_log_format = 'JSON';
-- 接続ログの確認
SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 20;
CloudTrailとCloudWatch Logsで証跡を保全する
RDSへのAPIコール(設定変更・スナップショット・パラメータ変更等)はCloudTrailで自動的に記録される。これに加えてCloudWatch Logsへのログ転送とアラートを設定する。
# Terraform — CloudTrail + S3 Object Lock でログ改竄防止
resource "aws_cloudtrail" "main" {
name = "myapp-cloudtrail"
s3_bucket_name = aws_s3_bucket.cloudtrail_logs.id
include_global_service_events = true
is_multi_region_trail = true
enable_log_file_validation = true # ログファイルの整合性検証を有効化
event_selector {
read_write_type = "All"
include_management_events = true
# S3へのデータアクセスも記録(オプション)
data_resource {
type = "AWS::S3::Object"
values = ["arn:aws:s3:::myapp-prod-*/*"]
}
}
}
# S3 Object Lock(ログの改竄・削除を防ぐ)
resource "aws_s3_bucket_object_lock_configuration" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail_logs.id
rule {
default_retention {
mode = "GOVERNANCE" # 管理者でも指定期間は削除不可
days = 365
}
}
}
本番アカウントが侵害された場合、同アカウント内のCloudTrailログも改竄・削除される可能性があります。ログ専用のAWSアカウント(Security/Log Archive Account)に転送することで、攻撃者によるログの消去を防ぎます。AWS Organizationsのマルチアカウント構成が理想です。
インシデント発生時のフォレンジック手順
侵害を検知してから「タイムラインの再構成」と「影響範囲の特定」を進める手順を整理する。
# 1. CloudTrailからRDS関連の操作ログをJSONで取得
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::RDS::DBInstance \
--start-time "2026-04-01T00:00:00Z" \
--end-time "2026-04-25T00:00:00Z" \
--max-results 100 \
| jq '.Events[] | {EventTime, EventName, Username: .CloudTrailEvent | fromjson | .userIdentity.arn}'
# 2. RDSのPostgreSQLログからCloudWatch Logsで特定期間のクエリを検索
aws logs filter-log-events \
--log-group-name "/aws/rds/instance/myapp-db/postgresql" \
--start-time 1743465600000 \ # Unixタイムスタンプ (ms)
--end-time 1745884800000 \
--filter-pattern "ERROR" \
| jq '.events[].message'
# 3. 異常な接続元IPを特定
aws logs filter-log-events \
--log-group-name "/aws/rds/instance/myapp-db/postgresql" \
--filter-pattern '"connection received"' \
| jq '.events[].message' | sort | uniq -c | sort -rn | head -20
# 4. 特定テーブルへのSELECT(pgaudit有効時)を抽出
aws logs filter-log-events \
--log-group-name "/aws/rds/instance/myapp-db/postgresql" \
--filter-pattern '"AUDIT: SESSION" "users" "READ"'
(Secretローテーション・
アカウント停止)"] B --> C["タイムライン再構成"] C --> D["CloudTrailで
APIコール履歴を確認"] C --> E["RDS監査ログで
クエリ履歴を確認"] C --> F["GitHubAudit Logで
リポジトリ操作を確認"] D & E & F --> G["影響範囲の特定
(何のデータを・いつ・どれだけ)"] G --> H["個人情報保護委員会への
報告(72時間以内)"] G --> I["被害者への通知"] style A fill:#c0392b,color:#fff style H fill:#e67e22,color:#fff style I fill:#e67e22,color:#fff
SIEM連携:ログを集約して異常を早期検知する
複数のログソースを一元管理し、相関分析で侵害の兆候を早期に発見する。
# OpenSearch(ELK)へのCloudWatch Logsサブスクリプション設定例
# または Amazon Security Lakeを使う場合
# CloudWatch Logs → Kinesis Data Firehose → OpenSearch
# の構成でリアルタイムログ集約
# 検知すべき異常パターンの例
alert_rules:
- name: "RDS異常接続"
condition: >
RDSアクセス元IPが通常のアプリケーションサーバーSG以外から来ている
action: SNS通知 → PagerDuty
- name: "深夜の大量SELECT"
condition: >
業務時間外(22:00-06:00 JST)に大量のSELECT文(>10,000行)が実行された
action: SNS通知 → SlackアラートChannel
- name: "GitHubからの直接DB接続"
condition: >
RDSのVPC Flow Logsで、GitHub ActionsのIPレンジからの接続が確認された
action: 即座にアラート + 自動遮断
| ログソース | 保存先 | 保存期間 | 改竄防止 |
|---|---|---|---|
| CloudTrail(APIコール) | S3 + Object Lock | 1年以上 | Object Lock (GOVERNANCE) |
| RDS監査ログ(pgaudit) | CloudWatch Logs → S3 | 90日〜1年 | 別アカウントS3 |
| VPC Flow Logs | CloudWatch Logs or S3 | 90日 | 別アカウントS3 |
| GitHub Audit Log | GitHub Enterprise or外部SIEM | 180日(GitHub側) | SIEM転送 |
| アプリケーションログ | CloudWatch Logs | 30〜90日 | ロール制限 |
インシデント発生時の初動:エンジニアが今すぐやること
自社システムでGitHub不正アクセスやSecret漏洩が疑われる場合の初動を整理する。
GitHub侵害が判明した時点でまず「影響を受けた可能性のあるすべてのSecretをローテーションする」を優先してください。調査と並行してローテーションを進め、漏洩有無の確定を待たないことが重要です。
緊急対応チェックリスト(発覚後24時間以内):
[ ] GitHubアカウントのパスワードをすぐに変更し、全セッションを失効させる
[ ] すべてのPersonal Access Tokenを失効させる(Settings > Developer settings)
[ ] 不審なOAuthアプリ認証を失効させる(Settings > Applications > Authorized OAuth Apps)
[ ] 影響を受けた可能性のあるAPIキー・DBパスワード・認証情報をすべてローテーション
[ ] AWSであれば CloudTrail でアクセスログを確認(想定外のAPIコールがないか)
[ ] GitHubのAudit Log(Organization)で不審な操作がないか確認
[ ] 個人情報保護委員会への報告要否を確認(規模・内容によって義務あり)
[ ] 警察への相談(不正アクセス禁止法違反の被害届)
GitHubのAudit Logの確認方法
# GitHub CLIでOrganizationのAudit Logを確認
gh api \
-H "Accept: application/vnd.github+json" \
"/orgs/{org}/audit-log?phrase=action:repo.access&per_page=100" \
| jq '.[] | {created_at, action, actor, repo}'
# 確認すべきアクション
# repo.access : リポジトリへのアクセス
# repo.download : リポジトリのダウンロード(zip export)
# hook.create/destroy : Webhookの変更(データ外部送信の可能性)
# org.add_member : 不審なメンバー追加
# repo.create : 見知らぬリポジトリの作成
よくある質問
Q. Secret Scanningをリポジトリで有効にすれば十分ですか?
GitHub Secret Scanningは既知のパターン(AWSキー、GitHubトークン等)を自動検出しますが、カスタムのDBパスワードや社内APIキーは検出できません。Secret Scanningは「最後の砦」であり、プッシュ前のpre-commitスキャン(gitleaks・detect-secrets)と組み合わせる必要があります。
Q. HashiCorp VaultとAWS Secrets Managerはどちらを選ぶべきですか?
AWSのみを使用しているなら Secrets Manager が低運用コストでおすすめです。マルチクラウドやオンプレ環境が混在する場合はVaultが適しています。VaultはOSSとして自己ホストできますが運用負荷が高いため、小規模チームにはSecretsManagerが現実的です。
Q. プライベートリポジトリなら.envをコミットしても大丈夫ですか?
大丈夫ではありません。プライベートリポジトリへのアクセス権を持つアカウント(チームメンバー・Bot・GitHub App)が侵害された場合、リポジトリ内のSecretも全て漏洩します。今回のCAMPFIREの事案はまさにこのパターンです。
Q. Fine-grained PATへの移行でCIが壊れませんか?
CI/CDでClassic PATを使っている場合、Fine-grained PATへ移行する際にスコープを絞る必要があるため、必要な権限を一つずつ確認する手間が発生します。ただしGitHub ActionsではGITHUB_TOKENやGitHub Appsを使う方が管理しやすいため、CIはGITHUB_TOKENやOIDCに移行し、Fine-grained PATは手動操作用に限定するのが現実的なアプローチです。
参照ソース
- 【重要】弊社システムへの不正アクセスによる個人情報漏えいの可能性に関するお詫びとご報告 — 株式会社CAMPFIRE(2026年4月24日)
- GitHubアカウントへの不正アクセス発生に関するお知らせとお詫び — 株式会社CAMPFIRE(2026年4月3日)
- GitHubアカウントへの不正アクセス発生に関するお知らせとお詫び(第三報)— 株式会社CAMPFIRE(2026年4月22日)
- CAMPFIRE、最大22.5万人分の個人情報漏えいか 一部口座情報も — ITmedia NEWS(2026年4月24日)
- Using OpenID Connect in GitHub Actions — GitHub Docs
- Security hardening for GitHub Actions — GitHub Docs
- Fine-grained personal access tokens — GitHub Docs
- AWS Secrets Manager ユーザーガイド — AWS Documentation
- truffleHog — GitHub Secret Scanner
- OpenSSF Scorecard — セキュリティスコア評価