OASIS



Cross-site request forgery defense proposal

Scott Malabarba / IBM

scott.malabarba@us.

|Version |Date |Comment |

|0.1 |03/04/10 |Initial draft |

|0.2 |03/24/10 |Corrected to reflect the use of JSONP in the spec |

| | | |

| | | |

Background

Cross-site request forgery (CSRF) is a class of attack in which malicious code executed within the browser, such as JavaScript, exploits the user's trust in the browser's security controls. There are several varieties of CSRF. In the type that concerns CMIS, the attacker tricks the victim into visiting a hostile web site while still logged in to the target web site. A malicious script then makes requests to the target site using the victim's active, authenticated session. On a CMIS repository, the attacker could corrupt or delete content and metadata.

Scope

Theoretically, a CSRF attack can target any server: GET, POST, PUT or DELETE, form-based or not. However, in reality, the attack relies on the user's trust in the browser. A browser-based application is unlikely to use the web services or Atom CMIS bindings when the browser binding is available. A standalone client application is not likely to authenticate users in a way that makes it vulnerable to CSRF.

Malicious script code using CSRF could potentially make a GET request for confidential data such as document content or metadata and the server would reply. In general, browser controls either disallow cross-site GETs completely or allow them but do not allow the script to inspect the response. Therefore, the attacker cannot breach confidentiality. In using JSON, however, the browser binding opens all GET requests to CSRF attacks as well.

There are several common defenses, of varying effectiveness, against CSRF attacks. Two of these, the secret token and custom header, require code on both client and server. It is these defenses that the browser binding specification must provide for. Other defenses, such as Referer or Domain header verification, are outside the scope of this document.

The proposal is intended to provide developers and administrators with several relatively lightweight options for securing their applications against CSRF attacks. Some actions outside of the specification will be required. For example, here is one path based on the capabilities described below:

Server requires the secret token on all GET and POST requests

JSONP-based client can make all requests across domains except for the secret token

JSONP-based client relies on a proxy service to get the secret token from its own domain

Script originating on a hostile host cannot get the secret token from either the server or client (proxy) domains

All requests from the hostile script lack a valid token and are rejected

Capabilities

The server must advertise to clients which CSRF defenses are in use. This proposal introduces a new category of capabilities:

CSRF capabilities:

capabilitySecretToken (enumCapabilitySecretToken)

Indicates that a secret token is required to authorize all form POST requests.

requiredOnPost: a secret token provided by the server must accompany all form POST requests

requiredOnAll: a secret token provided by the server must accompany all GET and POST requests

none: secret tokens are not required.

capabilityCustomHeader (enumCapabilityCustomHeader)

Indicates that a custom header, “X-Cmis-Request”, must accompany all form POST requests.

requiredOnPost: “X-Cmis-Request” is required on all form POSTs

requiredOnAll: “X-Cmis-Request” is required on all GET and POST requests

none: the header is not required

TODO: do we need more granularity?

Custom Header Defense

The custom header defense relies on the fact that XHttpRequest libraries do not allow client code to send custom headers across domains. It is not ironclad: for instance, Flash does allow scripts to send custom headers across domains. However, it can provide a supplement to the secret token defense, or be applied in cases where the secret token cannot be implemented.

The mere presence of the custom header on a request is enough for the server to verify the request's origin; the name and content of the header are irrelevant.

If the server reports the capabilityCustomHeader capability, then it SHOULD verify that every incoming form POST and/or GET request, depending on the capability value, has the “X-Cmis-Request” header set. If the server enforces this rule and finds an incoming request that is missing the header, the server MUST return HTTP status code 401 and SHOULD include a meaningful error message in the response body.

If the server reports the capabilityCustomHeader capability, then a client MUST send a custom header “X-Cmis-Request” along with all POST and/or GET requests, as indicated by the capability value.

Note that plain HTML forms and GET requests triggered by tags cannot send custom headers, whether they originate in the same domain as the server or not.

