From 7428254a601207e35f7235c9f3c6f38f8ce82925 Mon Sep 17 00:00:00 2001 From: pdontthink Date: Wed, 12 Aug 2009 08:20:46 +0000 Subject: [PATCH] Implemented page referal verification mechanism. (Secunia Advisory SA34627) git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@13816 7612ce4b-ef26-0410-bec9-ea0150e637f0 --- doc/ChangeLog | 1 + functions/auth.php | 33 ++++++++++++++++++++++++++++++++- include/init.php | 31 ++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 34767c78..ab2101fd 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -322,6 +322,7 @@ Version 1.5.2 - SVN - Fixed broken RFC1918 reference in contrib/.htaccess and doc/.htaccess (#2798839). - Stop using deprecated ereg functions. (#2820952) - Remove personal data from Message ID seed. (#880029/847107) + - Implemented page referal verification mechanism. (Secunia Advisory SA34627) Version 1.5.1 (branched on 2006-02-12) -------------------------------------- diff --git a/functions/auth.php b/functions/auth.php index 9ee20bbb..6bfb76a9 100644 --- a/functions/auth.php +++ b/functions/auth.php @@ -24,13 +24,44 @@ * and PAGE_NAME in session and returns false. POST information is saved in * 'session_expired_post' variable, PAGE_NAME is saved in 'session_expired_location'. * + * This function optionally checks the referrer of this page request. If the + * administrator wants to impose a check that the referrer of this page request + * is another page on the same domain (otherwise, the page request is likely + * the result of a XSS or phishing attack), then they need to specify the + * acceptable referrer domain in a variable named $check_referrer in + * config/config.php (or the configuration tool) for which the value is + * usually the same as the $domain setting (for example: + * $check_referrer = 'example.com'; + * However, in some cases (where proxy servers are in use, etc.), the + * acceptable referrer might be different. If $check_referrer is set to + * "###DOMAIN###", then the current value of $domain is used (useful in + * situations where $domain might change at runtime (when using the Login + * Manager plugin to host multiple domains with one SquirrelMail installation, + * for example)): + * $check_referrer = '###DOMAIN###'; + * NOTE HOWEVER, that referrer checks are not foolproof - they can be spoofed + * by browsers, and some browsers intentionally don't send them, in which + * case SquirrelMail silently ignores referrer checks. + * * Script that uses this function instead of is_logged_in() function, must handle user * level messages. * @return boolean * @since 1.5.1 */ function sqauth_is_logged_in() { - if ( sqsession_is_registered('user_is_logged_in') ) { + + global $check_referrer, $domain; + if (!sqgetGlobalVar('HTTP_REFERER', $referrer, SQ_SERVER)) $referrer = ''; + if ($check_referrer == '###DOMAIN###') $check_referrer = $domain; + if (!empty($check_referrer)) { + $ssl_check_referrer = 'https://' . $check_referrer; + $check_referrer = 'http://' . $check_referrer; + } + if (sqsession_is_registered('user_is_logged_in') + && (!$check_referrer || empty($referrer) + || ($check_referrer && !empty($referrer) + && (strpos(strtolower($referrer), strtolower($check_referrer)) === 0 + || strpos(strtolower($referrer), strtolower($ssl_check_referrer)) === 0)))) { return true; } diff --git a/include/init.php b/include/init.php index 047f1877..a9cc62f6 100644 --- a/include/init.php +++ b/include/init.php @@ -118,7 +118,7 @@ if(!empty($_SERVER['UNIQUE_ID'])) { } $seed .= uniqid(mt_rand(),TRUE); -$seed .= implode( '', stat( __FILE__) ); +$seed .= implode('', stat( __FILE__)); // mt_srand() uses an integer to seed, so we need to distill our // very large seed to something useful (without taking a sub-string, @@ -571,16 +571,28 @@ switch (PAGE_NAME) { /** - * Check if we are logged in + * Check if we are logged in and does optional referrer check */ require(SM_PATH . 'functions/auth.php'); - if ( !sqsession_is_registered('user_is_logged_in') ) { + global $check_referrer, $domain; + if (!sqgetGlobalVar('HTTP_REFERER', $referrer, SQ_SERVER)) $referrer = ''; + if ($check_referrer == '###DOMAIN###') $check_referrer = $domain; + if (!empty($check_referrer)) { + $ssl_check_referrer = 'https://' . $check_referrer; + $check_referrer = 'http://' . $check_referrer; + } + if (!sqsession_is_registered('user_is_logged_in') + || ($check_referrer && !empty($referrer) + && strpos(strtolower($referrer), strtolower($check_referrer)) !== 0 + && strpos(strtolower($referrer), strtolower($ssl_check_referrer)) !== 0)) { // use $message to indicate what logout text the user // will see... if 0, typical "You must be logged in" // if 1, information that the user session was saved - // and will be resumed after (re)login + // and will be resumed after (re)login, if 2, there + // seems to have been a XSS or phishing attack (bad + // referrer) // $message = 0; @@ -597,6 +609,13 @@ switch (PAGE_NAME) { if ($session_expired_location == 'compose') $message = 1; } + + // was bad referrer the reason we were rejected? + // + if (sqsession_is_registered('user_is_logged_in') + && $check_referrer && !empty($referrer)) + $message = 2; + // signout page will deal with users who aren't logged // in on its own; don't show error here // @@ -622,8 +641,10 @@ switch (PAGE_NAME) { set_up_language($squirrelmail_language, true); if (!$message) logout_error( _("You must be logged in to access this page.") ); - else + else if ($message == 1) logout_error( _("Your session has expired, but will be resumed after logging in again.") ); + else if ($message == 2) + logout_error( _("The current page request appears to have originated from an unrecognized source.") ); exit; } -- 2.25.1