Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Sometimes missing Vary: Origin header may lead to Web cache poisoning #248

Open
1 task done
jub0bs opened this issue Aug 31, 2023 · 0 comments
Open
1 task done
Labels

Comments

@jub0bs
Copy link

jub0bs commented Aug 31, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

The CORS middleware fails to add a Vary: Origin header in responses in some cases where it's needed.

Multiple origins allowed vs CORS request from a disallowed origin

If multiple origins are allowed (but not via the wildcard) and a CORS request comes from a disallowed origin, the response lacks a Vary: Origin header. Here's a failing test case that illustrates the problem:

func TestMultipleOriginsAllowedVersusCorsRequestFromDisallowedOrigin(t *testing.T) {
	cors := CORS(
		AllowedOrigins([]string{
			"https://example.com",
			"https://example.org",
		}),
	)
	r := newRequest(http.MethodGet, "https://backend.com")
	r.Header.Set("Origin", "https://disallowed.com")
	rr := httptest.NewRecorder()

	testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

	cors(testHandler).ServeHTTP(rr, r)
	resp := rr.Result()
	if resp.Header.Get("Vary") != "Origin" {
		t.Fatal("Origin not listed in Vary header")
	}
}

Why is it problematic? Because caching intermediaries may cache such a response, which would subsequently get served to clients that do issue requests from allowed origins. In essence, this would lead to a limited form of denial of service for as long as that "bad" response remains in the cache.

Multiple origins allowed vs non-CORS request

A similar issue exists when the request lacks an Origin header. Here's a failing test case that illustrates the problem:

func TestMultipleOriginsAllowedVersusNonCorsRequest(t *testing.T) {
	cors := CORS(
		AllowedOrigins([]string{
			"https://example.com",
			"https://example.org",
		}),
	)
	r := newRequest(http.MethodGet, "https://backend.com")
	rr := httptest.NewRecorder()

	testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

	cors(testHandler).ServeHTTP(rr, r)
	resp := rr.Result()
	if resp.Header.Get("Vary") != "Origin" {
		t.Fatal("Origin not listed in Vary header")
	}
}

Multiple origins allowed via AllowedOriginValidator

Similar issue for users who opt for AllowedOriginValidator to allow multiple origins:

func TestMultipleOriginsAllowedViaCallback(t *testing.T) {
	cors := CORS(
		AllowedOriginValidator(func(origin string) bool {
			switch origin {
			case "https://example.com", "https://example.org":
				return true
			default:
				return false
			}
		}),
	)
	r := newRequest(http.MethodGet, "https://backend.com")
	r.Header.Set("Origin", "https://example.com")
	rr := httptest.NewRecorder()

	testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

	cors(testHandler).ServeHTTP(rr, r)
	resp := rr.Result()
	if resp.Header.Get("Vary") != "Origin" {
		t.Fatal("Origin not listed in Vary header")
	}
}

Edit: this specific problem was already reported in #244

Expected Behavior

In both cases, the middleware should list Origin in a Vary response header.

Steps To Reproduce

Reproducible in v1.5.1.

Anything else?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: No status
Development

No branches or pull requests

1 participant