スキル一覧に戻る
dkoosis

go-security-patterns

by dkoosis

0🍴 0📅 2026年1月3日
GitHubで見るManusで実行

SKILL.md


name: Go Security Patterns description: This skill should be used when the user asks about "gosec", "G115", "G404", "integer overflow", "weak random", "crypto/rand", "security lint", "hardcoded credentials", or needs guidance on fixing Go security vulnerabilities. Provides patterns for common security anti-patterns.

Go Security Patterns

Patterns for fixing common Go security lint violations from gosec.

Overview

gosec (Go Security Checker) identifies security anti-patterns:

RuleCategoryRisk
G115Integer overflowData corruption, crashes
G404Weak randomPredictable tokens
G101Hardcoded secretsCredential exposure
G107Unvalidated URLSSRF attacks
G304Path traversalFile access bypass
G401Weak crypto (MD5/SHA1)Hash collisions

Pattern 1: Safe Integer Conversion (G115)

Converting between integer types can overflow on different architectures.

Problem

func ProcessCount(n int64) {
    count := int(n)  // G115: int is 32-bit on some systems
    buffer := make([]byte, count)
}

Solution 1: Bounds Check

import "math"

func ProcessCount(n int64) error {
    if n < 0 || n > math.MaxInt {
        return fmt.Errorf("count %d out of range", n)
    }
    count := int(n)
    buffer := make([]byte, count)
    return nil
}

Solution 2: Keep Original Type

func ProcessCount(n int64) {
    buffer := make([]byte, n)  // slice length accepts int64
}

Solution 3: Document Constraint

When overflow is impossible due to domain constraints:

// pageSize is always 1-100 from validated input
func ProcessPage(pageSize int64) {
    //nolint:gosec // pageSize validated to [1,100] by caller
    count := int(pageSize)
}

Architecture-Safe Patterns

// Instead of: int(x) where x is int64
// Use: explicit checks or keep type

// For array/slice indices from int64:
if idx < 0 || idx > math.MaxInt {
    return ErrIndexOutOfRange
}
arr[int(idx)]  // Now safe

// For loop counters:
for i := int64(0); i < n; i++ {
    // Keep as int64 throughout
}

Pattern 2: Cryptographic Random (G404)

math/rand is deterministic - use crypto/rand for security.

Problem

import "math/rand"

func GenerateSessionID() string {
    return fmt.Sprintf("%x", rand.Int63())  // G404: predictable
}

Solution

import (
    "crypto/rand"
    "encoding/hex"
)

func GenerateSessionID() (string, error) {
    b := make([]byte, 16)
    if _, err := rand.Read(b); err != nil {
        return "", fmt.Errorf("generate session ID: %w", err)
    }
    return hex.EncodeToString(b), nil
}

When math/rand is Acceptable

  • Shuffling for display (not security)
  • Test data generation
  • Games/simulations
  • Performance-critical non-security code

Add explicit nolint with justification:

//nolint:gosec // shuffle for UI display order, not security
rand.Shuffle(len(items), func(i, j int) {
    items[i], items[j] = items[j], items[i]
})

Crypto/Rand Patterns

// Generate random bytes
b := make([]byte, 32)
_, err := rand.Read(b)

// Generate random number in range
n, err := rand.Int(rand.Reader, big.NewInt(100))

// Generate UUID-like token
func GenerateToken() string {
    b := make([]byte, 16)
    rand.Read(b)
    return base64.RawURLEncoding.EncodeToString(b)
}

Pattern 3: Secrets Management (G101)

Never hardcode credentials, API keys, or tokens.

Problem

const (
    apiKey = "sk-abc123xyz"  // G101
    dbPass = "secret123"     // G101
)

Solution: Environment Variables

func getAPIKey() string {
    key := os.Getenv("API_KEY")
    if key == "" {
        log.Fatal("API_KEY not set")
    }
    return key
}

Solution: Config Files

type Config struct {
    APIKey string `env:"API_KEY" yaml:"api_key"`
}

func LoadConfig() (*Config, error) {
    // Load from environment or config file
}

False Positives

gosec may flag non-secrets. Use nolint with reason:

//nolint:gosec // example key for documentation
const exampleKey = "sk-example-not-real"

//nolint:gosec // password field name, not actual password
const passwordField = "password"

Pattern 4: URL Validation (G107)

Unvalidated URLs in HTTP requests enable SSRF.

Problem

func Fetch(url string) (*http.Response, error) {
    return http.Get(url)  // G107: URL from variable
}

Solution: Allowlist

var allowedHosts = map[string]bool{
    "api.example.com": true,
    "cdn.example.com": true,
}

func Fetch(rawURL string) (*http.Response, error) {
    u, err := url.Parse(rawURL)
    if err != nil {
        return nil, fmt.Errorf("invalid URL: %w", err)
    }
    if !allowedHosts[u.Host] {
        return nil, fmt.Errorf("host %s not allowed", u.Host)
    }
    return http.Get(rawURL)
}

Pattern 5: Path Sanitization (G304)

User-controlled paths enable directory traversal.

Problem

func ReadFile(name string) ([]byte, error) {
    return os.ReadFile(filepath.Join("/data", name))  // G304
}

Solution

func ReadFile(name string) ([]byte, error) {
    // Clean the path and verify it's within allowed directory
    clean := filepath.Clean(name)
    if strings.Contains(clean, "..") {
        return nil, fmt.Errorf("invalid path: %s", name)
    }

    full := filepath.Join("/data", clean)
    if !strings.HasPrefix(full, "/data/") {
        return nil, fmt.Errorf("path escapes data directory")
    }

    return os.ReadFile(full)
}

Quick Reference

IssueDetectionFix
int(x) where x is int64G115Bounds check or keep type
math/rand for tokensG404Use crypto/rand
Literal secretsG101Environment/config
http.Get(variable)G107Validate/allowlist URL
os.ReadFile(variable)G304Sanitize path

Verification

golangci-lint run --enable gosec ./...
go test ./...

スコア

総合スコア

35/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

0/5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です