Difference between revisions of "Re-Authentication for expired Tokens"

From GeeklogWiki
Jump to: navigation, search
(forgot to explain the use of SECINT_recreateFilesArray())
(explicit call to SECINT_recreateFilesArray no longer necessary; clarified correct use of SEC_checkToken)
Line 19: Line 19:
 
On proper authentication, the GET or POST request is recreated from this information. A new security token is added and the request is sent to the original URL again. So from the target script's point of view, it looks like a regular request with a valid token came through.
 
On proper authentication, the GET or POST request is recreated from this information. A new security token is added and the request is sent to the original URL again. So from the target script's point of view, it looks like a regular request with a valid token came through.
  
In other words: The re-authentication and recreation of the request is entirely transparent. For scripts that already used CSRF protection, no code changes are required.
+
In other words: '''The re-authentication and recreation of the request is entirely transparent. For scripts that already use CSRF protection, no code changes are required.'''
 
 
Well, there's one small exception ...
 
  
 
=== Uploaded Files ===
 
=== Uploaded Files ===
 
First of all, if you expect files to be uploaded in your code, you need to call the new function <tt>SECINT_recreateFilesArray()</tt> somewhere ''after'' the call to <tt>SEC_checkToken()</tt> and ''before'' you access the <tt>$_FILES</tt> array for the first time. This function will ensure that a proper <tt>$_FILES</tt> array is recreated from the information provided in the recreated request.
 
  
 
Files uploaded through the original request (the one with the expired token) are rescued and moved to the site's 'data' directory. This is necessary, as the files would automatically be deleted by PHP otherwise. As a result, the rescued files can no longer be moved with <tt>move_uploaded_file()</tt> since that function only works on files that were uploaded with the last POST request.
 
Files uploaded through the original request (the one with the expired token) are rescued and moved to the site's 'data' directory. This is necessary, as the files would automatically be deleted by PHP otherwise. As a result, the rescued files can no longer be moved with <tt>move_uploaded_file()</tt> since that function only works on files that were uploaded with the last POST request.
Line 32: Line 28:
  
 
If you are using your own upload handling, then check the <tt>$_FILES</tt> array for an <tt>'_gl_data_dir'</tt> entry, e.g. <tt>$_FILES['file1']['_gl_data_dir']</tt>. If such an entry is present and not <tt>false</tt>, then use <tt>rename()</tt> instead of <tt>move_uploaded_file()</tt> to move the uploaded file to its destination. The <tt>'tmp_name'</tt> entry will already contain the correct path (for the 'data' directory), so you don't need to (and shouldn't) bother with its actual location.
 
If you are using your own upload handling, then check the <tt>$_FILES</tt> array for an <tt>'_gl_data_dir'</tt> entry, e.g. <tt>$_FILES['file1']['_gl_data_dir']</tt>. If such an entry is present and not <tt>false</tt>, then use <tt>rename()</tt> instead of <tt>move_uploaded_file()</tt> to move the uploaded file to its destination. The <tt>'tmp_name'</tt> entry will already contain the correct path (for the 'data' directory), so you don't need to (and shouldn't) bother with its actual location.
 +
 +
 +
== Using SEC_checkToken ==
 +
 +
It is important that you only call <tt>SEC_checkToken()</tt> when you expect a token. In other words, do '''NOT''' do this:
 +
 +
<pre>$token_valid = SEC_checkToken(); // don't do this!
 +
 +
switch ($_POST['mode']) {
 +
case 'delete':
 +
    if ($token_valid) ...
 +
    break;
 +
 +
case 'save':
 +
    if ($token_valid) ...
 +
    break;
 +
 +
case 'edit':
 +
    // token not checked here
 +
    display_editor();
 +
    break;
 +
}</pre>
 +
 +
In the above example, a token check is required for the 'delete' and 'save' cases, but not for 'edit'. However, <tt>SEC_checkToken()</tt> is also being called in the 'edit' case. In Geeklog versions 1.5.0 - 1.6.1, this would only cause <tt>SEC_checkToken()</tt> to return <tt>false</tt> for this case - but since the return value isn't checked, this would do no harm. From now on, however, this case would trigger the re-authentication form to be displayed!
 +
 +
