@@ -3,6 +3,7 @@ package http
3
3
import (
4
4
"net/http"
5
5
"net/url"
6
+ "strings"
6
7
"sync"
7
8
8
9
cors "github.com/rs/cors"
@@ -14,6 +15,19 @@ const (
14
15
ACACredentials = "Access-Control-Allow-Credentials"
15
16
)
16
17
18
+ // disallowedUserAgents specifies a denylist of user agents that are not
19
+ // allowed to perform POST requests if they are not providing Origin
20
+ // and/or Referer headers. As mitigation for things like
21
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=429594. Defaults to
22
+ // Firefox-related things. The matching against the user-agent string
23
+ // is made with strings.Contains().
24
+ var disallowedUserAgents = []string {
25
+ "Firefox" ,
26
+ "Focus" ,
27
+ "Klar" ,
28
+ "FxiOS" ,
29
+ }
30
+
17
31
type ServerConfig struct {
18
32
// APIPath is the prefix of all request paths.
19
33
// Example: host:port/api/v0/add. Here the APIPath is /api/v0
@@ -30,6 +44,14 @@ type ServerConfig struct {
30
44
// websites to include resources from the API but not _read_ them.
31
45
AllowGet bool
32
46
47
+ // DisallowUserAgents specifies a blacklist of user agents that are not
48
+ // allowed to perform POST requests if they are not providing Origin
49
+ // and/or Referer headers. As mitigation for things like
50
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=429594.
51
+ // Defaults to ["Firefox"]. The matching against the user-agent
52
+ // string is made with strings.Contains().
53
+ DisallowUserAgents []string
54
+
33
55
// corsOpts is a set of options for CORS headers.
34
56
corsOpts * cors.Options
35
57
@@ -150,3 +172,31 @@ func allowReferer(r *http.Request, cfg *ServerConfig) bool {
150
172
151
173
return false
152
174
}
175
+
176
+ // allowUserAgent checks the request's user-agent against the list
177
+ // of DisallowUserAgents for requests with no origin nor referer set.
178
+ func allowUserAgent (r * http.Request , cfg * ServerConfig ) bool {
179
+ // This check affects POST as we should never get POST requests from a
180
+ // browser without Origin or Referer, but we might:
181
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=429594.
182
+ if r .Method != http .MethodPost {
183
+ return true
184
+ }
185
+
186
+ origin := r .Header .Get ("Origin" )
187
+ referer := r .Referer ()
188
+
189
+ // If these are set, we leave up to CORS and CSRF checks.
190
+ if origin != "" || referer != "" {
191
+ return true
192
+ }
193
+
194
+ // If not, check that request is not from a blacklisted UA.
195
+ ua := r .Header .Get ("User-agent" )
196
+ for _ , forbiddenUA := range disallowedUserAgents {
197
+ if strings .Contains (ua , forbiddenUA ) {
198
+ return false
199
+ }
200
+ }
201
+ return true
202
+ }
0 commit comments