Concept - Cross-Site Request Forgery Guard

1 Introduction

The cross-site request forgery guard closes Intershop's vulnerability to cross-site request forgery attacks.

1.1 Glossary

Cross site request forgery (CSRF)

A common vulnerability of web applications that use cookies for storing login states.

Synchronizer token

A security token that is embedded into a web applications response and expected to be sent back by a browser upon subsequent requests.

1.2 References

2 Cross-Site Request Forgery Attacks

A cross-site request forgery attack (CSRF for short, pronounced like sea-surf) is an attack which "forces an end user to execute unwanted actions on a web application in which he/she is currently authenticated." (Definition from OWASP)

The prerequisites for the attack are:

  • A user's identity and authentication state (like the session ID) are entirely determined by browser cookies.
  • An authorized user may perform actions with side effects, without being required to authorize the specific action (like by re-entering his password).
  • An attacker can build or guess valid URLs and all required form inputs for actions to be performed by an authorized user (like the ID of a resource to be manipulated).

The attack is run like this:

CSRFAttackScheme

1. A user authenticates at a login page. The session ID and authentication state are stored as cookies by his/her browser.
2. An attacker forges a URL and form parameters, if required, that has malicious side effects when requested by the authorized user.
3. The attacker sends the forged URL to the user via e-mail/chat/social media or on a website prepared by the attacker, making sure that the user is forced or tricked into sending a request to this URL.

In order to achieve this, the URL might be specified as source of an embedded image/ related stylesheet (even if the URL does not point to a valid image/stylesheet). Other means include presenting a fraudulent form to the user whose submit button is fixed to lead to the forged URL or using JavaScript to forge any kind of single request or even request pattern (including AJAX).
4. The authorized user opens the e-mail/chat message or website and either his/her browser automatically issues the request or the user is tricked into triggering it. The browser includes session and authorization cookies into the request.
5. The request passes all authentication/authorization checks in the web application and proceeds to perform the malicious action.

Note

Please note that the attack is blind, i.e., the attacker will not receive the web application response page – given no other security holes are exploited.

2.1 Severity

"According to the United States Department Of Homeland Security the most dangerous CSRF vulnerability ranks in at the 909th most dangerous software bug ever found, making this vulnerability more dangerous than most buffer overflows." (Quote from Wikipedia )

Also, it is considered number 5 of the Top 10 Web Application Security Risks for 2010 by the Open Web Application Security Project (OWASP).

2.2 Applicability to Intershop

In most configurations, Intershop fulfills all mentioned prerequisites and is vulnerable to this attack without taking counter measures.

It is not vulnerable if the session ID is configured to be a required part of every request's URL. (This can be achieved by setting session.tracking.mode = URLOnly in the webadapter.properties.) However, besides usability problems, this increases vulnerability to other attacks, namely session hijacking.

2.3 Counter Measures

In order to counter a CSRF attack, the strategy proven most effective OWASP Prevention Cheat Sheet is the synchronizer token pattern. In the absence of an attack it works as follows:

  1. Presented with a request from an authorized user, the web application generates a cryptographically strong pseudorandom number and stores it at the user's session. This number is called the synchronizer token.
  2. The synchronizer token is embedded into every part of a response page that can trigger actions with side effects. Note that the synchronizer token is neither sent nor stored as a browser cookie.
  3. The authorized user triggers one of these requests from the page.
  4. The web application receives the request and validates that the synchronizer token embedded in the request matches one stored at the user's session and performs the requested action.

An attacker trying to run a CSRF attack on this web application will now fail because he cannot retrieve or guess the synchronizer token and the web application will thus reject to perform any forged requests.

Note that this countermeasure (like most others) is superseded by vulnerabilities to other kinds of attack, like cross-site scripting (XSS) and session fixation or session hijacking, or man-in-the-middle attacks (caused by unsecured transport).

3 Cross-Site Request Forgery Guard

The cross-site request forgery guard ("CSRFGuard" for short) is an implementation of the synchronizer token pattern, especially tailored for Intershop applications. It is not the equally named CSRFGuard by OWASP.

