The value of the onclick attribute should be escaped like any other HTML attribute, using htmlspecialchars(). Actual Javascript strings inside the code should be encoded using json_encode(). For example:
<?php
$message = 'Some \' problematic \\ chars " ...';
$jscode = 'alert('.json_encode($message).');';
echo '<a onclick="' . htmlspecialchars($jscode) . '">Click me</a>';
That being said... onclick (or any other event) attributes are so 2005. Do yourself a favor and separate your javascript code from your html code, preferably to external file, and attach the events using DOM functions (or jQuery, which wraps it up nicely)
Answer from shesek on Stack Overflowjavascript - JS inside PHP Escape String (for functions) - Stack Overflow
php - Preventing XSS attacks with proper escaping - Code Review Stack Exchange
Pass a PHP variable to a JavaScript variable - Stack Overflow
PHP equivalent for javascript escape/unescape - Stack Overflow
The value of the onclick attribute should be escaped like any other HTML attribute, using htmlspecialchars(). Actual Javascript strings inside the code should be encoded using json_encode(). For example:
<?php
$message = 'Some \' problematic \\ chars " ...';
$jscode = 'alert('.json_encode($message).');';
echo '<a onclick="' . htmlspecialchars($jscode) . '">Click me</a>';
That being said... onclick (or any other event) attributes are so 2005. Do yourself a favor and separate your javascript code from your html code, preferably to external file, and attach the events using DOM functions (or jQuery, which wraps it up nicely)
I'm really just re-wording what @Marshall House says here, but:
In HTML, a double quote (") will always end an attribute, regardless of a backslash - so it sees: onclick="var a = prompt('New value: ', 'aaaa\". The solution that @Marshall offers is to separate your code out into a function. This way you can print escaped PHP into it without a problem.
E.g.:
<script>
// This is a function, wrapping your code to be called onclick.
function doOnClickStuff() {
// You should no longer need to escape your string. E.g.:
//var a = prompt('new value:','<?php echo
i]; ?>');
// Although the following could be safer
var a = prompt('new value:',<?php json_encode(
i]); ?>);
if (a) { <!--javascript code--> }
else { <!--javascript code--> }
}
</script>
<someelement onclick="doOnClickStuff();"> <!-- this calls the javascript function doOnClickStuff, defined above -->
Same as always: encode as JSON.
echo '<a onClick="myFunctionTakesPHPValues('.json_encode($element[0]).','.json_encode($element[1]).')">'.$element[2].'</a>';
- Never echo JS from PHP. Escape from PHP mode instead, it will save you a lot of slashes and nerves.
- Every value have to be escaped properly, as explained in this article
So, for the JS values you have to escape them with json_encode() and, as they are going into HTML attribute, escape them as HTML too.
For the last element only HTML encoding is required.
foreach ($array as $element)
{
$param1 = htmlspecialchars(json_encode($element[0])); // better give them
$param2 = htmlspecialchars(json_encode($element[1])); // meaningful names
$param3 = htmlspecialchars($element[2]);
?>
<a onClick="myFunctionTakesPHPValues(<?=$param1?>,<?=$param2?>)">
<?=$param3?>
</a>
<? }
And yes, using raw JS in HTML attributes considered as a bad practice.
There are already native functions to escape for HTML and JS strings: htmlspecialchars() and json_encode(). See this related question on Stack Overflow
As for innerHTML, simply don't use it. Use textContent instead. If you wish to allow for formatting (for example, in comments or posts), I recommend Markdown
My other answer explains the general best practice, let's go over your code.
You can pass an array to
str_replace()so that it replaces every occurence of matching substrings with their replacement array counterparts with the same index:return str_replace(["/", "\n"], ["\\/", "\\n"], $subject)You aren't escaping
;for JavaScript strings, that can be used to break out of the context.You shouldn't really care about JavaScript replacement. Data comes from the server, that's where all escaping should be.
ES5 has the
Array.isArray()static method to check if a given parameter is an array. For strings,typeof strwill returnstring. So your getType function is a bit redundant:function isString(str) { return typeof str === 'string'; } function isArray(arr) { return Array.isArray(arr); }Also, ES5 has
Array.prototype.forEachfor iterating over an array, and is considered a better alternative tofor.
Expanding on someone else's answer:
<script>
var myvar = <?= json_encode($myVarValue, JSON_UNESCAPED_UNICODE); ?>;
</script>
Using json_encode() requires:
- PHP 5.2.0 or greater
$myVarValueencoded as UTF-8 (or US-ASCII, of course)
Since UTF-8 supports full Unicode, it should be safe to convert on the fly.
Please note that if you use this in html attributes like onclick, you need to pass the result of json_encode to htmlspecialchars(), like the following:
htmlspecialchars(json_encode($string), ENT_QUOTES);
or else you could get problems with, for example, &bar; in foo()&&bar; being interpreted as an HTML entity.
encode it with JSON
I have a website that lets the user build a dynamic form (think tabs with widgets) and then reduce the whole thing to a string with json.stringify().
But when I pass it to PHP (on one of our two systems to make things weirder) it adds a mess of escape characters. For example:
{\\"tab_type\\":\\"accordion\\",\\"options\\"{\\"tab::style\\":\\"color:0x8000ff00\\", When the correct formatting should look like:
"type":"accordion","options":{"tab::style":"color:0x8000ff00" The json is stringified properly on the JS side but it adds the slashes as soon as I look at the variable in the GET/POST on the PHP side.
The best way to do this is to encode the values using json_encode. Here is a simple example:
<?php
$name = "Jason's Bakery";
?>
<script>
var name = <?php echo json_encode($name); ?>;
DoSomethingWithName(name);
</script>
This can be used for integers, strings, and other values. Keep in mind that it will add quotes as needed, so you need to assemble and encode a "whole value at once". In your example of using the URLs, you need to use the PHP urlencode() function to encode them FIRST, and then pass it through json_encode to convert to a javascript value. And if you are placing that inside of an HTML attribute, like onclick, you need to further pass it through htmlspecialchars(..., ENT_QUOTES) and then place it in double quotes.
http://php.net/manual/en/function.json-encode.php
So for example, you need to build a URL in PHP and then use it in javascript...
<?php
$name = "Jason's \"Awesome\" Bakery";
$url = "http://site.com/page.php?name=" . urlencode($name);
?>
<script>
var name = <?php echo json_encode($name); ?>;
DoSomethingWithName(name);
</script>
<input type="button" onclick="<?php echo htmlspecialchars('window.location = ' . json_encode($url) . ';', ENT_QUOTES); ?>" value="Click Me" />
Which results in something like this:
<script>
var name = "Jason's \"Awesome\" Bakery";
DoSomethingWithName(name);
</script>
<input type="button" onclick="window.location = "http:\/\/site.com\/page.php?name=Jason%27s+%22Awesome%22+Bakery";" value="Click Me" />
Needless to say, you do not want to do without these:
http://php.net/manual/en/function.json-encode.php
http://www.php.net/manual/en/function.urlencode.php
http://www.php.net/manual/en/function.htmlspecialchars.php
Due to respects of readability and future maintainability, I'd like to point out a few things which may help you out.
First, I see you're generating HTML elements in a PHP string. This isn't inherently bad, but when your string wraps across 2 or more lines, it becomes increasingly difficult to manage. Instead, you may want to think about escaping PHP for outputting HTML portions, and re-entering PHP for logical portions. You can escape PHP and enter HTML within if statements, function declarations etc, so there's really no good reason not to. Look at the following example (this solution also escapes the strings in an appropriate manner where its value can contain single quotes, double quotes or line breaks):
<?php
function urlFriendly($input) {
return urlencode($input);
}
function jsFriendly($input, $urlFriendly = True) {
$output = htmlentities($input, ENT_QUOTES);
// Double quotes in PHP translate "\n" to a newline.
// Single quotes in PHP keep the literal value.
$output = str_replace("\r\n", '\n', $output); // Windows support
$output = str_replace("\n", '\n', $output); // Linux support
if($urlFriendly) { // Encode for use in URLs
$output = urlFriendly($output);
}
return $output;
}
$vrj_name = 'vrj';
$messageContent_vrj = 'message content';
$from_email = 'from email';
$email = 'email';
$subject = 'subject line';
?>
<script type="text/javascript">
function SelectRedirect() {
switch(document.getElementById('s1').value) {
case '?vrj_name=<?php print jsFriendly($vrj_name);?>':
var toloc = '?vrj_name=<?php print jsFriendly($vrj_name);?>';
toloc += '&messageContent=<?php print jsFriendly($messageContent_vrj);?>'';
toloc += '&from_email=<?php print jsFriendly($from_email);?>';
toloc += '&email=<?php print jsFriendly($email);?>';
toloc += '&subject=<?php print jsFriendly($subject);?>';
window.location = toloc;
break;
}
</script>