# HG changeset patch
# User Dan
# Date 1212857178 14400
# Node ID 75df0b2c596c8750516f396aadf41987c746b99f
# Parent e53cf8b1d94233fb76868909b481cf3063fdbc5e
Got initial CSRF token framework implemented and sample implementation added in Special:Logout; removing Javascript compression engine from aggressive_optimize_html() and instead calling JavascriptCompressor class from js-compressor.php
diff -r e53cf8b1d942 -r 75df0b2c596c includes/clientside/static/faders.js
--- a/includes/clientside/static/faders.js Sat Jun 07 12:43:57 2008 -0400
+++ b/includes/clientside/static/faders.js Sat Jun 07 12:46:18 2008 -0400
@@ -788,7 +788,7 @@
var mb = new MessageBox(MB_YESNO|MB_ICONQUESTION, $lang.get('user_logout_confirm_title'), $lang.get('user_logout_confirm_body'));
mb.onclick['Yes'] = function()
{
- window.location = makeUrlNS('Special', 'Logout/' + title);
+ window.location = makeUrlNS('Special', 'Logout/' + csrf_token + '/' + title);
}
}
diff -r e53cf8b1d942 -r 75df0b2c596c includes/functions.php
--- a/includes/functions.php Sat Jun 07 12:43:57 2008 -0400
+++ b/includes/functions.php Sat Jun 07 12:46:18 2008 -0400
@@ -391,6 +391,91 @@
}
+/**
+ * Generates a confirmation form if a CSRF check fails. Will terminate execution.
+ */
+
+function csrf_confirm_form()
+{
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $lang;
+
+ // If the token was overridden with the correct one, the user confirmed the action using this form. Continue exec.
+ if ( isset($_POST['cstok']) || isset($_GET ['cstok']) )
+ {
+ // using the if() check makes sure that the token isn't in a cookie, since $_REQUEST includes $_COOKIE.
+ $token_check =& $_REQUEST['cstok'];
+ if ( $token_check === $session->csrf_token )
+ {
+ // overridden token matches, continue exec
+ return true;
+ }
+ }
+
+ $template->tpl_strings['PAGE_NAME'] = htmlspecialchars($lang->get('user_csrf_confirm_title'));
+ $template->header();
+
+ // initial info
+ echo '
' . $lang->get('user_csrf_confirm_body') . '
';
+
+ // start form
+ $form_method = ( empty($_POST) ) ? 'get' : 'post';
+ echo '';
+
+ $template->footer();
+
+ exit;
+}
+
+function csrf_confirm_get_recursive($_inner = false, $pfx = false, $data = false)
+{
+ // make posted arrays work right
+ if ( !$data )
+ ( $_inner == 'post' ) ? $data =& $_POST : $data =& $_GET;
+ foreach ( $data as $key => $value )
+ {
+ $pfx_this = ( empty($pfx) ) ? $key : "{$pfx}[{$key}]";
+ if ( is_array($value) )
+ {
+ csrf_confirm_get_recursive(true, $pfx_this, $value);
+ }
+ else if ( empty($value) )
+ {
+ echo htmlspecialchars($pfx_this . " = ") . "
\n";
+ echo '';
+ }
+ else
+ {
+ echo htmlspecialchars($pfx_this . " = " . $value) . "
\n";
+ echo '';
+ }
+ }
+}
+
+function csrf_confirm_post_recursive()
+{
+ csrf_confirm_get_recursive('post');
+}
+
// Removed wikiFormat() from here, replaced with RenderMan::render
/**
@@ -2894,6 +2979,8 @@
// Optimize (but don't obfuscate) Javascript
preg_match_all('/";
// apply changes
$html = str_replace($jscript[0][$i], $replacement, $html);
+
}
// Re-insert untouchable tags
diff -r e53cf8b1d942 -r 75df0b2c596c language/english/user.json
--- a/language/english/user.json Sat Jun 07 12:43:57 2008 -0400
+++ b/language/english/user.json Sat Jun 07 12:46:18 2008 -0400
@@ -90,6 +90,7 @@
logout_confirm_body: 'If you log out, you will no longer be able to access your user preferences, your private messages, or certain areas of this site until you log in again.',
logout_confirm_title_elev: 'Are you sure you want to de-authenticate?',
logout_confirm_body_elev: 'If you de-authenticate, you will no longer be able to use the administration panel until you re-authenticate again. You may do so at any time using the Administration button on the sidebar.',
+ logout_confirm_btn_logout: 'Log Out',
logout_err_title: 'An error occurred during the logout process.',
// Unused at this point
logout_err_not_loggedin: 'You don\'t seem to be logged in.',
@@ -226,6 +227,12 @@
autofill_heading_suggestions: 'Username suggestions',
autofill_msg_no_suggestions: 'No suggestions',
+
+ // CSRF confirmation form
+ csrf_confirm_title: 'Invalid form confirmation key',
+ csrf_confirm_body: 'Your browser sent an invalid confirmation key for a form. Your session may have expired, or you may have been redirected here from a remote site in an attack known as Cross-Site Request Forgery (CSRF). If you are sure you want to continue with this action, you may click the button below. Otherwise, return to the main page and do not proceed.',
+ csrf_confirm_btn_viewrequest: 'View request and form data',
+ csrf_confirm_btn_continue: 'Continue',
},
usercp: {
// Meta
diff -r e53cf8b1d942 -r 75df0b2c596c plugins/SpecialUserFuncs.php
--- a/plugins/SpecialUserFuncs.php Sat Jun 07 12:43:57 2008 -0400
+++ b/plugins/SpecialUserFuncs.php Sat Jun 07 12:46:18 2008 -0400
@@ -5,7 +5,7 @@
"Plugin URI" : "http://enanocms.org/",
"Description" : "plugin_specialuserfuncs_desc",
"Author" : "Dan Fuhry",
- "Version" : "1.1.3",
+ "Version" : "1.1.4",
"Author URI" : "http://enanocms.org/"
}
**!*/
@@ -226,6 +226,12 @@
case 'key_not_found':
$errstring = $lang->get('user_err_key_not_found');
break;
+ case 'ERR_DH_KEY_NOT_FOUND':
+ $errstring = $lang->get('user_err_dh_key_not_found') . " -- {$__login_status['debug']}";
+ break;
+ case 'ERR_DH_KEY_NOT_INTEGER':
+ $errstring = $lang->get('user_err_dh_key_not_numeric');
+ break;
case 'key_wrong_length':
$errstring = $lang->get('user_err_key_wrong_length');
break;
@@ -252,7 +258,7 @@
$attempts = $__login_status['lockout_threshold'];
$server_time = time();
- $time_rem = ( $__login_status['lockout_last_time'] == time() ) ? $__login_status['lockout_duration'] : $__login_status['lockout_duration'] - round( ( $server_time - $__login_status['lockout_last_time'] ) / 60 );
+ $time_rem = ( intval(@$__login_status['lockout_last_time']) == time() ) ? $__login_status['lockout_duration'] : $__login_status['lockout_duration'] - round( ( $server_time - $__login_status['lockout_last_time'] ) / 60 );
if ( $time_rem < 1 )
$time_rem = $__login_status['lockout_duration'];
@@ -452,9 +458,8 @@
}
if ( isset($_GET['act']) && $_GET['act'] == 'ajaxlogin' )
{
- die('This version of the Enano LoginAPI is deprecated. Please use the action.json method instead.');
- $db->close();
- exit;
+ echo 'This version of the Enano LoginAPI is deprecated. Please use the action.json method instead.';
+ return true;
}
if(isset($_POST['login']))
{
@@ -480,7 +485,12 @@
$dh_public = $_POST['dh_public_key'];
if ( !preg_match('/^[0-9]+$/', $dh_public) )
{
- die_semicritical('DiffieHellman error', 'Public key not integer: ' . $dh_public);
+ $__login_status = array(
+ 'success' => false,
+ 'error' => 'ERR_DH_KEY_NOT_INTEGER',
+ 'debug' => "public key: $dh_public"
+ );
+ return false;
}
$q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
if ( !$q )
@@ -488,7 +498,12 @@
if ( $db->numrows() < 1 )
{
- die_semicritical('DiffieHellman error', 'ERR_DH_KEY_NOT_FOUND');
+ $__login_status = array(
+ 'success' => false,
+ 'error' => 'ERR_DH_KEY_NOT_FOUND',
+ 'debug' => "public key: $dh_public"
+ );
+ return false;
}
list($dh_private, $dh_key_id) = $db->fetchrow_num();
@@ -508,7 +523,12 @@
$dh_hash = $_POST['crypt_key'];
if ( $dh_secret_check !== $dh_hash )
{
- die_semicritical('DiffieHellman error', 'ERR_DH_HASH_NO_MATCH');
+ $__login_status = array(
+ 'success' => false,
+ 'error' => 'ERR_DH_HASH_NO_MATCH',
+ 'debug' => "dh_secret_check = $dh_secret_check\ndh_hash_input = $dh_hash"
+ );
+ return false;
}
// All good! Generate the AES key
@@ -581,18 +601,28 @@
exit;
}
-function page_Special_Logout() {
+function page_Special_Logout()
+{
global $db, $session, $paths, $template, $plugins; // Common objects
global $lang;
+
if ( !$session->user_logged_in )
$paths->main_page();
+ $token = $paths->getParam(0);
+ if ( $token !== $session->csrf_token )
+ {
+ csrf_confirm_form();
+ }
+
$l = $session->logout();
if ( $l == 'success' )
{
$url = makeUrl(getConfig('main_page'), false, true);
- if ( $pi = $paths->getAllParams() )
+ if ( $paths->getParam(1) )
{
+ $pi = explode('/', $paths->getAllParams());
+ $pi = implode('/', array_values(array_slice($pi, 1)));
list($pid, $ns) = RenderMan::strToPageID($pi);
$perms = $session->fetch_page_acl($pid, $ns);
if ( $perms->get_permissions('read') )
@@ -600,7 +630,7 @@
$url = makeUrl($pi, false, true);
}
}
- redirect($url, $lang->get('user_logout_success_title'), $lang->get('user_logout_success_body'), 4);
+ redirect($url, $lang->get('user_logout_success_title'), $lang->get('user_logout_success_body'), 3);
}
$template->header();
echo '' . $lang->get('user_logout_err_title') . '
';
@@ -2027,10 +2057,14 @@
}
$timestamp = enano_date('D, j M Y H:i:s T', $lang_local->lang_timestamp);
+ // generate expires header
+ $expires = date('r', mktime(-1, -1, -1, -1, -1, intval(date('y'))+1));
+
header("Last-Modified: $timestamp");
header("Date: $timestamp");
header("ETag: \"$etag\"");
header('Content-type: text/javascript');
+ header("Expires: $expires");
$lang_local->fetch();
echo "if ( typeof(enano_lang) != 'object' )
@@ -2108,9 +2142,6 @@
}
fclose($fh);
- gzip_output();
-
- return true;
}
return true;
}