The cross-site request forgery guard closes Intershop's vulnerability to cross-site request forgery attacks.
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. |
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:
The attack is run like this:
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.
"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).
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.
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:
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).
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.
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:
For these reasons, CSRFGuard only reads synchronizer tokens from HTTP POST parameters and a custom HTTP header.
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.
CSRFGuard now needs a notion to determine whether a request handler either
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
.
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:
| This method has to be called whenever a state change is about to occur. It will throw a |
---|---|
| Opens a safe changes block in the current thread. All calls to |
| Closes the current safe changes block, thus neutralizing the last call to |
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:
checkStateChangeAllowed
ineffective, enclose it in startSafeChangesBlock
and endSafeChangesBlock
.checkStateChangeAllowed
in front of it.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:
checkStateChangeAllowed
has occurred outside safe changes blocks.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
.
ProcessMultipartRequest
( bc_foundation).CopyFile
, DeleteFile
and ExtractCompressedFile
(bc_foundation).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:
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.
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
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.
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.
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.
X-IS-PLACEHOLDER_CSRF
HTTP header, which indicates that there are placeholders for CSRF tokens on the current page.CSRFServlet
to determine the CSRF tokens and injects them into the appropriate <wacsrf>
placeholders.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.
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.