Skip to content

Commit

Permalink
fix API handler to respect referer + exit on CORS
Browse files Browse the repository at this point in the history
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 #1532

License: MIT
Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
  • Loading branch information
jbenet committed Jul 29, 2015
1 parent 5d9ee59 commit d5f94be
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
71 changes: 71 additions & 0 deletions commands/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ const (
applicationJson = "application/json"
applicationOctetStream = "application/octet-stream"
plainText = "text/plain"
originHeader = "origin"
)

const (
ACAOrigin = "Access-Control-Allow-Origin"
ACAMethods = "Access-Control-Allow-Methods"
ACACredentials = "Access-Control-Allow-Credentials"
)

var localhostOrigins = []string{
Expand Down Expand Up @@ -115,6 +122,13 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Debug("Incoming API request: ", r.URL)

if !allowOrigin(r, i.cfg) || !allowReferer(r, i.cfg) {
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("403 - Forbidden"))
log.Warningf("API blocked request to %s. (possible CSRF)", r.URL)
return
}

req, err := Parse(r, i.root)
if err != nil {
if err == ErrNotFound {
Expand Down Expand Up @@ -310,3 +324,60 @@ func sanitizedErrStr(err error) string {
s = strings.Split(s, "\r")[0]
return s
}

// allowOrigin just stops the request if the origin is not allowed.
// the CORS middleware apparently does not do this for us...
func allowOrigin(r *http.Request, cfg *ServerConfig) bool {
origin := r.Header.Get("Origin")

// curl, or ipfs shell, typing it in manually, or clicking link
// NOT in a browser. this opens up a hole. we should close it,
// but right now it would break things. TODO
if origin == "" {
return true
}

for _, o := range cfg.CORSOpts.AllowedOrigins {
if o == "*" { // ok! you asked for it!
return true
}

if o == origin { // allowed explicitly
return true
}
}

return false
}

// allowReferer this is here to prevent some CSRF attacks that
// the API would be vulnerable to. We check that the Referer
// is allowed by CORS Origin (origins and referrers here will
// work similarly in the normla uses of the API).
// See discussion at https://github.com/ipfs/go-ipfs/issues/1532
func allowReferer(r *http.Request, cfg *ServerConfig) bool {
referer := r.Referer()

// curl, or ipfs shell, typing it in manually, or clicking link
// NOT in a browser. this opens up a hole. we should close it,
// but right now it would break things. TODO
if referer == "" {
return true
}

// check CORS ACAOs and pretend Referer works like an origin.
// this is valid for many (most?) sane uses of the API in
// other applications, and will have the desired effect.
for _, o := range cfg.CORSOpts.AllowedOrigins {
if o == "*" { // ok! you asked for it!
return true
}

// referer is allowed explicitly
if o == referer {
return true
}
}

return false
}
6 changes: 3 additions & 3 deletions core/corehttp/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
log.Info("Using API.HTTPHeaders:", nc.API.HTTPHeaders)

if acao := nc.API.HTTPHeaders["Access-Control-Allow-Origin"]; acao != nil {
if acao := nc.API.HTTPHeaders[cmdsHttp.ACAOrigin]; acao != nil {
c.CORSOpts.AllowedOrigins = acao
}
if acam := nc.API.HTTPHeaders["Access-Control-Allow-Methods"]; acam != nil {
if acam := nc.API.HTTPHeaders[cmdsHttp.ACAMethods]; acam != nil {
c.CORSOpts.AllowedMethods = acam
}
if acac := nc.API.HTTPHeaders["Access-Control-Allow-Credentials"]; acac != nil {
if acac := nc.API.HTTPHeaders[cmdsHttp.ACACredentials]; acac != nil {
for _, v := range acac {
c.CORSOpts.AllowCredentials = (strings.ToLower(v) == "true")
}
Expand Down

0 comments on commit d5f94be

Please sign in to comment.