CSRF is a middleware that verifies that a CSRF token is present in the request for non-GET requests. If enforce is false, then CSRF enforcement is disabled. We still want to include the CSRF middleware because it will set the CSRF cookie.
(cookieCfg codersdk.HTTPCookieConfig)
| 17 | // If enforce is false, then CSRF enforcement is disabled. We still want |
| 18 | // to include the CSRF middleware because it will set the CSRF cookie. |
| 19 | func CSRF(cookieCfg codersdk.HTTPCookieConfig) func(next http.Handler) http.Handler { |
| 20 | return func(next http.Handler) http.Handler { |
| 21 | mw := nosurf.New(next) |
| 22 | mw.SetBaseCookie(*cookieCfg.Apply(&http.Cookie{Path: "/", HttpOnly: true})) |
| 23 | mw.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 24 | sessCookie, err := r.Cookie(codersdk.SessionTokenCookie) |
| 25 | if err == nil && |
| 26 | r.Header.Get(codersdk.SessionTokenHeader) != "" && |
| 27 | r.Header.Get(codersdk.SessionTokenHeader) != sessCookie.Value { |
| 28 | // If a user is using header authentication and cookie auth, but the values |
| 29 | // do not match, the cookie value takes priority. |
| 30 | // At the very least, return a more helpful error to the user. |
| 31 | http.Error(w, |
| 32 | fmt.Sprintf("CSRF error encountered. Authentication via %q cookie and %q header detected, but the values do not match. "+ |
| 33 | "To resolve this issue ensure the values used in both match, or only use one of the authentication methods. "+ |
| 34 | "You can also try clearing your cookies if this error persists.", |
| 35 | codersdk.SessionTokenCookie, codersdk.SessionTokenHeader), |
| 36 | http.StatusBadRequest) |
| 37 | return |
| 38 | } |
| 39 | |
| 40 | http.Error(w, "Something is wrong with your CSRF token. Please refresh the page. If this error persists, try clearing your cookies.", http.StatusBadRequest) |
| 41 | })) |
| 42 | |
| 43 | mw.ExemptRegexp(regexp.MustCompile("/api/v2/users/first")) |
| 44 | |
| 45 | // Exempt all requests that do not require CSRF protection. |
| 46 | // All GET requests are exempt by default. |
| 47 | mw.ExemptPath("/api/v2/csp/reports") |
| 48 | |
| 49 | // This should not be required? |
| 50 | mw.ExemptRegexp(regexp.MustCompile("/api/v2/users/first")) |
| 51 | |
| 52 | // Agent authenticated routes |
| 53 | mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceagents/me/*")) |
| 54 | mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceagents/*")) |
| 55 | // Workspace Proxy routes |
| 56 | mw.ExemptRegexp(regexp.MustCompile("api/v2/workspaceproxies/me/*")) |
| 57 | // Derp routes |
| 58 | mw.ExemptRegexp(regexp.MustCompile("derp/*")) |
| 59 | // Scim |
| 60 | mw.ExemptRegexp(regexp.MustCompile("api/v2/scim/*")) |
| 61 | // Provisioner daemon routes |
| 62 | mw.ExemptRegexp(regexp.MustCompile("/organizations/[^/]+/provisionerdaemons/*")) |
| 63 | |
| 64 | mw.ExemptFunc(func(r *http.Request) bool { |
| 65 | // Enforce CSRF on API routes and the OAuth2 authorize |
| 66 | // endpoint. The authorize endpoint serves a browser consent |
| 67 | // form whose POST must be CSRF-protected to prevent |
| 68 | // cross-site authorization code theft (coder/security#121). |
| 69 | if !strings.HasPrefix(r.URL.Path, "/api") && |
| 70 | !strings.HasPrefix(r.URL.Path, "/oauth2/authorize") { |
| 71 | return true |
| 72 | } |
| 73 | |
| 74 | // CSRF only affects requests that automatically attach credentials via a cookie. |
| 75 | // If no cookie is present, then there is no risk of CSRF. |
| 76 | sessCookie, err := r.Cookie(codersdk.SessionTokenCookie) |