fix API handler to respect referer + exit on CORS
this commit makes the API handler short circuit the request if the CORS headers say its not allowed. (the CORS handler only sets the headers, but does not short-circuit) It also makes the handler respect the referer again. See security discussion at https://github.com/ipfs/go-ipfs/issues/1532 License: MIT Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
Juan Batiz-Benet committed
Jul 28, 2015 at 22:57 UTC
d5f94be474ebb723efa3002517a03fc79290585f
2 files changed
+74
-3
commands/http/handler.go
+71
@@ -45,6 +45,13 @@ const (
45
applicationJson = "application/json"
46
applicationOctetStream = "application/octet-stream"
47
plainText = "text/plain"
48
+ originHeader = "origin"
49
+)
50
+
51
+const (
52
+ ACAOrigin = "Access-Control-Allow-Origin"
53
+ ACAMethods = "Access-Control-Allow-Methods"
54
+ ACACredentials = "Access-Control-Allow-Credentials"
55
)
56
57
var localhostOrigins = []string{
@@ -115,6 +122,13 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
122
func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
123
log.Debug("Incoming API request: ", r.URL)
124
125
+ if !allowOrigin(r, i.cfg) || !allowReferer(r, i.cfg) {
126
+ w.WriteHeader(http.StatusForbidden)
127
+ w.Write([]byte("403 - Forbidden"))
128
+ log.Warningf("API blocked request to %s. (possible CSRF)", r.URL)
129
+ return
130
+ }
131
+
132
req, err := Parse(r, i.root)
133
if err != nil {
134
if err == ErrNotFound {
@@ -310,3 +324,60 @@ func sanitizedErrStr(err error) string {
324
s = strings.Split(s, "\r")[0]
325
return s
326
}
327
+
328
+// allowOrigin just stops the request if the origin is not allowed.
329
+// the CORS middleware apparently does not do this for us...
330
+func allowOrigin(r *http.Request, cfg *ServerConfig) bool {
331
+ origin := r.Header.Get("Origin")
332
+
333
+ // curl, or ipfs shell, typing it in manually, or clicking link
334
+ // NOT in a browser. this opens up a hole. we should close it,
335
+ // but right now it would break things. TODO
336
+ if origin == "" {
337
+ return true
338
+ }
339
+
340
+ for _, o := range cfg.CORSOpts.AllowedOrigins {
341
+ if o == "*" { // ok! you asked for it!
342
+ return true
343
+ }
344
+
345
+ if o == origin { // allowed explicitly
346
+ return true
347
+ }
348
+ }
349
+
350
+ return false
351
+}
352
+
353
+// allowReferer this is here to prevent some CSRF attacks that
354
+// the API would be vulnerable to. We check that the Referer
355
+// is allowed by CORS Origin (origins and referrers here will
356
+// work similarly in the normla uses of the API).
357
+// See discussion at https://github.com/ipfs/go-ipfs/issues/1532
358
+func allowReferer(r *http.Request, cfg *ServerConfig) bool {
359
+ referer := r.Referer()
360
+
361
+ // curl, or ipfs shell, typing it in manually, or clicking link
362
+ // NOT in a browser. this opens up a hole. we should close it,
363
+ // but right now it would break things. TODO
364
+ if referer == "" {
365
+ return true
366
+ }
367
+
368
+ // check CORS ACAOs and pretend Referer works like an origin.
369
+ // this is valid for many (most?) sane uses of the API in
370
+ // other applications, and will have the desired effect.
371
+ for _, o := range cfg.CORSOpts.AllowedOrigins {
372
+ if o == "*" { // ok! you asked for it!
373
+ return true
374
+ }
375
+
376
+ // referer is allowed explicitly
377
+ if o == referer {
378
+ return true
379
+ }
380
+ }
381
+
382
+ return false
383
+}
core/corehttp/commands.go
+3
-3
@@ -42,13 +42,13 @@ func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
42
func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
43
log.Info("Using API.HTTPHeaders:", nc.API.HTTPHeaders)
44
45
- if acao := nc.API.HTTPHeaders["Access-Control-Allow-Origin"]; acao != nil {
45
+ if acao := nc.API.HTTPHeaders[cmdsHttp.ACAOrigin]; acao != nil {
46
c.CORSOpts.AllowedOrigins = acao
47
}
48
- if acam := nc.API.HTTPHeaders["Access-Control-Allow-Methods"]; acam != nil {
48
+ if acam := nc.API.HTTPHeaders[cmdsHttp.ACAMethods]; acam != nil {
49
c.CORSOpts.AllowedMethods = acam
50
}
51
- if acac := nc.API.HTTPHeaders["Access-Control-Allow-Credentials"]; acac != nil {
51
+ if acac := nc.API.HTTPHeaders[cmdsHttp.ACACredentials]; acac != nil {
52
for _, v := range acac {
53
c.CORSOpts.AllowCredentials = (strings.ToLower(v) == "true")
54
}