1
votes

Scenario:

I would like to replace: " ' < > with &quot; &#039; &lt; &gt; but keep the "&" character.

The string I'm dealing with is a URL and I want URL parameters to be separated by &, not &amp;.

Example Solution:

$url = "/some/path?a=123&b=456"; // from $_SERVER["REQUEST_URI"]; 
$url = htmlspecialchars($url, ENT_QUOTES, 'ISO-8859-1', true); 
$url = str_replace('&amp;','&',$url);

Question:

If I use $url on my page (e.g. echo $url; inside HTML or JavaScript) can this be exploited by XSS?

Similar Questions:

There are other posts on SO covering XSS & htmlspecialchars() but I can't find an answer around whether the "&" character (and the htmlentities it may allow) can expose you to XSS.

2
Is the URL acquired from user data? - Explosion Pills
In my case URL is taken from $_SERVER["REQUEST_URI"]; ... basically anything entered into the browser address bar. - Chris Jacob

2 Answers

1
votes

Sijmen Ruwhof made this interesting point that I feel is relevant:

http://www.php.net/manual/en/function.htmlentities.php#99896

The 'ENT_QUOTES' option doesn't protect you against javascript evaluation in certain tag's attributes, like the 'href' attribute of the 'a' tag. When clicked on the link below, the given JavaScript will get executed:

<?php
$_GET['a'] = 'javascript:alert(document.cookie)';
$href = htmlEntities($_GET['a'], ENT_QUOTES);
print "<a href='$href'>link</a>"; # results in: <a href='javascript:alert(document.cookie)'>link</a>
?>
0
votes

I once used really weird code for escaping urls, but I bet this can be done better, furthermore this does not cover the html specialchars yet, it was just used for saving urls to the database.

function escapeUrl(&$url){
 $matches = parse_url($url);
    if(!isset($matches["host"])){
        Utils_Logging_Logger::getLogger()->log(
            "Unknown host for URL: \"$url\".",
            Utils_Logging_Logger::TYPE_ERROR
       );
       $matches["host"] = "";
    }
    if(!isset($matches["scheme"])){
        Utils_Logging_Logger::getLogger()->log(
            "Sheme (like http://) for URL: \"$url\" was not set.",
            Utils_Logging_Logger::TYPE_LOG);
        $url = "http://";
    }else{$url = $matches["scheme"]."://";}
    $url.=$matches["host"];
    if(isset($matches["path"])){
        $path = rawurldecode($matches["path"]);
        $url.=$path;   
    }
    if(isset($matches["query"])){
        $query = rawurldecode($matches["query"]);
        $url.="?".$query;
    }
    return $url;

}