3.1 POST Only, Single-Instance Synchronizer Token

While different variations of the synchronizer token pattern exist and are in operation at various major websites, Intershop's CSRFGuard utilizes a POST only, single-instance synchronizer token.

At first, it is single-instance because a user session will get assigned a synchronizer token upon creation and use it for all responses until the session expires.

In contrast, other implementations generate a new synchronizer token for each response or each possible action that can be triggered from a response page which is only valid for this specific action. While this theoretically raises the barrier against CSRF attacks even higher, it requires storing multiple synchronizer tokens per session and associating them with actions. Since the number of valid synchronizer tokens has to be bounded, this can lead to usability issues: when a user tries to use a form from a response page that was generated non-recently and therefore its synchronizer token has expired, the request will be regarded as forged, will be rejected and trigger a false alert. On the other hand, a single synchronizer token per session provides an effective protection against CSRF.

The second special feature of Intershop's CSRFGuard is that it makes a distinction between requests that require CSRF protection and those that do not. For a request that does not require CSRF protection, the synchronizer token is not validated. This is necessary to avoid including the synchronizer token in the URL of a request, as this would result in two major flaws:

  • URLs that include a synchronizer token might affect usability, as they expire with the token. Bookmarking, sharing, or revisiting URLs from a browser history might not work as expected.
  • Including the synchronizer tokens in the URL might render this CSRF counter measure ineffective as URLs are more inconsiderately shared and tracked by users and technical systems than any other request parameters. Re-consider bookmarks, browser history – which might be exposed to cross-site scripting – or HTTP proxies.

For these reasons, CSRFGuard only reads synchronizer tokens from HTTP POST parameters and a custom HTTP header.

3.2 Safe HTTP Methods

As a result, there is a close correlation between HTTP methods and the question of whether they may cause side effects or not. The HTTP specification, for similar reasons, establishes the concept of "safe" methods RFC 2616 :

Implementors should be aware that the software represents the user in their interactions over the Internet, and should be careful to allow the user to be aware of any actions they might take which may have an unexpected significance to themselves or others.

In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe". This allows user agents to represent other methods, such as POST, PUT and DELETE, in a special way, so that the user is made aware of the fact that a possibly unsafe action is being requested.

Please note that there are a few side effects that are allowed and unavoidable to be triggered by GET requests, like storing a list of recently viewed products, collecting statistical data or more intricate patterns, like lazily evaluating some calculation and caching its result. In accordance to the above notion, these exceptional side effects are called "safe".

Note

A GET request is not allowed to have any unsafe side effects. In order to perform unsafe side effects, an HTML form/an HTTP POST request has to be implemented.

3.2.1 Detecting (Unsafe) State Changes

CSRFGuard now needs a notion to determine whether a request handler either

  • manipulates persistent states, or
  • only retrieves persistent states to produce a response.

Hitherto there is no such distinction available. Pipeline start nodes have a call mode that states whether they are public or not, but not if they are reachable by HTTP GET or only by HTTP POST requests. Therefore, a mechanism is introduced to dynamically detect state changes. Only if such a state change occurs, the synchronizer token from the request will be validated against the one stored at the user session. If no state changes occur, no check will be performed.

A StateChangeSafetyMgr is introduced whose purpose is to collect all occurring state changes and inform StateChangeGuards about them. The StateChangeSafetyMgr is implicitly context-aware. It will check from which context its methods are called (like which thread or which request). If needed, it will then determine which StateChangeGuard is responsible for this context and ask it to check whether a state change is permitted or not.

The CrossSiteRequestForgeryGuard is a special StateChangeGuard that guards a single Request object. When asked to check whether a state change is permitted, it will validate the synchronizer token from the request. If the token is missing or invalid, it will throw a CSRFAlertException.

StateChangeSafety

StateChangeSafetyMgr cannot fully detect changes in persistent state on its own, so it has to be informed by other parts of the system before a state change occurs. For this it comes with three methods:

checkStateChangeAllowed

This method has to be called whenever a state change is about to occur. It will throw a SecurityException if the StateChangeGuard for the callers context does not permit state changes.

