Skip to content

Commit

Permalink
Merge pull request #1988 from ipfs/ipfs-prefix-hardening
Browse files Browse the repository at this point in the history
gateway: harden path prefix
  • Loading branch information
whyrusleeping committed Apr 4, 2016
2 parents 8acd87d + 09937f8 commit c6e6bb0
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 19 deletions.
2 changes: 1 addition & 1 deletion cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
corehttp.CommandsROOption(*req.InvocContext()),
corehttp.VersionOption(),
corehttp.IPNSHostnameOption(),
corehttp.GatewayOption(writable),
corehttp.GatewayOption(writable, cfg.Gateway.PathPrefixes),
}

if len(cfg.Gateway.RootRedirect) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion cmd/ipfswatch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func run(ipfsPath, watchPath string) error {
if *http {
addr := "/ip4/127.0.0.1/tcp/5001"
var opts = []corehttp.ServeOption{
corehttp.GatewayOption(true),
corehttp.GatewayOption(true, nil),
corehttp.WebUIOption,
corehttp.CommandsOption(cmdCtx(node, ipfsPath)),
}
Expand Down
14 changes: 8 additions & 6 deletions core/corehttp/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ type Gateway struct {
}

type GatewayConfig struct {
Headers map[string][]string
BlockList *BlockList
Writable bool
Headers map[string][]string
BlockList *BlockList
Writable bool
PathPrefixes []string
}

func NewGateway(conf GatewayConfig) *Gateway {
Expand Down Expand Up @@ -48,10 +49,11 @@ func (g *Gateway) ServeOption() ServeOption {
}
}

func GatewayOption(writable bool) ServeOption {
func GatewayOption(writable bool, prefixes []string) ServeOption {
g := NewGateway(GatewayConfig{
Writable: writable,
BlockList: &BlockList{},
Writable: writable,
BlockList: &BlockList{},
PathPrefixes: prefixes,
})
return g.ServeOption()
}
Expand Down
9 changes: 7 additions & 2 deletions core/corehttp/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,13 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
// It will be prepended to links in directory listings and the index.html redirect.
prefix := ""
if prefixHdr := r.Header["X-Ipfs-Gateway-Prefix"]; len(prefixHdr) > 0 {
log.Debugf("X-Ipfs-Gateway-Prefix: %s", prefixHdr[0])
prefix = prefixHdr[0]
prfx := prefixHdr[0]
for _, p := range i.config.PathPrefixes {
if prfx == p || strings.HasPrefix(prfx, p+"/") {
prefix = prfx
break
}
}
}

// IPNSHostnameOption might have constructed an IPNS path using the Host header.
Expand Down
60 changes: 52 additions & 8 deletions core/corehttp/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, *core
ts.Listener,
VersionOption(),
IPNSHostnameOption(),
GatewayOption(false),
GatewayOption(false, []string{"/good-prefix"}),
)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -227,7 +227,7 @@ func TestIPNSHostnameRedirect(t *testing.T) {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")
req.Header.Set("X-Ipfs-Gateway-Prefix", "/good-prefix")

res, err = doWithoutRedirect(req)
if err != nil {
Expand All @@ -241,8 +241,8 @@ func TestIPNSHostnameRedirect(t *testing.T) {
hdr = res.Header["Location"]
if len(hdr) < 1 {
t.Errorf("location header not present")
} else if hdr[0] != "/prefix/foo/" {
t.Errorf("location header is %v, expected /prefix/foo/", hdr[0])
} else if hdr[0] != "/good-prefix/foo/" {
t.Errorf("location header is %v, expected /good-prefix/foo/", hdr[0])
}
}

Expand Down Expand Up @@ -387,7 +387,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")
req.Header.Set("X-Ipfs-Gateway-Prefix", "/good-prefix")

res, err = doWithoutRedirect(req)
if err != nil {
Expand All @@ -402,13 +402,57 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
s = string(body)
t.Logf("body: %s\n", string(body))

if !strings.Contains(s, "Index of /prefix") {
if !strings.Contains(s, "Index of /good-prefix") {
t.Fatalf("expected a path in directory listing")
}
if !strings.Contains(s, "<a href=\"/prefix/\">") {
if !strings.Contains(s, "<a href=\"/good-prefix/\">") {
t.Fatalf("expected backlink in directory listing")
}
if !strings.Contains(s, "<a href=\"/prefix/file.txt\">") {
if !strings.Contains(s, "<a href=\"/good-prefix/file.txt\">") {
t.Fatalf("expected file in directory listing")
}

// make request to directory listing with illegal prefix
req, err = http.NewRequest("GET", ts.URL, nil)
if err != nil {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "/bad-prefix")

res, err = doWithoutRedirect(req)
if err != nil {
t.Fatal(err)
}

// make request to directory listing with evil prefix
req, err = http.NewRequest("GET", ts.URL, nil)
if err != nil {
t.Fatal(err)
}
req.Host = "example.net"
req.Header.Set("X-Ipfs-Gateway-Prefix", "//good-prefix/foo")

res, err = doWithoutRedirect(req)
if err != nil {
t.Fatal(err)
}

// expect correct backlinks without illegal prefix
body, err = ioutil.ReadAll(res.Body)
if err != nil {
t.Fatalf("error reading response: %s", err)
}
s = string(body)
t.Logf("body: %s\n", string(body))

if !strings.Contains(s, "Index of /") {
t.Fatalf("expected a path in directory listing")
}
if !strings.Contains(s, "<a href=\"/\">") {
t.Fatalf("expected backlink in directory listing")
}
if !strings.Contains(s, "<a href=\"/file.txt\">") {
t.Fatalf("expected file in directory listing")
}
}
Expand Down
1 change: 1 addition & 0 deletions repo/config/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ type Gateway struct {
HTTPHeaders map[string][]string // HTTP headers to return with the gateway
RootRedirect string
Writable bool
PathPrefixes []string
}
1 change: 1 addition & 0 deletions repo/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func Init(out io.Writer, nBitsForKeypair int) (*Config, error) {
Gateway: Gateway{
RootRedirect: "",
Writable: false,
PathPrefixes: []string{},
},
}

Expand Down
2 changes: 1 addition & 1 deletion test/supernode_client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func run() error {

opts := []corehttp.ServeOption{
corehttp.CommandsOption(cmdCtx(node, repoPath)),
corehttp.GatewayOption(false),
corehttp.GatewayOption(false, nil),
}

if *cat {
Expand Down

0 comments on commit c6e6bb0

Please sign in to comment.