csrf icon indicating copy to clipboard operation
csrf copied to clipboard

[BUG] Forbidden - Invalid Origin in localhost

Open shamilovstas opened this issue 6 months ago • 4 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Current Behavior

When running in localhost, every csrf check returns "Invalid Origin" error. go-chi router is used.

router.Use(CsrfMiddleware(mustGetCsrfKey()))
func CsrfMiddleware(key []byte) func(http.Handler) http.Handler {
	var opts []csrf.Option
	env := os.Getenv("APP_ENV")
	if env == "local" || env == "dev" {
		opts = append(opts, csrf.Secure(false))
	}
	csrfFn := csrf.Protect(key, opts...)
	return csrfFn
}

I made sure that the APP_ENV variable is correctly set and csrf.Protect is called with csrf.Secure(false) option. After debugging I noticed the error happens do to the check if !sameOrigin(&requestURL, parsedOrigin) ... in gorilla/csrf/csrf.go. The request url gets rewritten in the same csrf.go file in this block:

requestURL := *r.URL // shallow clone

requestURL.Scheme = "https"
if isPlaintext {
    requestURL.Scheme = "http"
}
if requestURL.Host == "" {
    requestURL.Host = r.Host
}

Expected Behavior

csrf.Secure(false) option should prevent rewriting url scheme from "http" to "https"

Steps To Reproduce

No response

Anything else?

No response

shamilovstas avatar May 14 '25 12:05 shamilovstas

Update: does not reproduce in the version 1.7.2 or lower, only 1.7.3 is affected. There is a context key PlaintextHTTPContextKey which is AFAIU supposed to be a switch for that url rewrite, but it's not used anywhere apart from tests. According to the commit message that introduced that change, the key is meant to be used by the clients of the library, is it correct?

shamilovstas avatar May 14 '25 13:05 shamilovstas

I was able to reproduce this on localhost using 1.7.3. Workaround is to downgrade to 1.7.2 as mentioned. Minimal example:

router:

...
	csrfKey := "abcd1234abcd1234abcd1234abcd1234"
	csrfMiddleware := csrf.Protect(
		[]byte(csrfKey),
		csrf.Secure(false),
	)
	http.ListenAndServe(":3000", csrfMiddleware(r))

controller:

func (u Users) Signup(w http.ResponseWriter, r *http.Request) {
	var data struct {
		Email     string
		CSRFField template.HTML
	}
	data.Email = r.FormValue("email")
	data.CSRFField = csrf.TemplateField(r)
	u.Templates.New.Execute(w, data) // template form with CSRF hidden input
}

Verified that the form hidden field and CSRF cookie were all properly set / sent with the POST requests. Received aforementioned Forbidden - Invalid Origin error with 403

kjpark avatar May 22 '25 00:05 kjpark

My understanding is that PlaintextHTTPRequest() is the library function meant to be used to allow plain text requests (just because it's an exported function - please correct me if I'm wrong).

I rewrote the @shamilovstas's original snippet to use the function:

func CsrfMiddleware(key []byte) func(http.Handler) http.Handler {
	var opts []csrf.Option
	env := os.Getenv("APP_ENV")
	if env == "local" || env == "dev" {
		opts = append(opts, csrf.Secure(false))
	}
	csrfFn := csrf.Protect(key, opts...)

	return func(next http.Handler) http.Handler {
		fn := func(w http.ResponseWriter, r *http.Request) {
			// Need this starting from gorilla/csrf 1.7.3.
			if env == "local" || env == "dev" {
				r = csrf.PlaintextHTTPRequest(r)
			}
			csrfFn(next).ServeHTTP(w, r)
		}

		return http.HandlerFunc(fn)
	}
}

Tangentially, the use of PlaintextHTTPRequest() to enable optional behavior seems to me to be counter to the conventions of this package; it seems having another option available for passing to Protect() should be the way to go instead.

geokat avatar May 26 '25 01:05 geokat

Funny, just got this error as well. Forgot I had updated this dependency and here we are.

hugmouse avatar May 27 '25 18:05 hugmouse

Hello! I wanted to ask when there will be a fix for this bug? The billing team at GitHub relies on this package for our service but we need to update from 1.7.2 in order to fix a security vulnerability.

davidjunxyang avatar Jun 16 '25 22:06 davidjunxyang

I also encountered the same issue with go-chi. Downgrade to 1.7.2 to temporarily fix that.

yeungon avatar Jul 03 '25 01:07 yeungon

I also encountered the same issue with go-chi. Downgrade to 1.7.2 to temporarily fix that.

I also encountered it locally & this fixes it

csrf.TrustedOrigins([]string{"localhost:3000"}),

but just a question is this causing issues during deployments as well or not? I don't have a deployment yet.

IMPERIAL-HEX avatar Jul 03 '25 18:07 IMPERIAL-HEX

I also encountered the same issue with go-chi. Downgrade to 1.7.2 to temporarily fix that.

I also encountered it locally & this fixes it

csrf.TrustedOrigins([]string{"localhost:3000"}),

but just a question is this causing issues during deployments as well or not? I don't have a deployment yet.

try that one as suggested somewhere but didn't work for no reason.

yeungon avatar Jul 13 '25 02:07 yeungon

I also encountered the same issue with go-chi. Downgrade to 1.7.2 to temporarily fix that.

I also encountered it locally & this fixes it

csrf.TrustedOrigins([]string{"localhost:3000"}),

but just a question is this causing issues during deployments as well or not? I don't have a deployment yet.

This didn't fix it for me. I downgraded temporarily instead.

Midwayne avatar Sep 21 '25 18:09 Midwayne