OWASP Top 10 в Go: secure coding practices
Зачем знать: Security — это не “feature”, это base requirement. Middle 2 разработчик должен знать OWASP Top 10 наизусть и понимать, как каждая категория применяется в Go-коде. Один баг с SQL injection может стоить компании миллионы (GDPR fines, утечка данных, brand damage). При этом большинство уязвимостей легко избежать, если знаешь стандартные паттерны: parameterized queries, html/template, validation, crypto/rand. В 2026 году security review — обязательная стадия любого production code review.
Содержание
Заголовок раздела «Содержание»- Концепция: OWASP Top 10 2021 в Go
- Production-практики: validation, crypto, secrets
- Gotchas: subtle bugs, race conditions, timing attacks
- Real cases: Equifax, log4shell, supply chain
- Вопросы для собеседования
- Practice
- Источники
1. Концепция: OWASP Top 10 2021 в Go
Заголовок раздела «1. Концепция: OWASP Top 10 2021 в Go»1.1 OWASP Top 10 2021 — overview
Заголовок раздела «1.1 OWASP Top 10 2021 — overview»Текущая версия (2021) от Open Web Application Security Project:
- A01: Broken Access Control — нарушение прав доступа.
- A02: Cryptographic Failures — слабая криптография.
- A03: Injection — SQL, command, LDAP injection.
- A04: Insecure Design — отсутствие threat modeling.
- A05: Security Misconfiguration — небезопасные defaults.
- A06: Vulnerable and Outdated Components — устаревшие зависимости.
- A07: Identification and Authentication Failures — слабая auth.
- A08: Software and Data Integrity Failures — supply chain.
- A09: Security Logging and Monitoring Failures — отсутствие audit.
- A10: Server-Side Request Forgery (SSRF) — SSRF атаки.
Изменения с 2017: добавили A04 (Insecure Design), A10 (SSRF), убрали XSS из топа (переместили в A03 Injection).
1.2 A01: Broken Access Control в Go
Заголовок раздела «1.2 A01: Broken Access Control в Go»Категория: пользователь делает что-то, на что не имеет прав.
Примеры в Go:
// УЯЗВИМО: IDOR (Insecure Direct Object Reference)func GetOrder(w http.ResponseWriter, r *http.Request) { orderID := r.URL.Query().Get("id") order, _ := db.GetOrder(orderID) // не проверяем owner json.NewEncoder(w).Encode(order)}
// ПРАВИЛЬНОfunc GetOrder(w http.ResponseWriter, r *http.Request) { userID := getUserID(r.Context()) orderID := r.URL.Query().Get("id") order, err := db.GetOrder(orderID) if err != nil { ... } if order.UserID != userID { http.Error(w, "forbidden", http.StatusForbidden) return } json.NewEncoder(w).Encode(order)}Defense:
- ВСЕГДА проверять authorization (не только authentication).
- Deny by default — explicit allow.
- RBAC / ABAC / OPA / Casbin (см. файл 38).
- Audit logs для access decisions.
1.3 A02: Cryptographic Failures
Заголовок раздела «1.3 A02: Cryptographic Failures»Категория: неправильное использование crypto.
Anti-patterns:
// УЯЗВИМО: MD5 для passwordshash := md5.Sum([]byte(password))
// УЯЗВИМО: math/rand для securityn := rand.Intn(1000000) // predictable
// УЯЗВИМО: own cryptofunc encrypt(data, key []byte) []byte { // never roll your own crypto}Правила:
| Need | Use | Don’t use |
|---|---|---|
| Password hashing | argon2 / bcrypt | MD5, SHA1, SHA256 |
| Random for security | crypto/rand | math/rand |
| Encryption | crypto/aes-gcm, chacha20poly1305 | DIY |
| TLS | crypto/tls | Plain TCP |
| Hash | sha256, sha512, blake2 | MD5, SHA1 |
| HMAC | crypto/hmac | DIY |
Пример password hashing:
import "golang.org/x/crypto/bcrypt"
// Hashhash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
// Verifyerr := bcrypt.CompareHashAndPassword(hash, []byte(input))1.4 A03: Injection в Go
Заголовок раздела «1.4 A03: Injection в Go»SQL Injection:
// УЯЗВИМОdb.Query("SELECT * FROM users WHERE email = '" + email + "'")
// ПРАВИЛЬНО — параметризованныйdb.Query("SELECT * FROM users WHERE email = $1", email)database/sql всегда использует параметры безопасно — драйвер делает escape на низком уровне.
Command Injection:
// УЯЗВИМОexec.Command("sh", "-c", "ping " + userInput).Run()
// ПРАВИЛЬНОexec.Command("ping", "-c", "1", userInput).Run() // args separately// + validate userInput через regex (only IPs/domains)Никогда не передавай user input в sh -c, и валидируй args.
Path Traversal:
// УЯЗВИМОfile := r.URL.Query().Get("file") // "../../etc/passwd"os.Open("/uploads/" + file)
// ПРАВИЛЬНОfile := r.URL.Query().Get("file")file = filepath.Clean(file)fullPath := filepath.Join("/uploads", file)// Verify результат внутри base directoryif !strings.HasPrefix(fullPath, "/uploads/") { http.Error(w, "invalid path", http.StatusBadRequest) return}os.Open(fullPath)В Go 1.20+ есть os.Root API (Go 1.24):
root, _ := os.OpenRoot("/uploads")defer root.Close()f, _ := root.Open(userFile) // automatically prevents traversalTemplate Injection / XSS:
// УЯЗВИМОtmpl := template.New("page")tmpl.Parse("<h1>" + userInput + "</h1>") // text/template
// ПРАВИЛЬНОimport "html/template"tmpl := template.New("page")tmpl.Parse("<h1>{{.}}</h1>") // auto-escapetmpl.Execute(w, userInput)LDAP Injection — фильтр sanitize:
import "github.com/go-ldap/ldap/v3"filter := fmt.Sprintf("(uid=%s)", ldap.EscapeFilter(username))1.5 A04: Insecure Design
Заголовок раздела «1.5 A04: Insecure Design»Категория: архитектурные проблемы (не bugs).
Examples:
- Нет rate limiting → DoS возможен.
- Нет audit logs → нельзя расследовать.
- Нет separation of concerns → один баг компрометирует всё.
Defense:
- Threat modeling до coding (STRIDE, DREAD).
- Security requirements в spec.
- Defense in depth (multiple layers).
- Principle of least privilege.
1.6 A05: Security Misconfiguration
Заголовок раздела «1.6 A05: Security Misconfiguration»Examples:
# УЯЗВИМОspec: containers: - image: app:latest ports: [80, 443, 22, 3306] # экспозиция лишних портов securityContext: privileged: true # root в containerDefense:
- Distroless / minimal base images.
runAsNonRoot: true.readOnlyRootFilesystem: true.- Network policies — deny by default.
- Disable unused features (admin panels, debug endpoints in prod).
1.7 A06: Vulnerable Components
Заголовок раздела «1.7 A06: Vulnerable Components»В Go контексте:
# Check vulnerabilitiesgo install golang.org/x/vuln/cmd/govulncheck@latestgovulncheck ./...Defense:
govulncheckв CI (см. файл 36).- Dependabot/Renovate для updates.
- Минимальные dependencies (less attack surface).
- Прикладывай
go.sum(cryptographic verification).
1.8 A07: Identification and Authentication Failures
Заголовок раздела «1.8 A07: Identification and Authentication Failures»См. файл 38 (auth & RBAC).
Common issues:
- Weak passwords (no policy).
- Brute force allowed (no rate limit).
- Session hijacking (no HttpOnly, Secure cookies).
- Predictable session IDs (math/rand).
1.9 A08: Software and Data Integrity Failures
Заголовок раздела «1.9 A08: Software and Data Integrity Failures»Категория: supply chain.
Examples:
- Unsigned packages.
- Auto-update без verify signature.
- Insecure deserialization (gob, JSON с unknown types).
Defense:
- cosign для signing (файл 36).
- SBOM tracking.
- Verify checksums в
go.sum.
1.10 A09: Security Logging and Monitoring Failures
Заголовок раздела «1.10 A09: Security Logging and Monitoring Failures»// УЯЗВИМО: нет audit logfunc DeleteUser(userID string) { db.Delete(userID)}
// ПРАВИЛЬНОfunc DeleteUser(ctx context.Context, userID string) { actor := getActor(ctx) slog.InfoContext(ctx, "user delete", "actor", actor.ID, "target", userID, "ip", getIP(ctx), ) db.Delete(userID)}What to log:
- Authentication attempts (success + fail).
- Authorization decisions.
- Critical data modifications.
- API calls с PII.
Don’t log:
- Passwords (plain or hashed).
- Tokens, API keys.
- Credit card numbers.
- Personal data без masking.
1.11 A10: SSRF (Server-Side Request Forgery)
Заголовок раздела «1.11 A10: SSRF (Server-Side Request Forgery)»Атака: сервер делает HTTP request на user-controlled URL → может dosrar internal services.
// УЯЗВИМОfunc Proxy(w http.ResponseWriter, r *http.Request) { url := r.URL.Query().Get("url") resp, _ := http.Get(url) // attacker passes http://169.254.169.254/latest/meta-data io.Copy(w, resp.Body)}В AWS — это metadata endpoint, может выдать IAM credentials.
Defense:
import ( "net" "net/http" "net/url")
var allowedHosts = map[string]bool{"api.example.com": true}
func safeFetch(rawURL string) (*http.Response, error) { u, err := url.Parse(rawURL) if err != nil { return nil, err }
// 1. Only allowed schemes if u.Scheme != "https" { return nil, fmt.Errorf("only https allowed") }
// 2. Allowed hosts if !allowedHosts[u.Hostname()] { return nil, fmt.Errorf("host not allowed") }
// 3. Resolve and check IP не internal ips, err := net.LookupIP(u.Hostname()) if err != nil { return nil, err } for _, ip := range ips { if isPrivate(ip) || isLoopback(ip) || isLinkLocal(ip) { return nil, fmt.Errorf("blocked IP") } }
return http.Get(rawURL)}
func isPrivate(ip net.IP) bool { private := []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16"} for _, cidr := range private { _, block, _ := net.ParseCIDR(cidr) if block.Contains(ip) { return true } } return false}Дополнительные защиты:
- Use HTTP proxy с allowlist.
- AWS IMDSv2 (требует token).
- Network policies — block egress на internal.
2. Production-практики
Заголовок раздела «2. Production-практики»2.1 Input validation: go-playground/validator
Заголовок раздела «2.1 Input validation: go-playground/validator»import "github.com/go-playground/validator/v10"
type CreateUserReq struct { Email string `json:"email" validate:"required,email"` Password string `json:"password" validate:"required,min=12,containsany=!@#$%^&*"` Age int `json:"age" validate:"gte=18,lte=120"` Username string `json:"username" validate:"required,alphanum,min=3,max=30"`}
func handler(w http.ResponseWriter, r *http.Request) { var req CreateUserReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, err.Error(), 400) return }
v := validator.New() if err := v.Struct(req); err != nil { http.Error(w, err.Error(), 400) return }
// ... process}2.2 Crypto Random
Заголовок раздела «2.2 Crypto Random»import "crypto/rand"
// Generate tokenfunc generateToken() (string, error) { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { return "", err } return base64.URLEncoding.EncodeToString(b), nil}
// Generate int in rangefunc randomInt(max int64) (int64, error) { n, err := rand.Int(rand.Reader, big.NewInt(max)) if err != nil { return 0, err } return n.Int64(), nil}2.3 CSRF Protection
Заголовок раздела «2.3 CSRF Protection»import "github.com/gorilla/csrf"
csrfMiddleware := csrf.Protect( []byte("32-byte-long-auth-key-rotated"), csrf.Secure(true), // only HTTPS csrf.SameSite(csrf.SameSiteStrictMode), csrf.HttpOnly(true),)
http.ListenAndServe(":8080", csrfMiddleware(handler))Modern alternative — SameSite cookies:
http.SetCookie(w, &http.Cookie{ Name: "session", Value: sessionID, HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode,})2.4 Rate limiting
Заголовок раздела «2.4 Rate limiting»import "golang.org/x/time/rate"
type Limiter struct { mu sync.Mutex limiters map[string]*rate.Limiter}
func (l *Limiter) Get(key string) *rate.Limiter { l.mu.Lock() defer l.mu.Unlock() if r, ok := l.limiters[key]; ok { return r } r := rate.NewLimiter(rate.Every(time.Second), 10) // 10 req/s l.limiters[key] = r return r}
func middleware(l *Limiter) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := getIP(r) if !l.Get(ip).Allow() { http.Error(w, "rate limited", http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) }}Distributed: Redis-based (go-redis-rate или DIY).
2.5 HTTP Security Headers
Заголовок раздела «2.5 HTTP Security Headers»func securityHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { h := w.Header() h.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains") h.Set("X-Content-Type-Options", "nosniff") h.Set("X-Frame-Options", "DENY") h.Set("Content-Security-Policy", "default-src 'self'") h.Set("Referrer-Policy", "strict-origin-when-cross-origin") h.Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()") next.ServeHTTP(w, r) })}2.6 TLS configuration
Заголовок раздела «2.6 TLS configuration»tlsConfig := &tls.Config{ MinVersion: tls.VersionTLS13, CipherSuites: []uint16{ tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_CHACHA20_POLY1305_SHA256, }, CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},}
srv := &http.Server{ Addr: ":443", TLSConfig: tlsConfig, // ...}В 2026 — TLS 1.3 как baseline. TLS 1.2 — minimum для legacy.
2.7 Secrets management
Заголовок раздела «2.7 Secrets management»Layered approach:
1. Local development: - .env files (НЕ commit) - direnv для project-scoped
2. CI/CD: - GitHub Secrets / GitLab Variables (encrypted) - OIDC для cloud (no static keys)
3. Production: - Vault / AWS Secrets Manager / GCP Secret Manager - SOPS для encrypted YAML в Git - External Secrets Operator (k8s) - CSI Secrets StoreVault example:
import "github.com/hashicorp/vault/api"
config := api.DefaultConfig()client, _ := api.NewClient(config)client.SetToken(os.Getenv("VAULT_TOKEN"))
secret, _ := client.KVv2("secret").Get(ctx, "myapp/db")password := secret.Data["password"].(string)2.8 Encryption at rest
Заголовок раздела «2.8 Encryption at rest»File: AES-GCM via crypto/cipher:
func encrypt(plaintext, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } nonce := make([]byte, gcm.NonceSize()) if _, err := rand.Read(nonce); err != nil { return nil, err } return gcm.Seal(nonce, nonce, plaintext, nil), nil}
func decrypt(ciphertext, key []byte) ([]byte, error) { block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) if len(ciphertext) < gcm.NonceSize() { return nil, errors.New("ciphertext too short") } nonce, ct := ciphertext[:gcm.NonceSize()], ciphertext[gcm.NonceSize():] return gcm.Open(nil, nonce, ct, nil)}2.9 Constant-time comparison
Заголовок раздела «2.9 Constant-time comparison»// УЯЗВИМО: timing attackif storedToken == providedToken { // ...}
// ПРАВИЛЬНОimport "crypto/subtle"if subtle.ConstantTimeCompare( []byte(storedToken), []byte(providedToken),) == 1 { // ...}2.10 Logging без sensitive data
Заголовок раздела «2.10 Logging без sensitive data»type User struct { ID string Email string Password string // sensitive APIKey string // sensitive}
func (u User) LogValue() slog.Value { return slog.GroupValue( slog.String("id", u.ID), slog.String("email", redactEmail(u.Email)), // НЕ Password, НЕ APIKey )}
slog.Info("user logged in", "user", user)// Output: user={id=123 email=a***@example.com}2.11 Static analysis в CI
Заголовок раздела «2.11 Static analysis в CI»- name: gosec uses: securego/gosec@master with: args: ./...
- name: govulncheck run: govulncheck ./...
- name: gitleaks uses: gitleaks/gitleaks-action@v2
- name: trivy uses: aquasecurity/trivy-action@master with: scan-type: fs severity: HIGH,CRITICAL2.12 Pen testing schedule
Заголовок раздела «2.12 Pen testing schedule»- Static analysis — каждый PR (CI).
- Dependency scan — каждый PR + weekly.
- DAST (dynamic) — eachly per environment.
- Pen test — annual + после major release.
- Bug bounty — continuous.
3. Gotchas
Заголовок раздела «3. Gotchas»3.1 ⚠️ json.Unmarshal с big payload
Заголовок раздела «3.1 ⚠️ json.Unmarshal с big payload»DoS attack — send huge JSON:
// УЯЗВИМОjson.NewDecoder(r.Body).Decode(&data)
// ПРАВИЛЬНОr.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1MBjson.NewDecoder(r.Body).Decode(&data)3.2 ⚠️ Reflect и unknown types
Заголовок раздела «3.2 ⚠️ Reflect и unknown types»encoding/gob с unknown types может triggered allocation bomb. Avoid gob с untrusted data.
3.3 ⚠️ ZIP bomb
Заголовок раздела «3.3 ⚠️ ZIP bomb»При decoding ZIP from user — check decompressed size.
3.4 ⚠️ Regex DoS (ReDoS)
Заголовок раздела «3.4 ⚠️ Regex DoS (ReDoS)»// УЯЗВИМОre := regexp.MustCompile(`^(a+)+$`)re.MatchString("aaaaaaaaaaaaaaaaaaaaaa!") // exponential timeGo’s regexp package использует RE2 (linear time), но не все regex libraries.
3.5 ⚠️ HTTP/2 Rapid Reset (CVE-2023-44487)
Заголовок раздела «3.5 ⚠️ HTTP/2 Rapid Reset (CVE-2023-44487)»Go 1.21 имел уязвимость. Update Go!
3.6 ⚠️ Path traversal через symlinks
Заголовок раздела «3.6 ⚠️ Path traversal через symlinks»filepath.Clean не resolve symlinks. Используй filepath.EvalSymlinks или os.Root API (Go 1.24).
3.7 ⚠️ Race в auth check
Заголовок раздела «3.7 ⚠️ Race в auth check»// УЯЗВИМО: TOCTOUif isAdmin(user) { // user может потерять admin между check и action doAdminAction(user)}
// ПРАВИЛЬНО: atomic check + actionreturn db.Transaction(func(tx) { if !isAdmin(tx, user) { return err } return doAdminAction(tx, user)})3.8 ⚠️ Open redirect
Заголовок раздела «3.8 ⚠️ Open redirect»// УЯЗВИМОhttp.Redirect(w, r, r.URL.Query().Get("next"), 302)
// ПРАВИЛЬНОnext := r.URL.Query().Get("next")if !strings.HasPrefix(next, "/") || strings.HasPrefix(next, "//") { next = "/"}http.Redirect(w, r, next, 302)3.9 ⚠️ XML/SSRF через external entities (XXE)
Заголовок раздела «3.9 ⚠️ XML/SSRF через external entities (XXE)»Go’s encoding/xml не resolve external entities by default — safe.
3.10 ⚠️ Memory disclosure через uninitialized buffer
Заголовок раздела «3.10 ⚠️ Memory disclosure через uninitialized buffer»// УЯЗВИМОbuf := make([]byte, 1024)n, _ := conn.Read(buf)sendToClient(buf) // отправляем 1024 байта, включая uninitialized
// ПРАВИЛЬНОsendToClient(buf[:n])3.11 ⚠️ Slice растёт при append → copy
Заголовок раздела «3.11 ⚠️ Slice растёт при append → copy»Если slice content sensitive (password buffer), capacity growth копирует данные → old memory не cleared. Use sync.Pool или runtime.GC после use.
3.12 ⚠️ time.Now() для tokens
Заголовок раздела «3.12 ⚠️ time.Now() для tokens»Tokens на основе timestamp predictable. Use crypto/rand.
3.13 ⚠️ Goroutine leak DoS
Заголовок раздела «3.13 ⚠️ Goroutine leak DoS»// УЯЗВИМОhttp.HandleFunc("/start", func(w, r) { go longTask() // никогда не cancelled})Attacker spam’ит /start → out of memory.
3.14 ⚠️ HTTP смешивание схем
Заголовок раздела «3.14 ⚠️ HTTP смешивание схем»Если cookies Secure: false — отправлены через HTTP. MITM перехватит. Always Secure: true в prod.
4. Real cases
Заголовок раздела «4. Real cases»4.1 Equifax (2017)
Заголовок раздела «4.1 Equifax (2017)»- Apache Struts vulnerability (CVE-2017-5638).
- Не patched 2 месяца.
- 147 million records leaked.
- $1.4 billion settlement.
Lesson: dependency updates критичны.
4.2 Log4Shell (2021)
Заголовок раздела «4.2 Log4Shell (2021)»- log4j vulnerability (Java, не Go, но lesson generic).
- JNDI lookup в log statements → RCE.
- Тысячи компаний affected.
Lesson: даже logging library может быть attack vector.
4.3 SolarWinds (2020)
Заголовок раздела «4.3 SolarWinds (2020)»- Supply chain attack.
- Backdoor inserted в build process.
- 18,000 customers affected.
Lesson: secure CI/CD critical.
4.4 GitLab CI runners (2023)
Заголовок раздела «4.4 GitLab CI runners (2023)»- Поломаны через path traversal в job artifacts.
- Compromised CI runners.
Lesson: validate всё, даже из internal source.
4.5 Postgres CVE-2024-XXXXX
Заголовок раздела «4.5 Postgres CVE-2024-XXXXX»Periodically database vulnerabilities. Govulncheck ловит зависимости.
4.6 SSRF в crypto exchange
Заголовок раздела «4.6 SSRF в crypto exchange»В 2022 некий exchange имел SSRF в проксях. Attacker fetch’ил AWS metadata → IAM creds → drained accounts.
4.7 Open redirect в OAuth
Заголовок раздела «4.7 Open redirect в OAuth»Через OAuth redirect_uri открытые перенаправления — phishing. Whitelist redirect_uris.
4.8 РФ-кейс: утечка через text/template
Заголовок раздела «4.8 РФ-кейс: утечка через text/template»Кейс из 2023: компания использовала text/template для HTML emails. User-controlled поле без escape → XSS в email клиентах.
5. Вопросы для собеседования
Заголовок раздела «5. Вопросы для собеседования»Q1: Что такое OWASP Top 10? A: Список 10 наиболее критичных security рисков для web applications от Open Web Application Security Project. Версия 2021: Broken Access Control, Crypto Failures, Injection, Insecure Design, Misconfiguration, Vulnerable Components, Auth Failures, Integrity Failures, Logging Failures, SSRF.
Q2: Как защититься от SQL injection в Go?
A: Использовать параметризованные запросы через database/sql: db.Query("SELECT ... WHERE id = $1", id). Никогда не concat’енировать user input в SQL. ORM (gorm, sqlx) делают это автоматически.
Q3: Чем html/template отличается от text/template?
A: html/template auto-escape user data в HTML context (предотвращает XSS). text/template для plain text, не escape. Для генерации HTML всегда html/template.
Q4: math/rand vs crypto/rand? A: math/rand — pseudo-random, predictable, для simulations. crypto/rand — cryptographically secure, для tokens, keys, secrets. ВСЕГДА crypto/rand для security.
Q5: Какой алгоритм для password hashing? A: bcrypt (классика), argon2 (победитель Password Hashing Competition 2015), scrypt. НИКОГДА MD5, SHA1, SHA256 (слишком быстрые — brute force легче).
Q6: Что такое SSRF и как защититься? A: Server-Side Request Forgery — атакующий заставляет сервер делать HTTP запрос на внутренний ресурс (AWS metadata, internal admin panel). Защита: allowlist хостов, блокировка приватных IPs после DNS resolve, отдельный egress proxy.
Q7: Path traversal в Go — как защититься? A: filepath.Clean + verify, что результат внутри base directory (strings.HasPrefix). В Go 1.24 — os.Root API, который автоматически предотвращает escape.
Q8: Что такое CSRF и как защититься? A: Cross-Site Request Forgery — атака, где зловредный сайт делает request от имени пользователя к target сайту. Защита: CSRF tokens (gorilla/csrf), SameSite=Strict cookies, проверка Origin/Referer header.
Q9: Что такое timing attack?
A: Атакующий измеряет время response для extract информации. Например, == сравнение byte-by-byte → можно угадать password char-by-char. Защита: crypto/subtle.ConstantTimeCompare.
Q10: Что такое govulncheck? A: Official Go tool, scan’ит зависимости vs Go vulnerability database. Уникальность: показывает только vulnerabilities, которые реально вызываются в коде (statically analyzed). Reduces false positives.
Q11: Какие HTTP security headers важны? A: Strict-Transport-Security (HSTS), Content-Security-Policy (CSP), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy. Defense in depth.
Q12: Что такое HSTS?
A: HTTP Strict Transport Security — header, заставляющий browser использовать только HTTPS для domain. Преволочает SSL stripping атаки. max-age=31536000; includeSubDomains; preload.
Q13: Как rate limit’ить в Go?
A: golang.org/x/time/rate для in-process. Distributed — Redis-based (например go-redis-rate). Granularity: per IP, per user, per API key, per endpoint. Возвращай 429 Too Many Requests с Retry-After.
Q14: Что такое SOPS? A: Mozilla SOPS — encrypted YAML/JSON в Git. Шифрует только values (keys видны). Поддерживает AWS KMS, GCP KMS, Vault, age. Стандарт для storing secrets в Git репо.
Q15: Где хранить secrets в production? A: Vault / AWS Secrets Manager / GCP Secret Manager. В k8s — External Secrets Operator или CSI Secrets Store sync’ит из Vault в Secret. SOPS для GitOps. Никогда в коде или env vars в plain.
Q16: Что такое TLS 1.3 и зачем? A: Latest TLS version (RFC 8446). Меньше handshake (1-RTT), better security (removed weak ciphers), forward secrecy by default. В 2026 — baseline для new services. TLS 1.2 — minimum для legacy compatibility.
Q17: Что такое JWT и его уязвимости? A: JSON Web Token. Уязвимости: alg=none (sign verification skip), weak HS256 key (brute force), token in URL (logs leak), no expiration. См. файл 38.
Q18: Что такое insecure deserialization? A: Десериализация untrusted data → RCE / DoS. В Go: gob с unknown types может trigger alloc bomb. JSON относительно safe но check sizes. Validate input после Unmarshal.
Q19: Как защититься от open redirect? A: Validate redirect URL: starts with /, not с //, не absolute URL. Whitelist allowed redirect domains. Не verbatim pass user input в Redirect.
Q20: Что НЕ должно быть в логах? A: Passwords (даже хэшированные), tokens, API keys, credit card numbers, PII без masking. Используй custom LogValue() для sanitize объектов. Redact в Collector.
6. Practice
Заголовок раздела «6. Practice»-
OWASP audit: возьми существующий Go проект, найди по 1 проблеме на каждую категорию Top 10.
-
SSRF defense: реализуй
safeFetch()с allowlist + блокировкой private IPs. -
Path traversal: создай file server. Сделай vulnerable version. Эксплуатируй (через
../). Исправь через os.Root. -
Password hashing: сравни performance bcrypt vs argon2id. Выбери cost factor для 250ms на hash.
-
Rate limit middleware: реализуй per-IP rate limit с golang.org/x/time/rate. Test через k6.
-
Validator setup: добавь go-playground/validator во все API requests. Покрытие 100% входов.
-
TLS config audit: настрой TLS 1.3-only с правильным cipher suite. Test через
testssl.sh. -
Govulncheck integration: добавь в CI, fix all findings.
7. Источники
Заголовок раздела «7. Источники»- OWASP Top 10 2021: https://owasp.org/Top10/.
- OWASP Go-SCP: https://github.com/OWASP/Go-SCP — secure coding practices Go.
- Go Security Best Practices: https://go.dev/security/best-practices.
- govulncheck: https://go.dev/security/vuln/.
- gosec: https://github.com/securego/gosec.
- Vault: https://www.vaultproject.io/docs.
- “Web Application Hacker’s Handbook” — Stuttard, Pinto.
- OWASP Cheat Sheet Series: https://cheatsheetseries.owasp.org/.
- “Real-World Cryptography” — David Wong, Manning.
- NIST Crypto Standards: https://csrc.nist.gov/.