How Radicore deals with CSRF attacks

By Tony Marston

8th October 2017

Introduction
Existing Measures
Additional Measures

Introduction

Cross Site Request Forgery (CSRF) is a type of attack used to compromise web applications. It is just one of several attacks which have been identified by the Open Web Application Security Project (OWASP). Its full description is as follows:

Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker's choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.

In short, this requires the attacker to construct a malicious URL and send it to his victim who then activates the hyperlink. This causes the hyperlink to be executed in the victim's browser without him/her realising it. Note that the term "currently authenticated" can mean one of two things depending on the authentication method:

Note that the cookie option may not actually work as the act of clicking on a URL in an email may open a new browser window instead of a new tab in the current window, and the cookies which are available in one browser window may not be available in a different window. This means that the malicious URL will never be able to piggyback onto an exiting authenticated session even if a valid session name, which points to a session ID in the cookie data, is known.

Note that the SSO option may still use a cookie to store the valid session ID, but even if the malicious URL does not piggyback on a current session and force the user through the logon screen, the logon processing will automatically create a valid session using the user's OS logon details. Depending on how the application is coded the script which is activated after the logon process may or may not be the script indicated in the URL which was bounced back to the logon screen. For example, a malicious URL attempts to run the "Invoice Payment" script, but this is rejected and bounced back to the logon screen, but after accepting the user's credentials the logon screen automatically activates the Home Page and not the script which caused the bounce.

Existing Measures

Certain security measures were built into the RADICORE framework when it was first created, and these measures already help to avoid various attacks.

  1. Authentication required

    Nobody can access the application unless they first pass through the logon screen using valid credentials.

  2. Stored hyperlinks are invalid

    It is not possible, even for an authenticated user, to bookmark a page and return to the same page later on. Unless a URL identifies a valid session, and that session contains data used by the script in that URL (see browser's address bar below), that URL will be rejected. Even though a URL may be captured from a valid session, once a session has been destroyed on the server any reference to it is no longer valid.

  3. Access Control

    The application may contain hundreds or even thousands of tasks (user transactions), but the inbuilt Role Based Access Control (RBAC) mechanism restricts a user's access to a limited subset of the available tasks. This means that when the framework constructs the links in each page, any which point to a task which is not on the user's access list will be dropped. Thus users will never see any options which they cannot access, and if they cannot see a link they cannot select it. Even if they try to enter the link in the browser's address bar the system will still check that the task is accessible by the user.

  4. Updates do not use the GET method

    A URL, such as that which can be typed into a browser's address bar, can only send a GET request to the server. All database updates use the POST method which uses a SUBMIT button within an HTML form, and this cannot be duplicated in a browser's address bar. Any operation activated by the GET method will retrieve data from the server and send it to the client's browser as an HTML form, a CSV file or a PDF document.

  5. Updates use GET followed by POST

    All operations which perform database inserts, updates or deletes are done in two steps. First, a GET method in which an HTML form is constructed on the server and sent to the client's browser. The user then reviews the form, makes any changes, then presses a SUBMIT button to send confirmation of the desired action to the server using the POST method.

  6. Primary keys are not embedded in any URLs

    The identities of any objects in the database, such as account numbers or names, are never exposed in any URL. This means that a URL such as

    http://bank.com/transfer.do?acct=BOB&amount=100
    is never used and therefore cannot be spoofed. Whenever primary keys are passed from one script to another it is done in the session data on the server.

  7. Primary keys are passed in the session data

    An operation such as "Pay Invoice" must first be given the identity of an invoice before it can proceed. This is done by selecting an invoice in the previous (parent) screen, then pressing a navigation button to activate the "Pay Invoice" (child) task. The navigation button will activate the POST method on the current form which will first check that the selected task is allowed to be activated, then it will create an area in the session data for that task into which it will insert the parameters under which the task will operate. When the selected task is activated it will again check that it is allowed, then look for its operating parameters in the session data, and if none exist it will terminate itself and kick the user back to the logon screen. Note that primary keys can only be extracted from the parent task and passed to the child task when the child task is activated from a navigation button in the parent task. These child tasks will expect a primary key to be passed to them, which means that they can only be associated with a parent task which provides the right keys. Primary keys are never passed to a task which is selected from a menu button.

  8. Scripts cannot be activated from the browser's address bar.

    Once a user has started a valid session by passing through the logon screen onto the Home Page the only way to activate a new task is either via a menu button or a navigation button. This will cause the current script to be reactivated so that it can create an area in the session data to hold parameters for the selected script before that script is activated.

  9. Script termination, suspension and reactivation

    When a script terminates its parameter area in the session data is destroyed so that it cannot be reactivated unless it is available from a new link in the current page. If the script is selected again a new parameter area will have to be created.

    If a menu button is used in a non-menu web page, then that script is terminated. It will disappear from the "breadcrumbs" area and be replaced by the selected menu.

    If a navigation button is used then the current script is suspended before control is passed to the requested child script. The identity of the child script will then be appended to the list in the "breadcrumbs" area.

    When the child script terminates by using a button on the action bar its parent will be woken up from its suspended state, thus allowing it to continue from where it left off. Any pagination or scrolling options which were in force when the script was suspended will be reinstated.

    If a script is currently visible in the "breadcrumbs" area of the menu bar it is suspended and can be reactivated. It will not require a new parameter area in the session data to be created as one will already exist.

Additional Measures

Although the existing measures may seem adequate, there is no harm in going one step further. Consequently I have added code to version 2.07.0 of the framework to implement the solution described in Good Patterns and procedures to prevent CSRF by adding a new anti-CSRF token to each request. This token is not per-session, it is per-request, so even if an attacker could sniff the network traffic and extract the token from a valid request this token could never be used more than once.

For those controls which use the POST method a single token is supplied in the FORM element.

For those controls which use the GET method a separate token is supplied in the hyperlink.

All tokens are generated from a random sequence by the framework and built into each HTML form during the XSL Transformation process, and then stored in the session data. Note that as each new web page is displayed all stored tokens which were generated by previous pages are destroyed, thus making any links which use those tokens invalid. As each script is activated the token included in the URL is compared against the token for that script in the session data, and only if the two match is the script allowed to continue, otherwise it will be terminated and kicked back to the login screen.

All this activity is automatically performed by the framework, so there is nothing that the developer need do.


© Tony Marston
8th October 2017

http://www.tonymarston.net
http://www.radicore.org

counter