Skip to content

Commit

Permalink
Prevent redirection loop everytime we rely on HTTP_REFERER:
Browse files Browse the repository at this point in the history
  * search tag
  * delete tag
  * pagination
  * display privates only
  * delete link
  * new/edit/cancel link return page

Move location generation to Utils.php + unit tests.

Fixes #256

ninja
  • Loading branch information
ArthurHoaro committed Jul 12, 2015
1 parent 7f1dfd1 commit 775803a
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 44 deletions.
34 changes: 33 additions & 1 deletion application/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,36 @@ function checkDateFormat($format, $string)
$date = DateTime::createFromFormat($format, $string);
return $date && $date->format($string) == $string;
}
?>

/**
* Generate a header location from HTTP_REFERER.
* Make sure the referer is Shaarli itself and prevent redirection loop.
*
* @param string $referer - HTTP_REFERER.
* @param string $host - Server HOST.
* @param array $loopTerms - Contains list of term to prevent redirection loop.
*
* @return string $referer - final referer.
*/
function generateLocation($referer, $host, $loopTerms = array())
{
$final_referer = '?';

// No referer if it contains any value in $loopCriteria.
foreach ($loopTerms as $value) {
if (strpos($referer, $value) !== false) {
return $final_referer;
}
}

// Remove port from HTTP_HOST
if ($pos = strpos($host, ':')) {
$host = substr($host, 0, $pos);
}

if (!empty($referer) && strpos(parse_url($referer, PHP_URL_HOST), $host) !== false) {
$final_referer = $referer;
}

return $final_referer;
}
91 changes: 49 additions & 42 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,11 @@ function renderPage()
if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?searchtags='.urlencode($_GET['addtag'])); exit; } // In case browser does not send HTTP_REFERER
parse_str(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_QUERY), $params);

// Prevent redirection loop
if (isset($params['addtag'])) {
unset($params['addtag']);
}

// Check if this tag is already in the search query and ignore it if it is.
// Each tag is always separated by a space
$current_tags = explode(' ', $params['searchtags']);
Expand All @@ -1123,50 +1128,54 @@ function renderPage()
}

// -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
if (isset($_GET['removetag']))
{
if (isset($_GET['removetag'])) {
// Get previous URL (http_referer) and remove the tag from the searchtags parameters in query.
if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?'); exit; } // In case browser does not send HTTP_REFERER
parse_str(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_QUERY), $params);
if (isset($params['searchtags']))
{
if (empty($_SERVER['HTTP_REFERER'])) {
header('Location: ?');
exit;
}

// In case browser does not send HTTP_REFERER
parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);

// Prevent redirection loop
if (isset($params['removetag'])) {
unset($params['removetag']);
}

if (isset($params['searchtags'])) {
$tags = explode(' ',$params['searchtags']);
$tags=array_diff($tags, array($_GET['removetag'])); // Remove value from array $tags.
if (count($tags)==0) unset($params['searchtags']); else $params['searchtags'] = implode(' ',$tags);
if (count($tags)==0) {
unset($params['searchtags']);
} else {
$params['searchtags'] = implode(' ',$tags);
}
unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different)
}
header('Location: ?'.http_build_query($params));
exit;
}

// -------- User wants to change the number of links per page (linksperpage=...)
if (isset($_GET['linksperpage']))
{
if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); }
// Make sure the referrer is Shaarli itself.
$referer = '?';
if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
$referer = $_SERVER['HTTP_REFERER'];
header('Location: '.$referer);
if (isset($_GET['linksperpage'])) {
if (is_numeric($_GET['linksperpage'])) {
$_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage']));
}

header('Location: '. generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('linksperpage')));
exit;
}

// -------- User wants to see only private links (toggle)
if (isset($_GET['privateonly']))
{
if (empty($_SESSION['privateonly']))
{
$_SESSION['privateonly']=1; // See only private links
}
else
{
if (isset($_GET['privateonly'])) {
if (empty($_SESSION['privateonly'])) {
$_SESSION['privateonly'] = 1; // See only private links
} else {
unset($_SESSION['privateonly']); // See all links
}
// Make sure the referrer is Shaarli itself.
$referer = '?';
if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
$referer = $_SERVER['HTTP_REFERER'];
header('Location: '.$referer);

header('Location: '. generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly')));
exit;
}

Expand Down Expand Up @@ -1349,10 +1358,10 @@ function renderPage()

// If we are called from the bookmarklet, we must close the popup:
if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; }
$returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
$returnurl .= '#'.smallHash($linkdate); // Scroll to the link which has been edited.
if (strstr($returnurl, "do=addlink")) { $returnurl = '?'; } //if we come from ?do=addlink, set returnurl to homepage instead
header('Location: '.$returnurl); // After saving the link, redirect to the page the user was on.
$returnurl = ( !empty($_POST['returnurl']) ? escape($_POST['returnurl']) : '?' );
$returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited.
$location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
header('Location: '. $location); // After saving the link, redirect to the page the user was on.
exit;
}

Expand All @@ -1363,6 +1372,7 @@ function renderPage()
if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; }
$returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
$returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited.
$returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
header('Location: '.$returnurl); // After canceling, redirect to the page the user was on.
exit;
}
Expand Down Expand Up @@ -1395,18 +1405,15 @@ function renderPage()
// redirect is not satisfied, and only then redirect to /
$location = "?";
// Self redirection
if (count($_GET) == 0 ||
isset($_GET['page']) ||
isset($_GET['searchterm']) ||
isset($_GET['searchtags'])) {

if (count($_GET) == 0
|| isset($_GET['page'])
|| isset($_GET['searchterm'])
|| isset($_GET['searchtags'])
) {
if (isset($_POST['returnurl'])) {
$location = $_POST['returnurl']; // Handle redirects given by the form
}

if ($location === "?" &&
isset($_SERVER['HTTP_REFERER'])) { // Handle HTTP_REFERER in case we're not coming from the same place.
$location = $_SERVER['HTTP_REFERER'];
} else {
$location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('delete_link'));
}
}

Expand Down
27 changes: 26 additions & 1 deletion tests/UtilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,30 @@ public function testCheckInvalidDateFormat()
$this->assertFalse(checkDateFormat('Y-m-d', '2015-06'));
$this->assertFalse(checkDateFormat('Ymd', 'DeLorean'));
}

/**
* Test generate location with valid data.
*/
public function testGenerateLocation() {
$ref = 'http://localhost/?test';
$this->assertEquals($ref, generateLocation($ref, 'localhost'));
$ref = 'http://localhost:8080/?test';
$this->assertEquals($ref, generateLocation($ref, 'localhost:8080'));
}

/**
* Test generate location - anti loop.
*/
public function testGenerateLocationLoop() {
$ref = 'http://localhost/?test';
$this->assertEquals('?', generateLocation($ref, 'localhost', ['test']));
}

/**
* Test generate location - from other domain.
*/
public function testGenerateLocationOut() {
$ref = 'http://somewebsite.com/?test';
$this->assertEquals('?', generateLocation($ref, 'localhost'));
}
}
?>

0 comments on commit 775803a

Please sign in to comment.