Skip to content

Commit fe42ba1

Browse files
Track CSRF error in metrics
ref DEV-2911
2 parents d67c187 + fc1d736 commit fe42ba1

3 files changed

Lines changed: 73 additions & 8 deletions

File tree

.vettedpositions

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@
172172
/pkg/auth/handler/webapp/create_passkey.go:53:27: requestcontext
173173
/pkg/auth/handler/webapp/create_password.go:107:27: requestcontext
174174
/pkg/auth/handler/webapp/csrf_error_instruction.go:49:36: requestcontext
175-
/pkg/auth/handler/webapp/csrf_middleware.go:73:5: requestcontext
176-
/pkg/auth/handler/webapp/csrf_middleware.go:84:9: requestcontext
175+
/pkg/auth/handler/webapp/csrf_middleware.go:74:5: requestcontext
176+
/pkg/auth/handler/webapp/csrf_middleware.go:85:9: requestcontext
177177
/pkg/auth/handler/webapp/enter_login_id.go:144:27: requestcontext
178178
/pkg/auth/handler/webapp/enter_login_id.go:146:31: requestcontext
179179
/pkg/auth/handler/webapp/enter_oob_otp.go:161:27: requestcontext

pkg/auth/handler/webapp/csrf_middleware.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package webapp
22