startSafeChangesBlock

Opens a safe changes block in the current thread. All calls to checkStateChangeAllowed inside a safe changes block will pass without exception.

endSafeChangesBlock

Closes the current safe changes block, thus neutralizing the last call to startSafeChangesBlock.

State changes inside a safe changes block are also called safe state changes. These are changes that are safe to execute even without a guard checking for permission, like the side effects that can still occur in GET requests. State changes outside safe changes blocks (they are the ordinary kind) are called "unsafe".

By using these three methods you may always override whether a change should be considered safe or not:

  • To render a call to checkStateChangeAllowed ineffective, enclose it in startSafeChangesBlock and endSafeChangesBlock.
  • To contradict a safe changes block, add a call to checkStateChangeAllowed in front of it.

SafeBlocks

Note

Please note that startSafeChangesBlock and endSafeChangesBlock always have to come in pairs. Especially, it is illegal to use a startSafeChangesBlock call without a following endSafeChangesBlock, as this would disable guarding for the rest of the thread, no matter what else might occur. They may however be nested. The level of nesting has no influence on what happens to checkStateChangeAllowed calls inside the safe changes blocks: they will always pass.

Also note that none of the methods has parameters. Note especially that checkStateChangeAllowed receives no input about the kind of state changes (like which persistent object is manipulated), either all changes of persistent state are currently allowed in a context or none.

To ease the usage of these methods, there are several ways to call them, from Java code, inside a pipeline or for each execution of a pipelet. See the Cookbook for details.

Besides the safe changes blocks above, CSRFGuard has a switch for enabling/disabling it on per request basis. For compatibility with legacy code it is disabled by default for custom Intershop applications. (It is shipped enabled for standard Intershop applications, like the back office and the demo storefront.) See the Cookbook on how to enable it.

To sum up, CSRFGuard will issue a CSRF alert and reject a request if and only if:

  1. CSRFGuard has been enabled for this request.
  2. A call to checkStateChangeAllowed has occurred outside safe changes blocks.
  3. The request does not contain a synchronizer token inside its POST parameters nor as a custom HTTP header, or the contained synchronizer token
    does not match the synchronizer token stored at the user session.

3.2.2 Transaction-Based Heuristic/ Default Checks

StateChangeSafetyMgr comes with a heuristic that tries to detect a state change in a generic way: checkStateChangeAllowed will be called whenever a transaction is opened, like by explicitly opening a database transaction in a pipeline, or implicitly by using a pipelet marked with the "transaction required flag" (capital T in Intershop Studio). A database transaction will imply unsafe state changes in most cases. For all others it can be marked a safe change by using safe changes blocks.

Also, there are a few other code artifacts that call checkStateChangeAllowed.

  • The pipelet for handling file uploads: ProcessMultipartRequest( bc_foundation).
  • Standard Enfinity pipelets that generically apply changes to the file system, or contents of files: CopyFile, DeleteFile and ExtractCompressedFile(bc_foundation).
  • All standard Enfinity pipelines that create/manage batch processes/scheduled jobs.

For batch processes/jobs the check is performed in the pipeline that triggers the job creation/scheduling – as every asynchronously running pipeline is called only for the sake of its state changes and all are considered unsafe. Inside an asynchronously running pipeline, all calls to checkStateChangeAllowed will pass.

Unfortunately, this heuristic is not perfect for detecting all state changes and deciding whether they are unsafe or safe. The following cases require further refinement:

3.2.3 False Alert

A CSRFAttackAlertException is thrown but no attack was run. This is called a "false alert". With automatic token injection (see below) this will occur mostly when a state change is detected for an HTTP GET request. This can result in rendering a web application non-functional since the requested action is not performed and only a general error response will be delivered. See the Cookbook on how to cope with this.

3.2.4 Missing Alert

An unsafe state change occurs but it is not detected and CSRFGuard does not validate the synchronizer token. This is called a "missing alert". This results in a security hole in the application, where it is not guarded against CSRF. Since this – other than false alerts – does not show up in logging or simple testing, care has to be taken to find these by investigating code or systematic penetration tests. For details again see the Cookbook.

