Show TOC

XSRF Protection for REST ServicesLocate this document in the navigation structure

Definition

Cross-Site Request Forgery (CXRF/ XSRF) is an attack that tricks a victim's browser into sending a request to a vulnerable Web application, which then performs an undesired action on behalf of the victim (for example, changing credentials, making an illegal purchase, or performing online financial operation). This vulnerability is extremely widespread since the majority of today's Web applications rely solely on automatically submitted credentials such as session cookies, basic authentication credentials, source IP addresses, Secure Socket Layer (SSL) certificates, or Microsoft Windows domain credentials. Therefore, as the user is currently authenticated to the site, the site will have no way to distinguish the XSRF attack from a legitimate user request.

Concept

To protect against attacks of this type, the Web application developer must configure a servlet filter in the filter chain of the application. The filter is specified in an application's web.xml file inside the <filter> element, along with a corresponding <filter-mapping> tag that maps the filter to a request pattern.

The filter provides access to the resource when a modifying HTTP method is used and an XSRF token is received with the same value as the token stored in the current session. To obtain the XSRF token, the client has to use a non-modifying HTTP method containing header X-CSRF-Token with the value Fetch . The token is issued only if the user has already been authenticated. If the user has not been authenticated , any request with a modifying method is rejected by this filter. In addition, the client also has to include all previously received cookies in the modifying requests.

Note

If two applications are in the same security policy domain, authenticating with the first application allows the user to access the second one without additional authentication. Also, the received XSRF token is valid for all applications across the security policy domain.

To configure the filter, you have to add the name and the class of this filter to the web.xml file:

            
...
<filter>
        <filter-name>{name_of_filter}</filter-name>
        <filter-class>com.sap.xsrf.rest.filter.XsrfRestFilter</filter-class>
</filter>
<filter-mapping>
        <filter-name>{name_of_filter}</filter-name>
        <url-pattern>{protected_path}</url-pattern>
</filter-mapping>
...
<servlet>
        <servlet-name>{name_of_servlet}</servlet-name>
        <servlet-class>{class_of_servlet}</servlet-class>
</servlet>
<servlet-mapping>
        <servlet-name>{name_of_servlet}</servlet-name>
        <url-pattern>{protected_path}</url-pattern>
</servlet-mapping>

         

By default, the filter accepts the following HTTP methods:

  • Non-modifying methods:

    • GET

    • HEAD

    • OPTIONS

  • Modifying methods

    All methods that are not specified as non-modifying are considered modifying.

You can customize the methods in the web.xml file. The optional initializing parameter for this filter is NonModifyingMethods .

For example, to specify "GET", "HEAD", "OPTIONS", and "CUSTOM-METHOD" as non-modifying methods:

            
<filter>
        <filter-name>{name_of_filter}</filter-name>
        <filter-class>com.sap.xsrf.rest.filter.XsrfRestFilter</filter-class>
        <init-param>
                <param-name>NonModifyingMethods</param-name>
                <param-value>GET,HEAD,OPTIONS,CUSTOM-METHOD</param-value>
        </init-param>
</filter>

         
Constraints
  • Modifying operations for services that do not require authentication are not supported by the filter.

  • The XSRF token must be obtained and used in the same security session.

Example

The following is a sample program that uses the web application:

                      HttpURLConnection readConn = null;
          HttpURLConnection writeConn = null;
          List<String> session = null;

          readConn = (HttpURLConnection) protected_resource_URL.openConnection();
          readConn.setRequestMethod("GET");
          readConn.setRequestProperty("Authorization", …);
          readConn.setRequestProperty("X-CSRF-Token", "Fetch");
          readConn.connect();

          session = getSessionCookies(readConn); // get the value of response header "set-cookie"
          String xsrfToken = extractXsrfToken(readConn);

          writeConn = (HttpURLConnection) protectedResourceURL.openConnection();
          writeConn.setRequestProperty("X-CSRF-Token", xsrfToken);
          writeConn.setRequestMethod("PUT");
          setSessionCookies(writeConn, session); // set request header "cookie"
          writeConn.connect();

          body = readResponseBody(writeConn);
          // process the response …

...
private static final List<String> getSessionCookies(HttpURLConnection conn) {
Map<String, List<String>> response_headers = conn.getHeaderFields();
Iterator<String> keys = response_headers.keySet().iterator();
String key;
    while (keys.hasNext()) {
      key = keys.next();
      if ("set-cookie".equalsIgnoreCase(key)) {
        List<String> session = response_headers.get(key);
        return session;
      }
    }

    // no session
    return null;
  }

private static final void setSessionCookies(HttpURLConnection conn, List<String> session) {
    if (session != null) {
      String agregated_cookies = "";
      for (String cookie: session) {
        agregated_cookies += cookie + "; ";
      }
      conn.setRequestProperty("cookie", agregated_cookies);
    }
}

private String extractXrsfToken(HttpURLConnection conn) {
List<String> value = null;
Map<String, List<String>> headers = conn.getHeaderFields();
Iterator<String> keys = headers.keySet().iterator();
    while (keys.hasNext()) {
      String key = keys.next();
      if ("X-CSRF-Token".equalsIgnoreCase(key)) {
        value = headers.get(key);
      }
    }

    if (value == null || value.size() == 0) {
      return null;
    } else {
      return value.get(0);
    }
}