I have been thinking about this for a couple of months now (building a
similar API) trying to find the right balance between ease of use for
developers to security and privacy. I am looking for a solution that
will address multiple client needs (desktop application, AJAX script,
server-side web script), and will be easy to use in multiple
technologies.
This post assumes some people are not that familiar with all the
current protocols and tries to explain it a bit (somewhat simplified).
There are a 3 separate (but related) issues at hand: authorization,
session management, and privacy. Each of the proposed solutions should
be evaluated based on its impact on each issue.
Authorization
Validating user credentials to allow access to API resources. This can
be done very simple with basic http authentication (can be done in
HTTPS which is secure) or with an authorization provider solution
(OpenID, oauth, AuthSub, Passport, Facebook, etc). The good thing
about basic http authentication is that it's very easy to use, and
when combined with HTTPS is also secure. However, it requires
authentication on every API call, and most importantly, it requires
the client application to keep a copy of the user credentials. This is
a known problem with Twitter apps where the non-Twitter sites ask and
store user passwords to offer their services. OpenID on the other hand
(and other similar protocols) requires redirection of the client which
is not compatible with AJAX and other scripting solutions. It also
requires maintaining state information about the auth transaction
which is a hassle for most clients.
The need to prevent 3rd parties from gaining access to user passwords
is very strong. Especially since users tend to use the same password
over many sites. The author of the next big Pownce or Twitter app
might very well be someone using the app to get access to your
password. I personally will not implement an API where service
provides using my API gain access to user credentials. It is just too
unsafe. I have been playing around with using OpenID in an API wrapper
which looks very promising. The nice part is that it doesn't require
any encryption work from the client. The following requires some
knowledge of how OpenID works.
Each user will have a local username and password. I was trying to
avoid it at first but with the current state of OpenID, it might cause
problems as some OpenID Providers (OP) are unreliable and users can
end up locked out if their only access is with the one OpenID they
setup their access with. The plan is to provide an OP for local users
(so that any local user is also an OpenID), and to also allow users to
associate any other OpenID with their account. Once a user adds
another OpenID to their account, they can use that to create a session
via:
http://api.example.com/session/openid/initiate.xml?openid=some_id&ret...
http://api.example.com/session/openid/validate.xml?response-url=op_en...
For example, a USER logs into a WEBSITE which is built on top of a web
SERVICE. The SERVICE requires OpenID authentication. The USER login
request is sent via AJAX to the SERVICE with a return_to URL able to
capture the OP reply and send it back to the SERVICE. The SERVICE
reply to the AJAX code with the checkid_immediate URL. The AJAX code
calls the checkid_immediate given and captures the redirection reply.
The AJAX code calls a second SERVICE API call providing the OP reply
to the checkid_immediate request. The SERVICE validates the reply and
answers with a SERVICE session. The AJAX code the stores the session
id in a cookie and uses it for other API calls.
The same logic can be used with checkid_setup and a server side API
call. The USER is redirected by the AJAX script to the checkid_setup
URL received from the initiate API call. The user signs-in and is
redirected back to the WEBSITE client_side_capture page with the
OpenID query parameters. The WEBSITE server page client_side_capture
receives the request, calls the SERVICE validate API, and redirects
the USER back to the WEBSITE with the session cookie. In order to keep
the transaction secure, I have added another signature to the
return_to parameter. This way when the WEBSITE returns the OP reply to
the API validate call, the SERVICE can make sure that the original
return_to was created by it, and not by the WEBSITE. The normal
openid.sig signature will make sure the OP reply is valid for the
entire input. This is the an example API call and reply:
API call:
http://api.example.com/session/openid/initiate.xml?openid=eran.pip.ve...
API reply:
http://pip.verisignlabs.com/server?openid.ns=http%3A%2F%2Fopenid.net%...
Session Management
Once authenticated, most API allow using a session token (with or
without expiration) to be used in place of constant authorization.
Combined with an OpenID-like system, a user signs in and the client
app gets a token to use for user-like access. Sessions also allow
state-based API calls where an activity can be performed by a set of
API calls with continuity. The biggest concern about sessions is
hijacking where someone gets hold of the session token and can make
calls pretending to be the user. Using HTTPS throughout the protocol
will solve this problem but is usually an overkill. Typical
implementation would include some sort of public key signature on the
content of the API request together with a nonce.
Most sites use a session token that is unique and random. Tokens are
impossible to guess and are also tied on the server side to a specific
user, IP address, and expiration. Users sign-in, get the token cookie,
and each consecutive call only needs to lookup the cookie in the
database, validate the source IP address, make sure it is still valid
(did not expire) and allow access to the owner of the cookie. A single
attempt to use a token from the wrong IP address invalidates the
token. This technique is vulnerable to IP spoofing but is usually good
enough for more applications and easy very simple to implement. The
client has no extra work expect for grabbing the cookie value and
using it as a parameter in API requests.
The more complex form of session management involves signatures. The
user/client authenticate with the server and gets a session token as
well as an key. The key is received on a secure channel or is itself
encrypted with a public key - the key is only known to the client. The
client construct the API call (usually a REST URL) and then calculate
a signature using the key. The signature is appended to the API
request. When the server gets the request, it takes the signature off
the URL, and then checks if the rest of the URL matches the signature.
This proves the request came from the authorized user. To prevent
reply attacks (someone grabs the API request and use it unchanged to
make API requests), the client adds a nonce (number used once) to the
API request before signing it. The server will only honor each nonce
once from the client, so if captured by someone else, the server will
refuse the call. Some implementations void the entire session/key when
such a failed attempt occurs.
I have yet to make up my mind about using signatures for my own API.
The question is, how big of a threat is IP Spoofing, and what's the
worst case scenario if someone grabs a session. For example, if the
sessions API (calls allowed during a session) exclude changing
passwords and allows posting and reading messages, the damage someone
can do by hijacking a session is low.
Privacy
If the API call itself is confidential (the fact that a user posted a
message, or the content of the message, etc), the entire API interface
must be encoded. In this case HTTPS would be the simplest solution
available without any additional work. In the context of micro-
blogging and casual internet messaging, this is rarely needed. It
would be nice if any API call that can be made over HTTP will also be
available over HTTPS. From an implementation standpoint (service side)
this is very simple to provide (but it does come at a CPU cost). My
solution is the same, providing HTTPS access to users/clients wishing
to keep their entire session private.
Would love to hear what other people think about this, especially
about the idea of using OpenID as an API authentication platform.
Eran Hammer-Lahav
Hueniverse, LLC
http://hueniverse.com