33
import (
44
"encoding/base64"
5+
"errors"
56
"fmt"
67
"log/slog"
78
"net/http"
@@ -82,11 +83,6 @@ func (m *CSRFMiddleware) Handle(next http.Handler) http.Handler {
8283

8384
func (m *CSRFMiddleware) unauthorizedHandler(w http.ResponseWriter, r *http.Request) {
8485
ctx := r.Context()
85-
otelutil.IntCounterAddOne(
86-
ctx,
87-
otelauthgear.CounterCSRFRequestCount,
88-
otelauthgear.WithStatusError(),
89-
)
9086

9187
// Check debug cookies and inject info for reporting
9288
omitCookie, err := m.Cookies.GetCookie(r, webapp.CSRFDebugCookieSameSiteOmitDef)
@@ -107,6 +103,35 @@ func (m *CSRFMiddleware) unauthorizedHandler(w http.ResponseWriter, r *http.Requ
107103
maskedCsrfCookieContent := ""
108104
securecookieError := ""
109105
csrfFailureReason := csrf.FailureReason(r)
106+
107+
otelutil.IntCounterAddOne(
108+
ctx,
109+
otelauthgear.CounterCSRFRequestCount,
110+
otelauthgear.WithStatusError(),
111+
otelauthgear.WithCSRFHasOmitCookie(hasOmitCookie),
112+
otelauthgear.WithCSRFHasNoneCookie(hasNoneCookie),
113+
otelauthgear.WithCSRFHasLaxCookie(hasLaxCookie),
114+
otelauthgear.WithCSRFHasStrictCookie(hasStrictCookie),
115+
otelauthgear.WithGorillaCSRFFailureReason(func() string {
116+
var val string
117+
switch {
118+
case errors.Is(csrfFailureReason, csrf.ErrNoReferer):
119+
val = "ErrNoReferer"
120+
case errors.Is(csrfFailureReason, csrf.ErrBadOrigin):
121+
val = "ErrBadOrigin"
122+
case errors.Is(csrfFailureReason, csrf.ErrBadReferer):
123+
val = "ErrBadReferer"
124+
case errors.Is(csrfFailureReason, csrf.ErrNoToken):
125+
val = "ErrNoToken"
126+
case errors.Is(csrfFailureReason, csrf.ErrBadToken):
127+
val = "ErrBadToken"
128+
default:
129+
val = "unknown"
130+
}
131+
return val
132+
}()),
133+
)
134+
110135
if csrfCookie != nil {
111136
// do not return value but length only for debug.
112137
csrfCookieSizeInBytes = len([]byte(csrfCookie.Value))
@@ -147,6 +172,7 @@ func (m *CSRFMiddleware) unauthorizedHandler(w http.ResponseWriter, r *http.Requ
147172
}
148173

149174
logger := CSRFMiddlewareLogger.GetLogger(ctx)
175+
150176
logger.With(
151177
slog.Bool("hasOmitCookie", hasOmitCookie),
152178
slog.Bool("hasNoneCookie", hasNoneCookie),
@@ -156,7 +182,7 @@ func (m *CSRFMiddleware) unauthorizedHandler(w http.ResponseWriter, r *http.Requ
156182
slog.String("maskedCsrfCookieContent", maskedCsrfCookieContent),
157183
slog.String("securecookieError", securecookieError),
158184
slog.Any("csrfFailureReason", csrfFailureReason),
159-
).Error(ctx, "CSRF Forbidden")
185+
).WithSkipLogging().Error(ctx, "CSRF Forbidden")
160186

161187
uiImpl := m.UIImplementationService.GetUIImplementation()
162188

pkg/lib/otelauthgear/metric.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,21 @@ var AttributeStatusOK = AttributeKeyStatus.String("ok")
9494
// AttributeStatusError is "status=error".
9595
var AttributeStatusError = AttributeKeyStatus.String("error")
9696

97+
// AttributeKeyCSRFHasOmitCookie defines the attribute.
98+
var AttributeKeyCSRFHasOmitCookie = attribute.Key("csrf.has_omit_cookie")
99+
100+
// AttributeKeyCSRFHasNoneCookie defines the attribute.
101+
var AttributeKeyCSRFHasNoneCookie = attribute.Key("csrf.has_none_cookie")
102+
103+
// AttributeKeyCSRFHasLaxCookie defines the attribute.
104+
var AttributeKeyCSRFHasLaxCookie = attribute.Key("csrf.has_lax_cookie")
105+
106+
// AttributeKeyCSRFHasStrictCookie defines the attribute.
107+
var AttributeKeyCSRFHasStrictCookie = attribute.Key("csrf.has_strict_cookie")
108+
109+
// AttributeKeyGorillaCSRFFailureReason defines the attribute.
110+
var AttributeKeyGorillaCSRFFailureReason = attribute.Key("gorilla_csrf.failure_reason")
111+
97112
var CounterOAuthSessionCreationCount = otelutil.MustInt64Counter(
98113
meter,
99114
"authgear.oauth_session.creation.count",
@@ -171,6 +186,10 @@ var CounterWhatsappRequestCount = otelutil.MustInt64Counter(
171186

172187
// CounterCSRFRequestCount has the following labels:
173188
// - AttributeKeyStatus
189+
// - AttributeKeyCSRFHasOmitCookie
190+
// - AttributeKeyCSRFHasNoneCookie
191+
// - AttributeKeyCSRFHasLaxCookie
192+
// - AttributeKeyCSRFHasStrictCookie
174193
var CounterCSRFRequestCount = otelutil.MustInt64Counter(
175194
meter,
176195
"authgear.csrf.request.count",
@@ -252,6 +271,26 @@ func WithHTTPStatusCode(code int) otelutil.MetricOption {
252271
return metricOptionAttributeKeyValue{semconv.HTTPResponseStatusCodeKey.Int(code)}
253272
}
254273

274+
func WithCSRFHasOmitCookie(b bool) otelutil.MetricOption {
275+
return metricOptionAttributeKeyValue{AttributeKeyCSRFHasOmitCookie.Bool(b)}
276+
}
277+
278+
func WithCSRFHasNoneCookie(b bool) otelutil.MetricOption {
279+
return metricOptionAttributeKeyValue{AttributeKeyCSRFHasNoneCookie.Bool(b)}
280+
}
281+
282+
func WithCSRFHasLaxCookie(b bool) otelutil.MetricOption {
283+
return metricOptionAttributeKeyValue{AttributeKeyCSRFHasLaxCookie.Bool(b)}
284+
}
285+
286+
func WithCSRFHasStrictCookie(b bool) otelutil.MetricOption {
287+
return metricOptionAttributeKeyValue{AttributeKeyCSRFHasStrictCookie.Bool(b)}
288+
}
289+
290+
func WithGorillaCSRFFailureReason(reason string) otelutil.MetricOption {
291+
return metricOptionAttributeKeyValue{AttributeKeyGorillaCSRFFailureReason.String(reason)}
292+
}
293+
255294
func SetProjectID(ctx context.Context, projectID string) {
256295
labeler, _ := otelhttp.LabelerFromContext(ctx)
257296
labeler.Add(attributeKeyProjectID.String(projectID))

0 commit comments

Comments
 (0)