Skip to content
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

API Tokens #1532

Closed
jbenet opened this issue Jul 29, 2015 · 16 comments · Fixed by #10218
Closed

API Tokens #1532

jbenet opened this issue Jul 29, 2015 · 16 comments · Fixed by #10218
Labels
need/community-input Needs input from the wider community topic/security Topic security

Comments

@jbenet
Copy link
Member

jbenet commented Jul 29, 2015

(from discussion with @mappum and @diasdavid)

Current State and Stopgaps

In the current state, the web API is protected by:

  • API binding to 127.0.0.1.
  • CORS checks.
  • a Referrer check.

CSRF attacks

For further explanations below, consider a CSRF attack where the user crafts any html page such as:

<html><body>
<img src="http://127.0.0.1:5001/api/v0/pin/rm?arg=<precious-file>" />
</body></html>

getting the user to click on the link attempt to load the "image", issuing a HTTP request to the API. Such maliciously crafted links are NOT stopped by the API binding only to 127.0.0.1, as the request would be coming from the user's browser on the same machine, and would work. They ARE stopped by the Referrer check.

Some more convoluted CSRF attacks may exist, so we need to move to "The Right Solution" below, with api tokens (caps). But until then, we must not open security holes.

Need to relax Referrer check

In the leadup to #1529, users encountered problems developing webapps for use with the ipfs api. Users/developers requested removing the Referrer check. The Referrer check is not perfect, but it is more secure than without it. CORS is not enough to prevent the mentioned CSRF attacks.

We decided that

  • we should move to build the api tokens solution
  • in the meantime, Fix CORS Support #1529 will not remove the Referrer check, but will just relax it to follow the CORS Access-Control-Allow-Origin header (set by the user).

That way, developers can easily get the access they need

# grant API access to http://localhost:1234
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://localhost:1234"]'

and still remain protected.

The importance of NOT setting "*"

Until we follow the right solution below, the API's security is not as good as it can be. Setting Access-Control-Allow-Origin: * opens a big security hole:

  • Exposes user to CSRF attacks as mentioned above.
  • IF the API is exposed (socket bound to 0.0.0.0 -- NOT by default) any host who could issue a malicious HTTP request.

The Right Solution -- API Tokens

The correct solution would allow:

  • granular permissions per-token (and thus per-application)
  • allow users (developers and end users) to select permissions
  • use capabilities, not user accounts.

One (relatively) easy way to do this is with a permissions + signed capabilities.

Permissions

First, suppose we have a simple language for expressing permissions. Bear with me, the language specifying the permisions could be very different. In particular, i'm sure there are already very good languages out there we can use. But this gets the point across.

[ 
  "pin add [-r] *", // can add direct or recursive pins to any files
  "cat /ipfs/QmQv4YQNmRPuTTHs4AgBhKEFDdN7eQYeTbSmr8JVWVfury", // cat files under given path
  "swarm peers", // can see the listing of connected peers
]

The idea is to be able to scope specifically what commands the capability grants access to (e.g. can add pins, but not remove them), and even give parameter constraints (e.g. can cat any file under a given root).

Permissions as a Signed Capability

Take the permissions, and create a "Signed Merkledag Object" (see elsewhere for this).

# got some permissions
> cat permissions.json
{ 
  "@type": "<capability-identifier>", 
  "permissions": [ "pin add [-r] *", "cat", "id" ],
}

# add it as an ipfs object/dagnode
> ipfs object add <permissions.json
QmehLm4DyLAi3SUudzC9qZRq8v2x67Afa7v7vt6w4Ps3ZL

# sign the object/dagnode (creates another object/dagnode)
> ipfs key sign QmehLm4DyLAi3SUudzC9qZRq8v2x67Afa7v7vt6w4Ps3ZL
QmQTkWSPct3bypTrqaNkY7BMMWHcETaLZGWdur1AbYgZYz 

# show the signature object/dagnode
> ipfs object get QmQTkWSPct3bypTrqaNkY7BMMWHcETaLZGWdur1AbYgZYz
{
  "@context": "/ipfs/<signing-context>"
  "type": "/ipfs/<signing-context>#Signature"
  "key": "<multihash-of-the-signing-key-(the-peer-id)>"
  "object": "QmehLm4DyLAi3SUudzC9qZRq8v2x67Afa7v7vt6w4Ps3ZL",
  "signature": "<signature-byes>"
}

# the hash is the capability
> my-program-that-uses-api --api-token=QmQTkWSPct3bypTrqaNkY7BMMWHcETaLZGWdur1AbYgZYz cat <foo-hash>
<foo-contents>

This is a simple expression of what to do. it could be done anywhere with access to the node: command line, programmatically, and even in a special webui webapp that has the capability of creating capabilities. For example, can have a page with checkboxes that select the permissions + with a big "Sign" button, that dumps out the capability to a field. (will prototype this).

Attention

if you want to help us implement the above o/ ping me, as it will be really awesome and useful for other applications/programs beyond ipfs. (to be continued)

@krl
Copy link
Contributor

krl commented Jul 29, 2015

it's so cool what falls out of these schemas 👍