Implementation hints

The custom header provides a relatively strong and lightweight defense against CSRF attacks. However, it also blocks a legitimate client use case for the browser binding: use of JSONP to access the CMIS server across domains. Therefore, server developers might wish to make the requirement configurable so that the administrators of a given server deployment can balancing the level of protection they enforce against accessibility to client applications.

Secret Token Defense

Servers can require that every sensitive request be accompanied by a parameter containing a secret token that is only known to the client application. In an application based on server-generated HTML pages, the server can insert the secret key as a hidden field directly into the form. In a purely browser-based application, however, the forms are generated in JavaScript. Therefore, the client must make a separate call, prior to generating the form, to get a secret key value (as noted earlier, a hostile script might be able to make a GET call to get the token, but will not be able to inspect the response and use the value). The secret token response cannot be in JSON, or any valid JavaScript syntax at all, or it will be subject to CSRF attacks. Therefore, JSONP cannot be used by a legitimate client to get around the same-domain policy and retrieve the token. Some other method, such as proxying the token request through the client's source host, must be used.

Secret token service

This proposal introduces a new repository service that provides the secret token to clients:

|Repository Service |getSecretToken |GET | |secretToken | |

Inputs

None

Outputs

A plain text representation of the secret token, its expiration date and time, and a boolean indicating whether or not the token is a nonce (meaning it can be used once and only once).

Exceptions Thrown & Conditions

See section “General Exceptions”.

Example

GET /cmis/repository/123?selector=secretToken

HTTP/1.1

Host:

User-Agent: Mozilla/5.0

HTTP/1.1 200 OK

Content-Type: application/json

Content-Length: xxxx

cmisSecretToken=1dcd6af0-624b-469d-9735-da8679fd812b

cmisTokenExpiration=2011-03-08T08:43:36.343Z

cmisIsNonce=false

TODO: verify that the sample above cannot be parsed via tag

Post data

If the server reports the capabilitySecretToken capability (below), then in every POST and/or GET, depending on the capability value, the client MUST include a control named “cmissecrettoken” that contains the secret token retrieved from the server.

Examples:

GET /cmis/repository/123?selector=&cmissecrettoken=1dcd6af0-624b-469d-9735-da679fd812b

If the server reports the capabilitySecretToken capability, then for every GET and/OR POST, depending on the capability value, it SHOULD validate the “cmissecrettoken” control value. If the value is missing, invalid or expired, then the server MUST return an HTTP status 401 and SHOULD include a meaningful error message in the response body.

If the server does not report the capabilitySecretToken capability, then it MUST allow requests that do not include the “cmissecrettoken” control.

Implementation hints

Implementation details of the secret token defense are also beyond the scope of this proposal. How the token is generated and verified is up to the server; the specification need only state how it is transmitted between client and server.

If not implemented correctly, the secret token method does not provide strong defense. For instance, if the token is appended to GET URLs in a feed or HTML page it might leak to a malicious script. If the token is generated by using freely available information then an attacker can forge it.

A straightforward implementation generates a nonce, such as a UUID, and stores it in the user's session for later validation. An alternative that does not require session state is to generate the token as the HMAC encryption of the session ID. The token does not require session state, is bound to the user's current session, and cannot be forged by any entity that does not have the shared HMAC key.

The expiration time is intended to provide some flexibility to servers that cannot rely on sessions or session IDs. The server can generate a token bound to some other identifier, such as the client IP, and expire it quickly or treat it as a true nonce (usable only once).

The secret token service, if implemented according to spec (no JSON), cannot be accessed using JSONP. If the client and server are in the same domain then it is straightforward to get the token in JavaScript. If not, some other workaround for the same-domain policy must be used. For instance, a lightweight proxy on the client host for the token service.

If secret tokens are required on GET requests then the server must take care to implement the method properly. The token should not be included in URLs embedded in results sent back from the server, since it could then leak to any malicious scripts that are also loaded in the browser.

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download