Skip to content

Commit be8c0bd

Browse files
authored
Merge pull request #193 from ipfs/fix/disallow-agent
Fix: disallow POST without Origin nor Referer from specific user agents
2 parents f2a4f7b + b00bc40 commit be8c0bd

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed

http/config.go

+32
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package http
33
import (
44
"net/http"
55
"net/url"
6+
"strings"
67
"sync"
78

89
cors "github.com/rs/cors"
@@ -150,3 +151,34 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
150151

151152
return false
152153
}
154+
155+
// allowUserAgent checks the request's user-agent against the list
156+
// of DisallowUserAgents for requests with no origin nor referer set.
157+
func allowUserAgent(r *http.Request, cfg *ServerConfig) bool {
158+
// This check affects POST as we should never get POST requests from a
159+
// browser without Origin or Referer, but we might:
160+
// https://bugzilla.mozilla.org/show_bug.cgi?id=429594.
161+
if r.Method != http.MethodPost {
162+
return true
163+
}
164+
165+
origin := r.Header.Get("Origin")
166+
referer := r.Referer()
167+
168+
// If these are set, we leave up to CORS and CSRF checks.
169+
if origin != "" || referer != "" {
170+
return true
171+
}
172+
173+
// Allow if the user agent does not start with Mozilla... (i.e. curl)
174+
ua := r.Header.Get("User-agent")
175+
if !strings.HasPrefix(ua, "Mozilla") {
176+
return true
177+
}
178+
179+
// Disallow otherwise.
180+
//
181+
// This means the request probably came from a browser and thus, it
182+
// should have included Origin or referer headers.
183+
return false
184+
}

http/errors_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,42 @@ func TestUnhandledMethod(t *testing.T) {
170170
}
171171
tc.test(t)
172172
}
173+
174+
func TestDisallowedUserAgents(t *testing.T) {
175+
tcs := []httpTestCase{
176+
{
177+
// Block Mozilla* browsers that do not provide origins.
178+
Method: "POST",
179+
AllowGet: false,
180+
Code: http.StatusForbidden,
181+
ReqHeaders: map[string]string{
182+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0",
183+
},
184+
},
185+
{
186+
// Do not block on GETs
187+
Method: "GET",
188+
AllowGet: true,
189+
Code: http.StatusOK,
190+
ReqHeaders: map[string]string{
191+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0",
192+
},
193+
},
194+
{
195+
// Do not block a Mozilla* browser that provides an
196+
// allowed Origin
197+
Method: "POST",
198+
AllowGet: false,
199+
AllowOrigins: []string{"*"},
200+
Origin: "null",
201+
Code: http.StatusOK,
202+
ReqHeaders: map[string]string{
203+
"User-Agent": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Build/KLP) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
204+
},
205+
},
206+
}
207+
208+
for _, tc := range tcs {
209+
tc.test(t)
210+
}
211+
}

http/handler.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
121121
return
122122
}
123123

124-
if !allowOrigin(r, h.cfg) || !allowReferer(r, h.cfg) {
124+
if !allowOrigin(r, h.cfg) || !allowReferer(r, h.cfg) || !allowUserAgent(r, h.cfg) {
125125
http.Error(w, "403 - Forbidden", http.StatusForbidden)
126126
log.Warnf("API blocked request to %s. (possible CSRF)", r.URL)
127127
return

0 commit comments

Comments
 (0)