jbenet added a commit that referenced this issue Jul 29, 2015
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>
jbenet added a commit that referenced this issue Jul 29, 2015
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>
@krl
Copy link
Contributor

krl commented Jul 29, 2015

One thing though, how about revoking capabilites?

@jbenet
Copy link
Member Author

jbenet commented Jul 29, 2015

@krl yeah, i thought the same thing. we can just make these records, like ipns/providers/..., and reuse the record validity stuff from https://github.com/ipfs/specs/tree/master/records ;)

@whyrusleeping whyrusleeping added the need/community-input Needs input from the wider community label Aug 13, 2015
kbala444 pushed a commit to kbala444/go-ipfs that referenced this issue Aug 15, 2015
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 ipfs#1532

License: MIT
Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
@Kubuxu
Copy link
Member

Kubuxu commented Dec 26, 2015

Doesn't this solution have same problem as storing private data in IPFS? Couldn't the hash accidentally be leaked?

It could be solved by additional token in the to be signed part, which would be hashed before storing. So hash of token is stored and then it could be used to confirm that it is true owner of the token.

I would like to implement it but it looks like it is blocked by IPLD.

@Stebalien
Copy link
Member

Instead of having a permission allowing applications to pin/unpin globally, it would be nice to make pins per-application and have quotas. That is, the permission would be:

{
  quota: "5MB" // or "infinite"
}

And each pin would list the applications that have requested that the object be pinned. When this list is empty, the object would be eligible for garbage collection.

Rational:

  1. Users don't really care about what applications pin (assuming they can control what applications can access), only how much disk space each application uses.
  2. This keeps applications from interfering with each other.

@Stebalien
Copy link
Member

Some Notes:

  • IMO, some security critical parts of the API (upgrading, mounting, changing the config, etc) should only be exposed over a unix domain socket when possible. This splits the API but makes some attacks harder.
  • It may be convenient to allow capability delegation.
  • I'm not so sure about the proposed permissions signing design. For one, it doesn't provide a way to revoke permissions. It also assumes that IPFS multihashes are private; IIRC, they are broadcast in the wantlist.

@song007
Copy link

song007 commented Nov 6, 2016

hi .. I wonder if the capacity token idea and its enhancement has been implemented or started to be implemented? thanks!

@Kubuxu
Copy link
Member

Kubuxu commented Nov 7, 2016

There is no implementation in progress, the first thing we need is a spec on how it would work. See issues that references this issue above.

@notslang
Copy link
Contributor

notslang commented Jan 5, 2017

Auth tokens shouldn't be a part of IPFS. It assumes that the IPFS HTTP API is being exposed to the whole internet, rather than just being linked to another container, for which authentication isn't needed and would be pointless overhead.

Users should just put an authentication proxy in front of their HTTP API and use whatever auth they like... For just playing around with IPFS (when you don't care if files get added/removed from your cache), it might be fine to leave unauthenticated. If you're exposing it outside your local network, you should use something like coreos/jwtproxy. If you wanted to use IPFS to build a Dropbox-like storage API for arbitrary applications, then you could build a permission-oriented proxy that sits in front of the IPFS instance and restricts access to resources on a per-URL basis. However, none of this needs to be included in the IPFS core.

@song007
Copy link

song007 commented Jan 6, 2017

As a follow up question .. any suggestion on authentication for limiting the membership of the IPFS nodes?

@notslang
Copy link
Contributor

notslang commented Jan 6, 2017

@song007 - Do you mean current methods for restricting access to the HTTP API? You could put it behind an nginx reverse-proxy and add whatever sort of auth you want right now... Even block off specific URLs for fine-grain control over permissions.

keks pushed a commit to ipfs/go-ipfs-cmds that referenced this issue Jan 28, 2017
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 ipfs/kubo#1532

License: MIT
Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
@ghost
Copy link

ghost commented May 1, 2019

<img src="http://127.0.0.1:5001/api/v0/pin/rm?arg=<precious-file>" />

I know this is a fairly old issue. Is this still how the API is? POST, DELETE, PUT, etc, should be used for modifying requests. Not GET. Would be another way to improve this as well.

@Kubuxu
Copy link
Member

Kubuxu commented May 2, 2019

@teran-mckinney this won't work because of CORS.

@kevincox
Copy link

Please elaborate why it won't work because of CORS? CORS will prevent most requests without authorization. There are some holes for compatibility, GET and POST with basic MIME types. The API should be updated to avoid these types for mutation requests and everything should work fine without the referrer check. Having the API use GET for mutations is dangerous and wrong.

@Stebalien
Copy link
Member

As of 0.5.0, we've switched to POST and will be removing the referer check.

@lidel
Copy link
Member

lidel commented Nov 23, 2021

Just a quick note that when we pick this up, we should see if we can use a version of UCAN.
Not because it is very useful for backend API like this, but to dogfood it for the wider ecosystem, improve libraries etc.

hacdias pushed a commit to ipfs/boxo that referenced this issue Jan 27, 2023
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 ipfs/kubo#1532

License: MIT
Signed-off-by: Juan Batiz-Benet <juan@benet.ai>


This commit was moved from ipfs/kubo@d5f94be
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
need/community-input Needs input from the wider community topic/security Topic security
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants