57 lines
3.0 KiB
Go
57 lines
3.0 KiB
Go
package api
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
)
|
|
|
|
// logRequests логирует метод и путь каждого входящего запроса.
|
|
// Middleware-обёртка: оборачивает любой http.Handler.
|
|
func logRequests(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
log.Printf("%s %s", r.Method, r.URL.Path)
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// withCORS добавляет CORS заголовки для разрешения cross-origin запросов.
|
|
//
|
|
// Почему Access-Control-Allow-Origin: *:
|
|
// Console — одностраничное приложение, может обслуживаться с любого домена.
|
|
// Строгий CORS с конкретным Origin потребовал бы конфигурации при деплое.
|
|
// Все чувствительные операции защищены X-Auth-Token — не cookie, не ambient credentials.
|
|
func withCORS(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Auth-Token, X-Auth-Env")
|
|
// Preflight OPTIONS — отвечаем сразу без передачи в handler
|
|
if r.Method == http.MethodOptions {
|
|
w.WriteHeader(http.StatusNoContent)
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// withSecurityHeaders добавляет стандартные HTTP security заголовки.
|
|
// Снижает риск XSS, clickjacking и утечки Referer.
|
|
func withSecurityHeaders(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// nosniff: браузер не должен угадывать MIME тип — только то что в Content-Type
|
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
// DENY: запрещаем отображение страницы в iframe (clickjacking protection)
|
|
w.Header().Set("X-Frame-Options", "DENY")
|
|
// strict-origin-when-cross-origin: скрываем полный URL при cross-origin запросах
|
|
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
|
// Отключаем браузерные API которые не нужны console
|
|
w.Header().Set("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
|
|
// CSP: разрешаем только ресурсы с того же origin + inline scripts/styles (нужны для embedded UI)
|
|
w.Header().Set("Content-Security-Policy",
|
|
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; "+
|
|
"img-src 'self' data:; connect-src 'self'; font-src 'self' data:; object-src 'none'; "+
|
|
"frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests; block-all-mixed-content")
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|