gosec

Table of Contents

  1. 解決する問題
  2. 設定
  3. オプション
  4. 主要なルール
    1. 認証情報・機密情報
    2. SQL・コマンドインジェクション
    3. ファイル操作
    4. 暗号
    5. その他
  5. サンプル
    1. 検出例
    2. 修正例
  6. 注意点
  7. 参考リンク

gosec は Go コードのセキュリティ上の問題を静的解析で検出するツールです。

解決する問題

SQL インジェクション、ハードコードされた認証情報、安全でない暗号アルゴリズムの使用など、コードレビューで見落としがちなセキュリティ脆弱性を自動的に検出します。

1
2
3
4
5
6
7
8
9
10
11
12
// Before: SQL インジェクションの脆弱性
func FindUser(db *sql.DB, name string) (*User, error) {
// ユーザー入力を直接 SQL に埋め込んでいる
query := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name)
row := db.QueryRow(query)

var user User
if err := row.Scan(&user.ID, &user.Name); err != nil {
return nil, fmt.Errorf("scan user: %w", err)
}
return &user, nil
}
1
2
3
4
5
6
7
8
9
10
// After: パラメータ化クエリで安全に
func FindUser(db *sql.DB, name string) (*User, error) {
row := db.QueryRow("SELECT * FROM users WHERE name = $1", name)

var user User
if err := row.Scan(&user.ID, &user.Name); err != nil {
return nil, fmt.Errorf("scan user: %w", err)
}
return &user, nil
}

設定

公式ドキュメント

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# .golangci.yml 設定例
version: "2"

linters:
enable:
- gosec
settings:
gosec:
includes:
- G101 # ハードコードされた認証情報
- G201 # SQL インジェクション(format string)
- G202 # SQL インジェクション(文字列結合)
- G204 # コマンド実行
- G304 # パストラバーサル
- G401 # 弱いハッシュアルゴリズム(MD5, SHA1)
- G402 # 安全でない TLS 設定
- G404 # 安全でない乱数生成
- G501 # crypto/md5 のインポート

オプション

オプション デフォルト 説明
includes []string [](全ルール) チェックするルール ID のリスト
excludes []string [] 除外するルール ID のリスト
exclude-generated bool false 生成されたコードを除外する
severity string "" 最小重大度でフィルタ(low, medium, high
confidence string "" 最小信頼度でフィルタ(low, medium, high
config map {} ルールごとの詳細設定

主要なルール

認証情報・機密情報

ルール 検出内容
G101 ハードコードされた認証情報(パスワード、API キー、トークン)

SQL・コマンドインジェクション

ルール 検出内容
G201 fmt.Sprintf を使った SQL クエリ構築
G202 文字列結合を使った SQL クエリ構築
G203 HTML テンプレートでのエスケープされていないデータ
G204 外部コマンド実行(exec.Command に変数を渡す)

ファイル操作

ルール 検出内容
G301 ディレクトリ作成時の不適切なパーミッション
G302 ファイル作成・chmod 時の不適切なパーミッション
G304 ユーザー入力によるファイルパス指定(パストラバーサル)
G305 ZIP 展開時のパストラバーサル
G306 ファイル書き込み時の不適切なパーミッション

暗号

ルール 検出内容
G401 MD5 / SHA1 の使用
G402 安全でない TLS 設定
G403 RSA 鍵長が 2048 ビット未満
G404 math/rand の使用(crypto/rand を使うべき)

その他

ルール 検出内容
G104 未チェックのエラー
G107 HTTP リクエストの URL にユーザー入力(SSRF)
G112 ReadHeaderTimeout 未設定の HTTP サーバー
G115 整数型変換によるオーバーフロー

サンプル

検出例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// G101: ハードコードされた認証情報
const apiKey = "sk-proj-ABCDEF123456" // gosec: Potential hardcoded credentials

// G304: ユーザー入力によるファイルパス
func ReadFile(r *http.Request) ([]byte, error) {
path := r.URL.Query().Get("file")
return os.ReadFile(path) // gosec: Potential file inclusion via variable

}

// G404: 安全でない乱数
func GenerateToken() string {
return fmt.Sprintf("%d", rand.Int()) // gosec: Use of weak random number generator
}

修正例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// G101: 環境変数から取得
func APIKey() string {
return os.Getenv("API_KEY")
}

// G304: パスを検証してから読む
func ReadFile(r *http.Request) ([]byte, error) {
path := r.URL.Query().Get("file")
cleanPath := filepath.Clean(path)
if !strings.HasPrefix(cleanPath, "/allowed/dir/") {
return nil, errors.New("access denied")
}
return os.ReadFile(cleanPath)
}

// G404: crypto/rand を使用
func GenerateToken() (string, error) {
b := make([]byte, 32)
if _, err := crypto_rand.Read(b); err != nil {
return "", fmt.Errorf("generate token: %w", err)
}
return hex.EncodeToString(b), nil
}

注意点

  • 誤検知を抑制するには //nolint:gosec またはコード中に // #nosec G404 のようにルール ID 付きで記述する
  • テストコードでは math/rand やハードコード値が必要な場面があるため、issues.exclude-rules でテストファイルを除外するとよい
  • includesexcludes は排他的。両方指定した場合は includes が優先される

参考リンク