From 876971eea8b2348cae10a8f517e26db37237da59 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Tue, 6 Oct 2026 18:14:27 +0300 Subject: [PATCH] =?UTF-8?q?P2.3:=20timing-safe=20=D1=81=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20admin=20bearer-=D1=82=D0=BE?= =?UTF-8?q?=D0=BA=D0=B5=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bearerAuthMiddleware теперь сравнивает Authorization через subtle.ConstantTimeCompare (с предварительной проверкой длины), а не через != — устраняет timing side-channel при подборе admin-токена. --- app/admin/admin.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/admin/admin.go b/app/admin/admin.go index 5a5f499..61b9f36 100644 --- a/app/admin/admin.go +++ b/app/admin/admin.go @@ -6,6 +6,7 @@ package admin import ( "context" + "crypto/subtle" "encoding/json" "net/http" "os" @@ -119,8 +120,12 @@ func (h *Handler) RegisterPublicRoutes(r *mux.Router) { func (h *Handler) bearerAuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") + // Timing-safe сравнение: длина префикса и сам токен проверяются отдельно, + // само сравнение значения — через subtle.ConstantTimeCompare, чтобы не + // давать timing side-channel при подборе admin-токена. expected := "Bearer " + h.adminToken - if authHeader != expected { + if len(authHeader) != len(expected) || + subtle.ConstantTimeCompare([]byte(authHeader), []byte(expected)) != 1 { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"})