Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions csrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,16 @@ func (cs *csrf) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// HTTP methods not defined as idempotent ("safe") under RFC7231 require
// inspection.
if !contains(safeMethods, r.Method) {
var isPlaintext bool
val := r.Context().Value(PlaintextHTTPContextKey)
if val != nil {
isPlaintext, _ = val.(bool)
// Secure(false) is the documented local-dev flag. Treat those
// requests as plaintext so Origin: http://localhost:... matches
// instead of being compared against a rewritten https URL.
// PlaintextHTTPRequest still forces this when Secure stays true
// behind a cleartext reverse proxy.
isPlaintext := !cs.opts.Secure
if val := r.Context().Value(PlaintextHTTPContextKey); val != nil {
if b, ok := val.(bool); ok && b {
isPlaintext = true
}
}

// take a copy of the request URL to avoid mutating the original
Expand Down
33 changes: 33 additions & 0 deletions csrf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,39 @@ func setCookie(rr *httptest.ResponseRecorder, r *http.Request) {
r.Header.Set("Cookie", rr.Header().Get("Set-Cookie"))
}

// Secure(false) is the documented local-dev option. A POST from
// http://localhost must pass the Origin check without wrapping the
// request in PlaintextHTTPRequest (issue #190).
func TestSecureFalseAllowsHTTPOrigin(t *testing.T) {
mux := http.NewServeMux()
var token string
mux.Handle("/", http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
token = Token(r)
}))
passed := false
mux.Handle("/submit", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
passed = true
}))
p := Protect(testKey, Secure(false))(mux)

r := httptest.NewRequest("GET", "/", nil)
r.Host = "localhost:3000"
rr := httptest.NewRecorder()
p.ServeHTTP(rr, r)

r = httptest.NewRequest("POST", "/submit", nil)
r.Host = "localhost:3000"
r.Header.Set("Origin", "http://localhost:3000")
setCookie(rr, r)
r.Header.Set("X-CSRF-Token", token)

rr = httptest.NewRecorder()
p.ServeHTTP(rr, r)
if rr.Code != http.StatusOK || !passed {
t.Fatalf("Secure(false) should accept http Origin on localhost: code=%d passed=%v", rr.Code, passed)
}
}

func TestProtectScenarios(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading