Skip to content

API Tokens #1532

@jbenet

Description

@jbenet

(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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions