go-security-audit について
go-security-auditは、Go開発者が変更箇所やコードベースを調べるためのセキュリティ監査スキルです。スキャナーの結果と手動確認を組み合わせ、重要度順に指摘をまとめます。
主な機能
- 01対象限定・差分・全面監査モード
- 02govulncheck、gosec、go vetの実行案内
- 03入力処理、インジェクション、アクセス制御の確認
- 04シークレット、TLS、ヘッダー、レート制限、ログの確認
- 05指摘にファイル行番号、入力経路、修正案を記載
活用シーン
- 01PRのマージ前にGoの変更をレビュー
- 02Goサービスの一般的なセキュリティ上の問題を監査
- 03認証や入力処理の経路を確認
- 04優先度付きのセキュリティ報告を作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ eduardo-sl/ go-agent-skills --skill go-security-auditインストール後、Claude Code で/go-security-auditを入力して読み込まれたか確認します。
詳細
Go セキュリティ監査
セキュリティは機能ではなく、特性です。コードの各行が、セキュリティを維持するか、損なうかのどちらかです。
実施モード
開始前に、依頼に合ったモードを選んでください。
- 対象を絞ったチェック — 単一の懸念事項(「このクエリはインジェクションの危険がありますか?」「この認証ミドルウェアをレビューしてください」など)。該当するセクションのみを適用します。
- 差分監査 — PR または作業ツリーで変更された行について、以下のすべての懸念事項を監査します。
- 全体監査(「サービスのセキュリティレビュー」の既定)—「大規模コードベースの監査」にある並行パスを使用して、コードベース全体を調べます。
まずスキャナーを実行する
手動レビューの前に、自動スキャナーを実行し、その出力を検出事項に反映してください(インストールされていないものはスキップし、その旨を記載してください)。
govulncheck ./... # known CVEs actually reachable from your code
gosec ./... # static analysis for insecure patterns
go vet ./... # includes some security-relevant checks
スキャナーは既知のパターンを検出します。以下の手動パスでは、スキャナーでは見つけられないロジック上の欠陥を探します。
大規模コードベースの監査
以下の各番号付きセクションは、それぞれ独立した監査パスです。およそ20ファイルを超えるコードベースでは、次の手順に従ってください。
- まず攻撃対象領域を特定します。HTTP/gRPC ハンドラー、CLI エントリーポイント、キューのコンシューマー、外部入力を解析するものなどが対象です。
- 懸念事項ごとに1つのパスを実行します:(a)入力検証とインジェクション、(b)認証と認可、(c)シークレットと暗号化、(d)TLS とセキュリティヘッダーとレート制限、(e)ログの適切な取り扱い。
- 環境で並行するサブエージェントまたはタスクへの作業委任がサポートされている場合は、各パスをそれぞれ1つに割り当てます。各パスは重複しません。サポートされていない場合は、順番に実行します。
- 各検出事項には、
file.go:line、脆弱な入力経路、および具体的な修正方法を記載する必要があります。重大度順に並べた1つのレポートにまとめます。
1. 入力検証
ユーザー入力は決して信頼しないでください。境界で検証します。
// ✅ Good — validate before use
func (h *Handler) handleCreate(w http.ResponseWriter, r *http.Request) {
// Limit body size
r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MB
var req CreateRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondError(w, http.StatusBadRequest, "invalid JSON")
return
}
if err := validate.Struct(req); err != nil {
respondError(w, http.StatusBadRequest, "validation failed")
return
}
// proceed with validated data
}
文字列のサニタイズ:
// Sanitize HTML to prevent XSS
import "github.com/microcosm-cc/bluemonday"
p := bluemonday.UGCPolicy()
sanitized := p.Sanitize(userInput)
// Validate email format
import "net/mail"
_, err := mail.ParseAddress(email)
// Validate URLs
u, err := url.Parse(input)
if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
// reject
}
2. SQL インジェクションの防止
必ずパラメーター化クエリを使用してください。
// ✅ Good — parameterized
row := db.QueryRowContext(ctx,
"SELECT id, name FROM users WHERE email = $1", email)
// ✅ Good — with sqlx named params
query := "SELECT * FROM users WHERE name = :name AND age > :age"
rows, err := db.NamedQueryContext(ctx, query, map[string]interface{}{
"name": name,
"age": minAge,
})
// ❌ CRITICAL — string concatenation = SQL injection
query := "SELECT * FROM users WHERE email = '" + email + "'"
query := fmt.Sprintf("SELECT * FROM users WHERE id = %s", id)
動的クエリ:
動的な WHERE 句を構築する場合は、クエリビルダーまたは安全な連結を使用してください。
// ✅ Good — safe dynamic query building
var conditions []string
var args []interface{}
argIdx := 1
if name != "" {
conditions = append(conditions, fmt.Sprintf("name = $%d", argIdx))
args = append(args, name)
argIdx++
}
query := "SELECT * FROM users"
if len(conditions) > 0 {
query += " WHERE " + strings.Join(conditions, " AND ")
}
3. 認証と認可
パスワードの取り扱い:
import "golang.org/x/crypto/bcrypt"
// Hash password
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
// Verify password — constant-time comparison built in
err := bcrypt.CompareHashAndPassword(hash, []byte(password))
平文のパスワードは決して保存しないでください。パスワードに MD5/SHA を決して使用しないでください。
JWT の検証:
// ✅ Always validate:
// 1. Signature (algorithm must match expectation)
// 2. Expiration (exp claim)
// 3. Issuer (iss claim)
// 4. Audience (aud claim)
// ❌ CRITICAL — never disable signature verification
// ❌ CRITICAL — never accept "alg": "none"
// ❌ CRITICAL — never hardcode signing keys in source code
認可ミドルウェア:
func RequireRole(role string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := UserFromContext(r.Context())
if user == nil || !user.HasRole(role) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}
4. シークレット管理
ルール:
- 🔴 ソースコードにシークレット、トークン、API キーをハードコードしないでください
- 🔴 「test」ファイルであっても、シークレットを git にコミットしないでください
- 🔴 シークレット、トークン、パスワードをログに記録しないでください
// ✅ Good — from environment
dbURL := os.Getenv("DATABASE_URL")
// ✅ Good — from secrets manager
secret, err := secretsManager.GetSecret(ctx, "api-key")
// ❌ CRITICAL
const apiKey = "sk-1234567890abcdef" // hardcoded secret
.gitignore を使用する:
.env
*.pem
*.key
credentials.json
漏えいしたシークレットをスキャンする:
# Use gitleaks in CI
gitleaks detect --source=. --verbose
5. HTTP セキュリティヘッダー
func SecurityHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("Content-Security-Policy", "default-src 'self'")
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
w.Header().Set("X-XSS-Protection", "0") // modern browsers handle this
next.ServeHTTP(w, r)
})
}
6. TLS 設定
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
PreferServerCipherSuites: true,
}
srv := &http.Server{
TLSConfig: tlsConfig,
// ...
}
7. レート制限
import "golang.org/x/time/rate"
type RateLimiter struct {
limiters sync.Map
rate rate.Limit
burst int
}
func (rl *RateLimiter) Allow(key string) bool {
limiter, _ := rl.limiters.LoadOrStore(key,
rate.NewLimiter(rl.rate, rl.burst))
return limiter.(*rate.Limiter).Allow()
}
認証エンドポイント、公開 API、およびリソースを大量に消費する処理にレート制限を適用してください。
8. ログのセキュリティ
// ❌ CRITICAL — logging sensitive data
log.Printf("user login: email=%s password=%s", email, password)
log.Printf("auth token: %s", token)
log.Printf("request body: %v", req) // may contain secrets
// ✅ Good — redact sensitive fields
log.Printf("user login: email=%s", email)
logger.Info("auth completed", slog.String("user_id", userID))
セキュリティ監査チェックリスト
重大(🔴 BLOCKER)
- SQL インジェクションの攻撃経路がない(すべてのクエリがパラメーター化されている)
- ハードコードされたシークレット/キー/トークンがない
- 平文のパスワードが保存されていない
- TLS 証明書の検証が無効化されていない
- リクエスト本文のサイズが制限されている
- JWT の署名が検証され、
alg: noneが拒否される
重要(🟡 WARNING)
- すべての外部データで入力検証が行われている
- 認証エンドポイントと公開エンドポイントにレート制限がある
- すべてのレスポンスにセキュリティヘッダーが設定されている
- CORS が制限的に設定されている
- エラーメッセージから内部情報が漏えいしない
- 認証イベントが監査ログに記録されている
推奨(🟢 SUGGESTION)
- CI パイプラインで
govulncheckを実行する - シークレットスキャンに
gitleaksを使用する - レダクションを適用した構造化ログを使用する
- 検証済みチェックサムを使って依存関係のバージョンを固定する
コメント
Markdown 対応