8 вопросов
OWASP Top 10 - основные риски веб-приложений: инъекции, сломанная аутентификация, чувствительные данные, XXE, сломанный контроль доступа, неверная конфигурация, XSS, небезопасная десериализация, уязвимые компоненты, недостаточное логирование. В Go: защита от SQL-инъекций (prepared statements), валидация ввода, HTTPS, хранение секретов вне кода, безопасные заголовки, санитизация вывода.
Никогда не подставлять пользовательский ввод в строку запроса. Использовать плейсхолдеры: db.QueryContext(ctx, "SELECT * FROM users WHERE id = $1", id). Параметры передавать как аргументы Exec/Query; драйвер экранирует. Проверять и валидировать типы (например, id - целое). ORM и query builder с параметризацией тоже защищают при правильном использовании.
// плохо: "SELECT * FROM users WHERE id = " + id
// хорошо:
db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = $1", id)XSS - внедрение скриптов через контент страницы. В Go при выводе в HTML экранировать данные: template пакет по умолчанию экранирует {{ }}; при ручной сборке HTML использовать html.EscapeString. Не выводить непроверенный пользовательский ввод без экранирования. Content-Type и заголовки (X-Content-Type-Options, CSP) снижают риски.
import "html"
safe := html.EscapeString(userInput)
// в шаблоне: {{ . }} уже экранируетсяCSRF - запрос с другого сайта с cookie пользователя. Защита: CSRF-токен в форме и проверка на сервере; SameSite cookie (Strict/Lax) ограничивает отправку cookie при cross-site запросах. В Go генерируют токен при отдаче формы, сохраняют в сессии, при POST проверяют. Для API - не полагаться на cookie; использовать Bearer token или проверять Origin/Referer.
cookie := &http.Cookie{
Name: "session",
Value: token,
SameSite: http.SameSiteLaxMode,
Secure: true,
}
http.SetCookie(w, cookie)Пароли не хранить в открытом виде; только хеш с солью. В Go: golang.org/x/crypto/bcrypt (GenerateFromPassword, CompareHashAndPassword). Cost задает сложность (10-12 типично). scrypt - альтернатива (более устойчив к ASIC). Не использовать обычный SHA без соли; не использовать MD5. При аутентификации сравнивать хеш с введенным паролем через Compare.
hash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
err := bcrypt.CompareHashAndPassword(storedHash, []byte(password))RBAC - доступ по ролям и разрешениям (роль имеет набор прав). В Go: middleware извлекает пользователя из контекста (после auth), проверяет роль/право для ресурса; при отсутствии права возвращает 403. Хранение: таблица ролей, связь пользователь-роли, роль-разрешения; или кеш в памяти. Проверка: hasRole(user, "admin") или hasPermission(user, "order:write").
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) {
if getUser(r).Role != role { http.Error(w, "Forbidden", 403); return }
next.ServeHTTP(w, r)
})
}
}Аутентификация: JWT в заголовке Authorization, или API key, или OAuth2. Проверка в middleware; контекст с user ID/ролями. Авторизация: проверка прав на ресурс (владелец или роль). HTTPS обязательно; rate limiting от злоупотреблений. Не логировать токены и пароли. В Go: извлечь токен, проверить подпись и exp, положить claims в context. Для каждого эндпоинта проверять право доступа.
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := extractBearer(r)
claims, err := validateToken(token)
if err != nil { http.Error(w, "Unauthorized", 401); return }
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "user", claims)))
})
}Секреты не в коде и не в репозитории. В проде: переменные окружения (env), файлы (смонтированные в K8s из Secret), внешние хранилища (HashiCorp Vault, AWS Secrets Manager). В Go читать из os.Getenv или из файла при старте; не логировать. В разработке - .env (в gitignore) или default-значения. Ротация секретов без перезапуска - через периодическое чтение или callback при изменении.
secret := os.Getenv("DB_PASSWORD")
if secret == "" { log.Fatal("DB_PASSWORD required") }