@cryptotaxi247 / kubo / commits / 1b490476e

HTTP API: Disallow GET requests on API

This commit upgrades go-ipfs-cmds and configures the commands HTTP API Handler to only allow POST/OPTIONS, disallowing GET and others in the handling of command requests in the IPFS HTTP API (where before every type of request method was handled, with GET/POST/PUT/PATCH being equivalent). The Read-Only commands that the HTTP API attaches to the gateway endpoint will additional handled GET as they did before (but stop handling PUT,DELETEs). By limiting the request types we address the possibility that a website accessed by a browser abuses the IPFS API by issuing GET requests to it which have no Origin or Referrer set, and are thus bypass CORS and CSRF protections. This is a breaking change for clients that relay on GET requests against the HTTP endpoint (usually :5001). Applications integrating on top of the gateway-read-only API should still work (including cross-domain access). Co-Authored-By: Steven Allen <steven@stebalien.com> Co-Authored-By: Marcin Rataj <lidel@lidel.org>

Hector Sanjuan committed Apr 4, 2020 at 01:55 UTC 1b490476e5517931b8d31a6636e7008771db201d
13 files changed +38 -33
core/corehttp/commands.go
+12 -6
@@ -117,11 +117,17 @@ func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) {
117 c.SetAllowedOrigins(newOrigins...)
118 }
119
120 -func commandsOption(cctx oldcmds.Context, command *cmds.Command) ServeOption {
120 +func commandsOption(cctx oldcmds.Context, command *cmds.Command, allowGet bool) ServeOption {
121 return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
122
123 cfg := cmdsHttp.NewServerConfig()
124 - cfg.SetAllowedMethods(http.MethodGet, http.MethodPost, http.MethodPut)
124 + cfg.AllowGet = allowGet
125 + corsAllowedMethods := []string{http.MethodPost}
126 + if allowGet {
127 + corsAllowedMethods = append(corsAllowedMethods, http.MethodGet)
128 + }
129 +
130 + cfg.SetAllowedMethods(corsAllowedMethods...)
131 cfg.APIPath = APIPath
132 rcfg, err := n.Repo.Config()
133 if err != nil {
@@ -140,15 +146,15 @@ func commandsOption(cctx oldcmds.Context, command *cmds.Command) ServeOption {
146 }
147
148 // CommandsOption constructs a ServerOption for hooking the commands into the
143 -// HTTP server.
149 +// HTTP server. It will NOT allow GET requests.
150 func CommandsOption(cctx oldcmds.Context) ServeOption {
145 - return commandsOption(cctx, corecommands.Root)
151 + return commandsOption(cctx, corecommands.Root, false)
152 }
153
154 // CommandsROOption constructs a ServerOption for hooking the read-only commands
149 -// into the HTTP server.
155 +// into the HTTP server. It will allow GET requests.
156 func CommandsROOption(cctx oldcmds.Context) ServeOption {
151 - return commandsOption(cctx, corecommands.RootRO)
157 + return commandsOption(cctx, corecommands.RootRO, true)
158 }
159
160 // CheckVersionOption returns a ServeOption that checks whether the client ipfs version matches. Does nothing when the user agent string does not contain `/go-ipfs/`
core/corehttp/webui.go
+2 -1
@@ -1,7 +1,7 @@
1 package corehttp
2
3 // TODO: move to IPNS
4 -const WebUIPath = "/ipfs/Qmexhq2sBHnXQbvyP2GfUdbnY7HCagH2Mw5vUNSBn2nxip"
4 +const WebUIPath = "/ipfs/bafybeihpkhgv3jfnyx5qcexded7agjpwbgvtc3o6lnk6n3cs37fh4xx4fe"
5
6 // this is a list of all past webUI paths.
7 var WebUIPaths = []string{
@@ -33,6 +33,7 @@ var WebUIPaths = []string{
33 "/ipfs/QmcjeTciMNgEBe4xXvEaA4TQtwTRkXucx7DmKWViXSmX7m",
34 "/ipfs/QmfNbSskgvTXYhuqP8tb9AKbCkyRcCy3WeiXwD9y5LeoqK",
35 "/ipfs/QmPkojhjJkJ5LEGBDrAvdftrjAYmi9GU5Cq27mWvZTDieW",
36 + "/ipfs/Qmexhq2sBHnXQbvyP2GfUdbnY7HCagH2Mw5vUNSBn2nxip",
37 }
38
39 var WebUIOption = RedirectOption("webui", WebUIPath)
go.mod
+1 -1
@@ -30,7 +30,7 @@ require (
30 github.com/ipfs/go-graphsync v0.0.5
31 github.com/ipfs/go-ipfs-blockstore v0.1.4
32 github.com/ipfs/go-ipfs-chunker v0.0.5
33 - github.com/ipfs/go-ipfs-cmds v0.1.4
33 + github.com/ipfs/go-ipfs-cmds v0.2.1
34 github.com/ipfs/go-ipfs-config v0.4.0
35 github.com/ipfs/go-ipfs-ds-help v0.1.1
36 github.com/ipfs/go-ipfs-exchange-interface v0.0.1
go.sum
+2 -4
@@ -256,8 +256,8 @@ github.com/ipfs/go-ipfs-chunker v0.0.1 h1:cHUUxKFQ99pozdahi+uSC/3Y6HeRpi9oTeUHbE
256 github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw=
257 github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=
258 github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8=
259 -github.com/ipfs/go-ipfs-cmds v0.1.4 h1:l5QAc1iaoMZeBd2vpanrHWs26haEBL4PVqgoHJNG2GE=
260 -github.com/ipfs/go-ipfs-cmds v0.1.4/go.mod h1:wm+C6M8FYDcWPU/EdWqMuHvdyWborFh+GuDl6Ov6sM0=
259 +github.com/ipfs/go-ipfs-cmds v0.2.1 h1:xnZJjonqngoQRsrugXTQPMRn2KZwdn5H8N5+bLLYpO0=
260 +github.com/ipfs/go-ipfs-cmds v0.2.1/go.mod h1:kqlUrp6m2ceoaJe40cXpADCi5aS6NKRn0NIeuLp5CeM=
261 github.com/ipfs/go-ipfs-config v0.4.0 h1:MOXdj8EYQG55v1y+5e1QcctDKPEGobdwnXaDVa0/cc0=
262 github.com/ipfs/go-ipfs-config v0.4.0/go.mod h1:nSLCFtlaL+2rbl3F+9D4gQZQbT1LjRKx7TJg/IHz6oM=
263 github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
@@ -274,8 +274,6 @@ github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAz
274 github.com/ipfs/go-ipfs-files v0.0.2/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
275 github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
276 github.com/ipfs/go-ipfs-files v0.0.4/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
277 -github.com/ipfs/go-ipfs-files v0.0.7 h1:s5BRD12ndahqYifeH1S8Z73zqZhR+3IdKYAG9PiETs0=
278 -github.com/ipfs/go-ipfs-files v0.0.7/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs=
277 github.com/ipfs/go-ipfs-files v0.0.8 h1:8o0oFJkJ8UkO/ABl8T6ac6tKF3+NIpj67aAB6ZpusRg=
278 github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs=
279 github.com/ipfs/go-ipfs-flags v0.0.1/go.mod h1:RnXBb9WV53GSfTrSDVK61NLTFKvWc60n+K9EgCDh+rA=
test/dependencies/go.mod
+1 -1
@@ -17,7 +17,7 @@ require (
17 github.com/ipfs/go-unixfs v0.2.4
18 github.com/ipfs/hang-fds v0.0.2
19 github.com/ipfs/iptb v1.4.0
20 - github.com/ipfs/iptb-plugins v0.2.1
20 + github.com/ipfs/iptb-plugins v0.2.2
21 github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785
22 github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c
23 github.com/jbenet/go-random-files v0.0.0-20190219210431-31b3f20ebded
test/dependencies/go.sum
+2 -2
@@ -295,8 +295,8 @@ github.com/ipfs/hang-fds v0.0.2 h1:ffZPd+OFbKpfjNAoBCI+G7okTQKd7oS1jCEDm2Kzm4c=
295 github.com/ipfs/hang-fds v0.0.2/go.mod h1:Ajpp/qR2orKbv5LsZmotGRASTcH38MwcIG5vTlZ9y8k=
296 github.com/ipfs/iptb v1.4.0 h1:YFYTrCkLMRwk/35IMyC6+yjoQSHTEcNcefBStLJzgvo=
297 github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg=
298 -github.com/ipfs/iptb-plugins v0.2.1 h1:au4HWn9/pRPbkxA08pDx2oRAs4cnbgQWgV0teYXuuGA=
299 -github.com/ipfs/iptb-plugins v0.2.1/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs=
298 +github.com/ipfs/iptb-plugins v0.2.2 h1:HleRKMeex/jmQrmNG36v51M3eZO5j9BhFplBPGs0qGQ=
299 +github.com/ipfs/iptb-plugins v0.2.2/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs=
300 github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785 h1:fASnkvtR+SmB2y453RxmDD3Uvd4LonVUgFGk9JoDaZs=
301 github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w=
302 github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5 h1:lSip43rAdyGA+yRQuy6ju0ucZkWpYc1F2CTQtZTVW/4=
test/sharness/t0051-object.sh
+4 -4
@@ -419,19 +419,19 @@ test_object_cmd() {
419 test_object_content_type() {
420
421 test_expect_success "'ipfs object get --encoding=protobuf' returns the correct content type" '
422 - curl -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=protobuf" | grep -q "^Content-Type: application/protobuf"
422 + curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=protobuf" | grep -q "^Content-Type: application/protobuf"
423 '
424
425 test_expect_success "'ipfs object get --encoding=json' returns the correct content type" '
426 - curl -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=json" | grep -q "^Content-Type: application/json"
426 + curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=json" | grep -q "^Content-Type: application/json"
427 '
428
429 test_expect_success "'ipfs object get --encoding=text' returns the correct content type" '
430 - curl -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=text" | grep -q "^Content-Type: text/plain"
430 + curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=text" | grep -q "^Content-Type: text/plain"
431 '
432
433 test_expect_success "'ipfs object get --encoding=xml' returns the correct content type" '
434 - curl -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=xml" | grep -q "^Content-Type: application/xml"
434 + curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=xml" | grep -q "^Content-Type: application/xml"
435 '
436 }
437
test/sharness/t0060-daemon.sh
+3 -3
@@ -65,11 +65,11 @@ test_expect_success "ipfs peer id looks good" '
65 # this is for checking SetAllowedOrigins race condition for the api and gateway
66 # See https://github.com/ipfs/go-ipfs/pull/1966
67 test_expect_success "ipfs API works with the correct allowed origin port" '
68 - curl -s -X GET -H "Origin:http://localhost:$API_PORT" -I "http://$API_ADDR/api/v0/version"
68 + curl -s -X POST -H "Origin:http://localhost:$API_PORT" -I "http://$API_ADDR/api/v0/version"
69 '
70
71 test_expect_success "ipfs gateway works with the correct allowed origin port" '
72 - curl -s -X GET -H "Origin:http://localhost:$GWAY_PORT" -I "http://$GWAY_ADDR/api/v0/version"
72 + curl -s -X POST -H "Origin:http://localhost:$GWAY_PORT" -I "http://$GWAY_ADDR/api/v0/version"
73 '
74
75 test_expect_success "ipfs daemon output looks good" '
@@ -134,7 +134,7 @@ test_expect_success SOCAT "transport should be encrypted ( needs socat )" '
134 '
135
136 test_expect_success "output from streaming commands works" '
137 - test_expect_code 28 curl -m 5 http://localhost:$API_PORT/api/v0/stats/bw\?poll=true > statsout
137 + test_expect_code 28 curl -X POST -m 5 http://localhost:$API_PORT/api/v0/stats/bw\?poll=true > statsout
138 '
139
140 test_expect_success "output looks good" '
test/sharness/t0090-get.sh
+1 -1
@@ -184,7 +184,7 @@ test_launch_ipfs_daemon
184 test_get_cmd
185
186 test_expect_success "empty request to get doesn't panic and returns error" '
187 - curl "http://$API_ADDR/api/v0/get" > curl_out || true &&
187 + curl -X POST "http://$API_ADDR/api/v0/get" > curl_out || true &&
188 grep "argument \"ipfs-path\" is required" curl_out
189 '
190 test_kill_ipfs_daemon
test/sharness/t0100-name.sh
+1 -1
@@ -195,7 +195,7 @@ test_expect_success "resolve output looks good" '
195 '
196
197 test_expect_success "empty request to name publish doesn't panic and returns error" '
198 - curl "http://$API_ADDR/api/v0/name/publish" > curl_out || true &&
198 + curl -X POST "http://$API_ADDR/api/v0/name/publish" > curl_out || true &&
199 grep "argument \"ipfs-path\" is required" curl_out
200 '
201
test/sharness/t0110-gateway.sh
+1 -1
@@ -104,7 +104,7 @@ test_expect_success "log output looks good" '
104 '
105
106 test_expect_success "GET /api/v0/version succeeds" '
107 - curl -v "http://127.0.0.1:$apiport/api/v0/version" 2> version_out
107 + curl -X POST -v "http://127.0.0.1:$apiport/api/v0/version" 2> version_out
108 '
109
110 test_expect_success "output only has one transfer encoding header" '
test/sharness/t0230-channel-streaming-http-content-type.sh
+2 -2
@@ -16,7 +16,7 @@ test_ls_cmd() {
16 mkdir -p testdir &&
17 echo "hello test" >testdir/test.txt &&
18 ipfs add -r testdir &&
19 - curl -i "http://$API_ADDR/api/v0/refs?arg=QmTcJAn3JP8ZMAKS6WS75q8sbTyojWKbxcUHgLYGWur4Ym&stream-channels=true&encoding=text" >actual_output
19 + curl -X POST -i "http://$API_ADDR/api/v0/refs?arg=QmTcJAn3JP8ZMAKS6WS75q8sbTyojWKbxcUHgLYGWur4Ym&stream-channels=true&encoding=text" >actual_output
20 '
21
22 test_expect_success "Text encoded channel-streaming command output looks good" '
@@ -39,7 +39,7 @@ test_ls_cmd() {
39 mkdir -p testdir &&
40 echo "hello test" >testdir/test.txt &&
41 ipfs add -r testdir &&
42 - curl -i "http://$API_ADDR/api/v0/refs?arg=QmTcJAn3JP8ZMAKS6WS75q8sbTyojWKbxcUHgLYGWur4Ym&stream-channels=true&encoding=json" >actual_output
42 + curl -X POST -i "http://$API_ADDR/api/v0/refs?arg=QmTcJAn3JP8ZMAKS6WS75q8sbTyojWKbxcUHgLYGWur4Ym&stream-channels=true&encoding=json" >actual_output
43 '
44
45 test_expect_success "JSON encoded channel-streaming command output looks good" '
test/sharness/t0600-issues-and-regressions-online.sh
+6 -6
@@ -11,16 +11,16 @@ test_launch_ipfs_daemon
11 # Tests go here
12
13 test_expect_success "commands command with flag flags works via HTTP API - #2301" '
14 - curl "http://$API_ADDR/api/v0/commands?flags" | grep "verbose"
14 + curl -X POST "http://$API_ADDR/api/v0/commands?flags" | grep "verbose"
15 '
16
17 test_expect_success "ipfs refs local over HTTP API returns NDJOSN not flat - #2803" '
18 echo "Hello World" | ipfs add &&
19 - curl "http://$API_ADDR/api/v0/refs/local" | grep "Ref" | grep "Err"
19 + curl -X POST "http://$API_ADDR/api/v0/refs/local" | grep "Ref" | grep "Err"
20 '
21
22 test_expect_success "args expecting stdin dont crash when not given" '
23 - curl "$API_ADDR/api/v0/bootstrap/add" > result
23 + curl -X POST "$API_ADDR/api/v0/bootstrap/add" > result
24 '
25
26 test_expect_success "no panic traces on daemon" '
@@ -28,20 +28,20 @@ test_expect_success "no panic traces on daemon" '
28 '
29
30 test_expect_success "metrics work" '
31 - curl "$API_ADDR/debug/metrics/prometheus" > pro_data &&
31 + curl -X POST "$API_ADDR/debug/metrics/prometheus" > pro_data &&
32 grep "ipfs_bs_cache_arc_hits_total" < pro_data ||
33 test_fsh cat pro_data
34 '
35
36 test_expect_success "pin add api looks right - #3753" '
37 HASH=$(echo "foo" | ipfs add -q) &&
38 - curl "http://$API_ADDR/api/v0/pin/add/$HASH" > pinadd_out &&
38 + curl -X POST "http://$API_ADDR/api/v0/pin/add/$HASH" > pinadd_out &&
39 echo "{\"Pins\":[\"QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6\"]}" > pinadd_exp &&
40 test_cmp pinadd_out pinadd_exp
41 '
42
43 test_expect_success "pin add api looks right - #3753" '
44 - curl "http://$API_ADDR/api/v0/pin/rm/$HASH" > pinrm_out &&
44 + curl -X POST "http://$API_ADDR/api/v0/pin/rm/$HASH" > pinrm_out &&
45 echo "{\"Pins\":[\"QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6\"]}" > pinrm_exp &&
46 test_cmp pinrm_out pinrm_exp
47 '