So the proper use of <tt>SEC_checkToken()</tt> would be:
 +
 +
<pre>switch ($_POST['mode']) {
 +
case 'delete':
 +
    if (SEC_checkToken()) ...
 +
    break;
 +
 +
case 'save':
 +
    if (SEC_checkToken()) ...
 +
    break;
 +
 +
case 'edit':
 +
    // token not checked here
 +
    display_editor();
 +
    break;
 +
}</pre>
  
  

Revision as of 09:09, 30 December 2009

Background

The introduction of CSRF Protection migitates the risk of Cross Site Request Forgery (CSRF) attacks on Geeklog sites. At the same time, however, it inconveniences Admin users, since an expired token will invalidate a request and may lead to data loss.

Re-authentication (currently only implemented in the 1.6.2hg development version) attempts to address this problem.

Request for re-authentication

How it works

Following the recommendations for the use of security tokens, a call to SEC_checkToken() will check the token for validity. Previously, that function simply returned false when the token was not valid.

From now on, when SEC_checkToken() detects an invalid token, it will instead display a re-authentication form. This form explains what just happened and asks the user to enter their username and password. If the user provides the correct information, the previous request is recreated and sent again automatically. As a result, the last changes are not lost and the request is processed as if a valid token had been found.

Recreating Requests

The re-authentication form contains the information from the last request (GET or POST) in hidden fields, i.e. the entire $_GET and $_POST arrays, as well as the information from $_FILES, if files were uploaded with the failed request (the uploaded files themselves are temporarily stored in the site's 'data' directory).

On proper authentication, the GET or POST request is recreated from this information. A new security token is added and the request is sent to the original URL again. So from the target script's point of view, it looks like a regular request with a valid token came through.

In other words: The re-authentication and recreation of the request is entirely transparent. For scripts that already use CSRF protection, no code changes are required.

Uploaded Files

Files uploaded through the original request (the one with the expired token) are rescued and moved to the site's 'data' directory. This is necessary, as the files would automatically be deleted by PHP otherwise. As a result, the rescued files can no longer be moved with move_uploaded_file() since that function only works on files that were uploaded with the last POST request.

If you are using Geeklog's upload class (upload.class.php), this is handled automatically for you.

If you are using your own upload handling, then check the $_FILES array for an '_gl_data_dir' entry, e.g. $_FILES['file1']['_gl_data_dir']. If such an entry is present and not false, then use rename() instead of move_uploaded_file() to move the uploaded file to its destination. The 'tmp_name' entry will already contain the correct path (for the 'data' directory), so you don't need to (and shouldn't) bother with its actual location.


Using SEC_checkToken

It is important that you only call SEC_checkToken() when you expect a token. In other words, do NOT do this:

$token_valid = SEC_checkToken(); // don't do this!

switch ($_POST['mode']) {
case 'delete':
    if ($token_valid) ...
    break;

case 'save':
    if ($token_valid) ...
    break;

case 'edit':
    // token not checked here
    display_editor();
    break;
}

In the above example, a token check is required for the 'delete' and 'save' cases, but not for 'edit'. However, SEC_checkToken() is also being called in the 'edit' case. In Geeklog versions 1.5.0 - 1.6.1, this would only cause SEC_checkToken() to return false for this case - but since the return value isn't checked, this would do no harm. From now on, however, this case would trigger the re-authentication form to be displayed!

So the proper use of SEC_checkToken() would be:

switch ($_POST['mode']) {
case 'delete':
    if (SEC_checkToken()) ...
    break;

case 'save':
    if (SEC_checkToken()) ...
    break;

case 'edit':
    // token not checked here
    display_editor();
    break;
}


Known Issues

  • Re-authentication does not work for users that authenticate against OpenID (it does, however, work with other remote authentication modules like LDAP and LiveJournal).
  • When using a server running as localhost on Mac OS X, the recreated request may fail due to the current IP address being ::1 in the session but 127.0.0.1 in the recreated request.
    Solution: Use the machine's Bonjour name (xxx.local) instead of localhost.