Note

Late alerts

As a special variant of missing alerts, there can also be late alerts. A "late alert" occurs if CSRFGuard successfully detects an attack and aborts request handling, but an unsafe change has already occurred. Besides database transactions there is no generic way of undoing changes.

Unfortunately, this issue can even slip through penetration tests. Therefore, it is important to call StateChangeSafetyMgr#checkStateChangeAllowed

  • once for every independent security relevant change (not relying on their order)
  • and before the change is applied.

3.3 Token Injection

To enable authorized users to pass CSRFGuard, the synchronizer token has to be injected into all forms that trigger security-relevant changes. For CSRFGuard it is inserted as hidden form field with a fixed name. The injection can either be performed automatically or manually.

3.3.1 Automatic Token Injection

There are two ways to inject the synchronizer token: on the client side using JavaScript or on the server side using the <isform> ISML tag. The server-side token injection is the preferred way, because the token is provided together with the HTML. The client-side token injection has the disadvantage that the entire HTML as well as JavaScript has to be loaded by the web browser before the synchronizer token can be injected. On huge pages (e.g., huge search results) it may happen that the user submits the HTML forms before the token is injected via JavaScript.

3.3.1.1 Server-Side Injection

To solve the problems that came up with the client side, a server-side token injection is supported. To this end, ISML provides a new ISML <isform> tag, which should be used instead of the HTML <form> tag. During compilation this tag will be replaced with a regular HTML <form> tag and a hidden field for the synchronizer token. The token itself will be set by the Web Adapter when delivering the page. A new CSRFServlet handles the token generation and delivers it to the Web Adapter.

ServerSideCSRFTokenInjection
  1. A consumer is browsing an IS7 web page and issues a request for a specific site. The request is sent to the web server (including the Web Adapter).
  2. The Web Adapter processes the request, collects page fragments from the page cache and the application server.
  3. After collecting all the page fragments, they are assembled to the result page.
  4. The Web Adapter now identifies the X-IS-PLACEHOLDER_CSRF HTTP header, which indicates that there are placeholders for CSRF tokens on the current page.
  5. The result page is now post-processed. The Web Adapter issues requests to the CSRFServlet to determine the CSRF tokens and injects them into the appropriate <wacsrf> placeholders.
  6. The assembled page is returned to the client.

3.3.1.2 Client-Side Injection

CSRFGuard provides a JavaScript that is able to distribute the synchronizer token from a global JavaScript variable embedded into the HTML header over all forms contained in the page. Client-side injection is considered safe – given no cross-site scripting vulnerability exists, which would supersede the synchronizer token pattern anyway.

Client-side injection also uses the fact that the browser has to come up with a parsed representation of the page while the web application – like Intershop 7 – usually does not. Attempts to cover all forms automatically at server side are thus doomed to either fail or drain considerable server performance.

As an exception to the rule that only HTTP POST requests may carry the synchronizer token, AJAX requests are generally equipped with a custom HTTP header containing the token. This improves automatic injection for forms dynamically generated by JavaScript. There might still be cases where the CSRFGuard fails to detect a dynamically created form. See the cookbook on how to handle this.

To see how to enable automatic token injection also refer to the Cookbook.

3.3.2 Manual Token Injection

The disadvantage of automatic token injection is that a web application with automatic token injection will require enabled JavaScript in the browser.

If a web application absolutely requires running with disabled JavaScript, you can still fall back to manually inserting a hidden field into each form on server side. See the Cookbook on how to perform this.

While this seems tedious and error-prone, missing a form will never cause a security breach. The validation of synchronizer tokens is still generically performed. Instead, it may cause a false alert.

Disclaimer

The information provided in the Knowledge Base may not be applicable to all systems and situations. Intershop Communications will not be liable to any party for any direct or indirect damages resulting from the use of the Customer Support section of the Intershop Corporate Web site, including, without limitation, any lost profits, business interruption, loss of programs or other data on your information handling system.

Customer Support
Knowledge Base
Product Resources
Support Tickets