-
Notifications
You must be signed in to change notification settings - Fork 37.8k
fix: add support for CORS headers and pre-flight request #12040
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
064976b
to
b7c23f7
Compare
Hmm, I mean in general its probably a terrible idea to be calling bitcoind's RPC from a browser directly...I would probably be happy to see this if we force the setting of an -alloworigin=$ORIGIN option (which implies IP whitelist as well) to force users to use it at least somewhat securely. |
There's plenty of good reason to enable RPC from browser, but I agree we have to make it opt-in. Will add that to the patch. |
Things like this are not sufficient to make access from browsers safe, because we must also be safe even if the user has old browsers with CORS bugs on their system. (e.g. IIRC old IE was trivially bypassable). As a result there would have to be a lot of developer resources available to implement, review, and characterize the full set of protections needed to prevent that from being a gaping vulnerability-- and a commensurate really good application for it. |
3b490cb
to
07fe036
Compare
@gmaxwell Done: I've added the flag I've also added tests for that flag. As for your question: we have an internal website that we use to sign transactions with hardware wallets. We use RPC to send out the signed transactions. We'd prefer to do this without a service (which needs audits, security updates, etc..) |
Needs rebase. I'm generally a fan of correct CORS / CSP headers, will review later. |
POST works (assuming
But OPTIONS doesn't:
That was using Postman which doesn't set the origin header, but I get the same error from a browser for the OPTIONS request. |
@Sjors Just using
|
07fe036
to
25f1000
Compare
Same error unfortunately. Incorrect OPTIONS request should probably throw a unique error regardless. I tried your full example, but since the JSON payload itself shouldn't be needed in an OPTIONS request, I think it can be shortened a bit:
|
@Sjors Did you provide the |
Yes, I used Better error messages would be very useful here, either through the browser or This worked:
I added If anyone else wants to test a POST request:
Nice to have: support for multiple |
Any malicious website would need to trick the user into handing out RPC authentication details, as well as setting For HTTP Basic Auth they need to trick the user into configuring I think it would be particularly useful for data visualization. E.g. a mempool chart is much easier to build using client-side javascript that just fetches the data through ajax. Although potentially very useful for hardware wallets, facilitating wallet access from a browser potentially opens a can of worms. For a non-wallet node I don't see much downside. Perhaps a compromise (for now) could be to only allow this flag when built with @gmaxwell regarding old browsers, I think we should work under the assumption that CORS is broken and any request can be made. Afaik enabling CORS is just a way to allow modern browsers to use the RPC (they require the |
@Sjors The default is still CORS disabled. Would you still want it to only be available with |
Needs rebase if still relevant |
25f1000
to
5f60c1b
Compare
@MarcoFalke @Sjors Rebased. |
any news on that one? is it going to be merged anytime soon? |
5f60c1b
to
8d848da
Compare
8d848da
to
53e054a
Compare
Rebased, if anybody still cared. This patch got merged in Bitcoin ABC which was what I cared about ;) |
@lionello that was quick, thank you. |
This seems like a nice change, and I'd really curious to know what some use-cases are. (Lacking web background, or enough of an imagination myself...) Maybe something about this could also be added to the documentation https://github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md |
@ryanofsky in my case, I'm considering to use in |
@ryanofsky The scenarios are having a website interact with the blockchain node directly, without the need for a backend: browser --rpc--> bitcoind. You can do transaction signing in the browser (JS libs, or HW device) and send transactions directly. |
53e054a
to
20d3c18
Compare
20d3c18
to
210b8ee
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tACK 210b8ee (with a preference for renaming the variables)
Recipe to test:
- launch REST server at port 8080:
src/bitcoind -server -rest=1 -rpcport=8080 -rpcuser=bitcoin -rpcpassword=bitcoin -rpccorsdomain=http://localhost:8080
- pretend to be a browser visiting a website on port 8085 which calls the REST server:
curl 'http://localhost:8080/' -H 'Authorization: Basic Yml0Y29pbjpiaXRjb2lu=' -H 'Origin: http://localhost:8080' -H 'Content-Type: application/json' -H 'Accept: application/json' --data-binary '{"method":"getblockchaininfo"}'
- do some wallet operation on wallet "A":
curl 'http://localhost:8080/' -H 'Authorization: Basic Yml0Y29pbjpiaXRjb2lu=' -H 'Origin: http://localhost:8080/wallet/A' -H 'Content-Type: application/json' -H 'Accept: application/json' --data-binary '{"method":"getwalletinfo"}'
You can also dynamically load any wallet on the nodes filesystem, so indeed disablewallet=1
is probably a good idea.
There's no way to configure this by accident and for good measure most users are behind a NAT. I think a warning in the release notes should suffice, i.e. to use disablewallet=1
, to never expose a machine with a wallet to the internet, and not to share RPC credentials in general.
Longer term I think we should add some sort of user-permission system, which is also quite useful in a Lightning node context (e.g. a connected Lightning node should be able to get information, request an address to refund on, etc, but not steal coins off the node).
@@ -66,6 +66,8 @@ class HTTPRPCTimerInterface : public RPCTimerInterface | |||
static std::string strRPCUserColonPass; | |||
/* Stored RPC timer interface (for unregistration) */ | |||
static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface; | |||
/* RPC CORS Domain, allowed Origin */ | |||
static std::string strRPCCORSDomain; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this str_rest_cors_domain
(new naming convention) and the parameter -restcorsdomain
(since it's only used with -rest=1
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other options still use the old -rpc...
naming though, so I'm not convinced this helps anyone. Even the variables declared right before strRPCCORSDomain
use the old naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the use of -rpc
confusing when a setting is about the REST interface. -rpcauth
, -rpcuser
and -rpcpassword
is shared between the regular RPC and REST so there it makes sense (although I'm not a fan of that sharing). The rest is just internal variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
REST doesn't have authentication, I thought?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sipa it does, see my command-line example above. It has full wallet access too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sjors I'm very confused. The REST interface only exposes public information (blocks, transactions with txindex, headers, chaininfo, utxoset, mempool), and should never require authentication. Does this PR change that? Your example seems to just use the RPC interface.
Actually NAT doesn't help, because an attacker could tell a user to run |
I missed how much RPC and REST are intertwined; it's one and the same server, not a separate one as I assumed. So in the examples above I'm just making normal (authenticated) JSON-RPC requests. Let's try that again:
The REST interace doesn't expose anything private. So all you need to change is the only add CORS headers to |
After brief IRC discussion today I'm switching to light Concept NACK. The REST API wasn't intended for this use case. It's meant as a more performant alternative to the JSON-RPC (using I think it's better to recommend users who really want this to run nginx and have that add the headers, as well as shield URLs that are not part of |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
Going to close this. A couple of (implied?) NACKs, and a reversal from a utACK to NACK, no further discussion for 8 months. |
This PR implements basic Cross-Origin Resource Sharing (CORS) support to the RPC server, as per the spec at https://www.w3.org/TR/cors/#resource-requests . The spec has been quoted verbatim in the source code for easier validation and maintenance of the code.
In practice this PR allows the REST interface to be used directly from a browser.
All the existing restrictions to the REST interface still apply: IP subnet, port, username, password. For this reason this PR doesn't explicitly check the request's
Origin
with a whitelist.