0

We've recently run our project through a security analyzer, and found areas in code where input needs to be escaped. For this question, I'm specifically referring to Cross Site Scripting attacks. The analyzer suggested sanitizing the input with htmlentities and json_encode, however I'm not convinced that json_encode is the right tool for the job. And I'm completely convinced after doing some research that it was never intended to be used this way and any usefulness in this regard is merely a side affect. Below is my function for escaping potential XSS attacks:

function escapeScriptTags($value)
{
    return json_encode(htmlentities($value), JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS);
}

The issue I'm having is that the returned string has double quotes concatenated and appended to it. This distorts the user's input and causes obvious problems. One workaround I've found is:

function escapeScriptTags($value)
{
    return trim(json_encode(htmlentities($value), JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS), '"');
}

Where because the quotes are being escaped to \u0022, any " character removed would be those added by json_encode.

But as I've said, I'm still not convinced that this is the correct methodology to protect against XSS attacks in PHP. Is there a more explicit way of escaping user input to protect against XSS attacks that does not change user input?

EDIT

In this particular instance writing the value of the input to HTML, but there are also cases where I'm writing to JavaScript. My issue may be that there isn't a one-size-fits-all approach to XSS protection.

EDIT2

So it does appear that there is not a one-size-fits-all approach. For completeness and for anyone who needs guidance on this, here are my input sanitizing functions--including a SQL Injection protection function. Any critiques or suggestions are very welcome:

/**
 * Escapes user input that is to be interpolated into JavaScript code to
 * protect against Cross-Site Scripting (XSS) attacks.
 * 
 * @param $value
 * @return string
 */
function encodeJavaScriptString($value)
{
    return json_encode($value, JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS);
}

/**
 * Escapes HTML tags to protect against Cross-Site Scripting (XSS) attacks.
 *
 * @param $value
 * @return string
 */
function escapeScriptTags($value)
{
    return htmlentities($value, ENT_QUOTES, 'UTF-8');
}

/**
 * Escapes a string in order protect against a SQL injection attack. If `$like` is true,
 * underscores and percent signs are also escaped for use in a`LIKE` clause.
 *
 * @param $value
 * @param \mysqli $mysqli
 * @param bool $like
 * @return string
 */
function escapeSqlString($value, \mysqli $mysqli, $like = false)
{
    return $like ? addcslashes($mysqli->real_escape_string($value), '%_')
        : $mysqli->real_escape_string($value);
}
Allie Fitter
  • 1,689
  • 14
  • 18
  • We need more information to help you. Are you outputting data into HTML? In that case, use `htmlspecialchars()`. Are you outputting data into JavaScript? In that case, use `json_encode()`. – Brad Feb 06 '18 at 19:03

2 Answers2

4

The encoding to use depends on the output format, so you should use either json_encode or htmlentities depending on the context. This means that the escaping needs to be done on output rather than input. Then any characters added or modified don't affect the rest of the system.

When writing to a JavaScript variable, then the double quotes become part of the JavaScript string syntax, and don't need to be removed:

<script>
    console.log(JSON.parse(<?= json_encode($value, JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS) ?>));
</script>

When writing to html, then use htmlentities only:

<div>
    <?= htmlentities($value); ?>
</div>
fgb
  • 18,439
  • 2
  • 38
  • 52
2

There are many approaches to address the XSS vulnerability.

If any vulnerable script got saved in the database, make sure it is not getting executed at frontend by sanitizing it while displaying, use below function to sanitize the output:

htmlspecialchars($string, ENT_QUOTES, 'UTF-8');

You can add HTTP X-XSS-Protection response header which is a feature of Internet Explorer, Chrome, and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks.

X-XSS-Protection "1; mode=block"