Loading...
 
Approach to CSRF protection Features »  Security Type of page »  Developers documentation

CSRF Protection

See also CSRF wishes

General Approach

The general approach to CSRF protection in Tiki is to validate the following for requests that change the database or user files on the server ("state-changing actions"):

  • The POST method is used
  • The request originates from the site itself
  • The request includes a ticket (a token) that matches a ticket on the server that is not expired

Other actions that only extract or display data (e.g., view, export, download, print) are not subject to CSRF protection validation and the GET method can be used for these actions.

This approach is in line with OWASP recommendations.

Implementation

Overview

The approach described above is implemented as follows:

  • A cryptographically secure pseudo-random sequence of bytes encoded into the base 64 character set (referred to as a ticket) is placed as a hidden input in each form from which state-changing actions can be requested
  • At the same time this ticket is created and placed into a form, it is stored on the server with a time stamp
  • Clicking on an element in a form that will trigger a state-changing action will first check that the ticket expiration period hasn't passed, and will popup a warning if it has (if javascript is enabled)
  • The php code that executes the state-changing request is made to be conditional on the CSRF protection validating successfully
  • The CSRF validation checks that (1) the request is a POST, (2) the request originates from the site itself, and (3) the ticket matches one stored on the server that is not expired.
  • The validation returns true if sucessful so that the action can be executed. If not successful, the action will not be performed and an error message will be displayed and environment details are written to the system php error log


The implementation also allows for the following:

  • Converting state-changing GET requests into POST requests through use of a confirmation form
  • Ability to generate a confirmation form even for POST requests that already have a ticket, for example, when desired for actions that cannot be undone (e.g., deleting an item)
  • For select elements, handling each option independently as to whether they require validation or a confirmation form
  • Ability to use popup confirmation forms even if ajax services are not being used to avoid loading a separate confirmation page, if javascript is enabled
  • When the bootstrap modal smarty function is used for ajax, includes form inputs into the modal popup so the parameters do not need to be separately included in the smarty function

Preferences

The security preference securityTimeout is used to set the number of seconds after which tickets and related forms expire. The session_lifetime preference is used for the default, if set, otherwise the session.gc_maxlifetime php.ini setting is used, subject to a default maximum of four hours in any case.

The old preferences for CSRF protection, feature_ticketlib and feature_ticketlib2 will be removed once the related check_ticket methods have been replaced throughout Tiki.

Ticket

The smarty ticket function is used to generate tickets for the HTML. This function creates a new ticket assuming a ticket smarty variable has not already been set (e.g., by another form on the same page) and also stores the ticket in the $_SESSION['tickets'] variable with a time stamp. A new ticket is generated for each page load. By default, tickets are deleted from the server once they are matched.

There are three ways the ticket function can be used, all of which are illustrated in the examples below:

  • {ticket} : returns hidden input HTML with the ticket
  • {ticket mode=confirm} : In addition to the ticket hidden input, returns the hidden input HTML indicating the submission is a confirmation
  • {ticket mode=get} : returns the ticket only

Javascript

Onclick methods are used (see examples below) to generate popup confirmation forms.

There is a listener function in tiki-confirm.js that generates a popup timeout warning the first time an input is given or a dropdown changed on any form that has the ticket input element. This way the user can refresh the page before filling out the complete form. The listener will also work for forms in a popover. It does not work for modal popups that have been left up long enough for the the ticket to expire - in this case the user will simply get an error notice once the form in the popup is submitted.

Other notes

Since the CSRF check is set by default to check that the request method is POST, there is less of a need to convert $_REQUEST to $_POST in the Php code where the actions are performed, although it is still better practice to use $_POST or $_GET rather than $_REQUEST.

The examples below cover the more common use cases. The checkCsrf() method within lib/tikiaccesslib.php provides other settings to accommodate other less common use cases.

Examples

These examples assume a smarty template is being used for the HTML.

Standard forms in a page (non-ajax)

These examples assumes ajax services are not being used for the action. They also assume a select element is not being used to submit the form - see example below if a select element is being used to submit the form.

No confirmation of action desired

Location Implementation Result
Form HTML Add {ticket} to the form Inserts hidden input with the ticket
Php Secondarily condition the code executing the request on $access->checkCsrf() Returns boolean depending on success of CSRF check

Here is an example of conditioning the Php code on the CSRF validition:

Copy to clipboard
<?php if (! empty($_POST['lock']) && $access->checkCsrf()) { //perform lock here }
 Important
The CSRF check condition should always be second to avoid unnecessary checks and false errors in case of GET requests intended for other parts of the code.

Confirmation of action desired

Location Implementation Result
Form HTML Add {ticket} to the form Inserts hidden input with the ticket
Form HTML Add onclick="confirmPopup('{tr}Delete this item?{/tr}')" to the submit element Pops up a confirmation form upon click. Ticket expiry is also checked so a warning preventing submitting pops up instead if tickets are expired (if javascript is enabled)
Php Secondarily condition the code executing the request on $access->checkCsrf(true) If request is the result of submission of the confirmation form, then a boolean is returned based on the result of the CSRF check. If not (because standard form was clicked and javascript was not enabled), then a redirect to a onfirmation page will occur.

Here is an example of conditioning the Php code on the CSRF check and submission of the confirmation form:

Copy to clipboard
<?php if (! empty($_POST['delete']) && $access->checkCsrf(true) { //perform delete here }
Confirmation forms

Note that confirmation forms use {ticket mode=confirm} , which adds a hidden input in addition to the ticket. The $access->checkCsrf(true) method determines whether the request is a confirmation or not using this hidden input. If the input is missing, then the method will perform a redirect to a confirmation page. If it is there, the CSRF check will be performed and a boolean returned based on the result. If javascript is enabled and the confirmPopup() method is used, then the confirmation input will be included and there will be no redirection to a confirmation page.

Change to form if possible

If a link is used for a state-changing action and ajax services are not being used, then the first choice is to change the link to a form so that the $_POST method is used rather than the $_GET method, which should not be used for state-changing actions. If the link was part of a popup list of actions (for example, the popups displayed after clicking the icon), the submit element of the form used to replace the link will be styled to look the same as a link if the button element is used and the classes btn btn-link are applied.

For example, this link:

Copy to clipboard
<a href="tiki-admin_feature.php?lock=1"> {icon name="lock" _menu_text='y' _menu_icon='y' alt="{tr}Lock{/tr}"} </a>


Should be changed to this form:

Copy to clipboard
<form action="tiki-admin_feature.php" method="post"> {ticket} <button type="submit" name="lock" value="1" class="btn btn-link"> {icon name="lock"} {tr}Lock{/tr} </button> </form>


The same considerations regarding whether a confirmation is needed would apply as described in the standardized form section above.

If it is not possible to change the link into a form, then a confirmation popup or page needs to be shown first so that the GET request is converted into a form (the confirm) that uses the POST method and a ticket.

Location Implementation Result
Link HTML Add onclick="confirmPopup('{tr}Delete this item?{/tr}', '{ticket mode=get}')" to the link element Pops up a confirmation form upon click. Ticket expiry is also checked so a warning preventing submitting pops up instead if tickets are expired (if javascript is enabled)
Php Secondarily condition the code executing the request on $access->checkCsrf(true) If request is the result of submission of the confirmation form, then a boolean is returned based on the result of the CSRF check. If not (because standard for was clicked and javascript was not enabled), then a redirect to a onfirmation page will occur.

Form with select element

For this example we'll assume there are two options in a "Perform action on checked items..." select element for which different treatments are desired:

  • One option to delete checked items, for which a confirmation is desired since the action cannot be undone
  • One option to lock checked items, for which no confirmation is desired since the action can easily be undone
Location Implementation Result
Form HTML Add {ticket} to the form Inserts hidden input with the ticket
Form HTML Add onclick="confirmPopup()" to the submit element Pops up a confirmation form upon click only for the options with the class confirm-popup. Ticket expiry is also checked so a warning preventing submitting pops up instead if tickets are expired (if javascript is enabled)
Form HTML For the option element for which confirmation is desired, add the class confirm-popup and optionally add the confirmation text as a data attribute, e.g., data-confirm-text="{tr}Delete items?{/tr}" Will cause a confirmation form to pop up when this option is selected and the select element is clicked
Php For the delete action requiring confirmation, secondarily condition the code executing the request on $access->checkCsrf(true) If request is the result of submission of the confirmation form, then a boolean is returned based on the result of the CSRF check. If not (because standard form was clicked and javascript was not enabled), then a redirect to a onfirmation page will occur.
Php For the lock action not requiring a confirmation, secondarily condition the code executing the request on $access->checkCsrf() Returns boolean depending on success of CSRF validation

Ajax services

When ajax services are used to perform the action, two things can be different than a non-ajax submission:

  • There are often two passes through the method that performs the action: the first that brings up a form, and the second that performs the action after the form is submitted
  • A Services_Utilities class should be used to carry out the CSRF checks. The methods referred to below are from this class.

Requiring two passes and the action code is first

Location Implementation Result
Original form HTML Add {ticket} to the form Inserts hidden input with the ticket
Original form HTML Add onclick="confirmPopup()" to the submit element Pops up the modal form specified in the ajax service Php code - including this method will include all inputs in the form. The method is not needed if the form inputs are not needed because all necessary parameters are set in the bootstrap modal smarty function
Php Condition the code executing the request on isConfirmPost() from the Services_Utilities class If request is the result of submission of the modal form, then a boolean is returned based on the result of the CSRF check. If not (because the original form was clicked and this is the first pass), then the code will be skipped and the code generating the modal form will be executed
Modal form HTML Add {ticket mode=confirm} to the form Inserts two hidden inputs: one with the ticket and the other with a hidden input named confirmForm with a value of y

Here is an example of the above:

Copy to clipboard
<?php $util = new Services_Utilities(); if ($util->isConfirmPost()) { //the isConfirmPost method also validates for CSRF if the post is a confirm submission //perform action here } else { //render modal form here }

Requiring two passes and the code to render the modal form is first

The HTML is the same as the prior example. For the Php, use the notConfirmPost() method and bring up form if true, else use checkCsrf() (from the Services_Utilities class) before performing the action. Below is an example:

Copy to clipboard
<?php $util = new Services_Utilities(); if ($util->notConfirmPost()) { //render modal form here } elseif ($util->checkCsrf()) { //perform action here }

Requiring one pass (no modal form involved)

Location Implementation Result
Original form HTML Add {ticket} to the form Inserts hidden input with the ticket
Original form HTML Add onclick="confirmPopup()" to the submit element Pops up the modal form specified in the ajax service Php code.
Php Condition the code executing the request on checkCsrf() from the Services_Utilities class Returns boolean depending on success of CSRF check

Keywords

The following is a list of keywords that should serve as hubs for navigation within the Tiki development and should correspond to documentation keywords.

Each feature in Tiki has a wiki page which regroups all the bugs, requests for enhancements, etc. It is somewhat a form of wiki-based project management. You can also express your interest in a feature by adding it to your profile. You can also try out the Dynamic filter.

Accessibility (WAI & 508)
Accounting
Administration
Ajax
Articles & Submissions
Backlinks
Banner
Batch
BigBlueButton audio/video/chat/screensharing
Blog
Bookmark
Browser Compatibility
Calendar
Category
Chat
Comment
Communication Center
Consistency
Contacts Address book
Contact us
Content template
Contribution
Cookie
Copyright
Credits
Custom Home (and Group Home Page)
Database MySQL - MyISAM
Database MySQL - InnoDB
Date and Time
Debugger Console
Diagram
Directory (of hyperlinks)
Documentation link from Tiki to doc.tiki.org (Help System)
Docs
DogFood
Draw -superseded by Diagram
Dynamic Content
Preferences
Dynamic Variable
External Authentication
FAQ
Featured links
Feeds (RSS)
File Gallery
Forum
Friendship Network (Community)
Gantt
Group
Groupmail
Help
History
Hotword
HTML Page
i18n (Multilingual, l10n, Babelfish)
Image Gallery
Import-Export
Install
Integrator
Interoperability
Inter-User Messages
InterTiki
jQuery
Kaltura video management
Kanban
Karma
Live Support
Logs (system & action)
Lost edit protection
Mail-in
Map
Menu
Meta Tag
Missing features
Visual Mapping
Mobile
Mods
Modules
MultiTiki
MyTiki
Newsletter
Notepad
OS independence (Non-Linux, Windows/IIS, Mac, BSD)
Organic Groups (Self-managed Teams)
Packages
Payment
PDF
Performance Speed / Load / Compression / Cache
Permission
Poll
Profiles
Quiz
Rating
Realname
Report
Revision Approval
Scheduler
Score
Search engine optimization (SEO)
Search
Security
Semantic links
Share
Shopping Cart
Shoutbox
Site Identity
Slideshow
Smarty Template
Social Networking
Spam protection (Anti-bot CATPCHA)
Spellcheck
Spreadsheet
Staging and Approval
Stats
Survey
Syntax Highlighter (Codemirror)
Tablesorter
Tags
Task
Tell a Friend
Terms and Conditions
Theme
TikiTests
Federated Timesheets
Token Access
Toolbar (Quicktags)
Tours
Trackers
TRIM
User Administration
User Files
User Menu
Watch
Webmail and Groupmail
WebServices
Wiki History, page rename, etc
Wiki plugins extends basic syntax
Wiki syntax text area, parser, etc
Wiki structure (book and table of content)
Workspace and perspectives
WYSIWTSN
WYSIWYCA
WYSIWYG
XMLRPC
XMPP




Useful Tools