Information Security 7 min read

Preventing XSS Attacks in PHP: Best Practices and Code Examples

This article explains various methods to prevent XSS injection in PHP, covering the limitations of built‑in filters, proper use of htmlspecialchars and htmlentities, replacement techniques, and provides comprehensive PHP functions with code examples for sanitizing user input and removing malicious scripts.

Laravel Tech Community
Laravel Tech Community
Laravel Tech Community
Preventing XSS Attacks in PHP: Best Practices and Code Examples

Websites can be attacked via XSS in many ways, and relying solely on PHP built‑in filtering functions such as filter_var, mysql_real_escape_string, htmlentities, htmlspecialchars, or strip_tags does not guarantee safety.

To prevent XSS, the article suggests treating all user input as potentially malicious, ensuring type consistency in weakly‑typed languages, using thorough regular expressions, employing strip_tags and htmlspecialchars, not trusting external JavaScript, paying special attention to quote filtering, and removing unnecessary HTML comments.

Method 1 – Using htmlspecialchars

Use htmlspecialchars($string, ENT_QUOTES) to encode both single and double quotes; ENT_NOQUOTES can be used when no quoting is needed. The article advises preferring htmlspecialchars over htmlentities for Chinese text because htmlentities also converts unrecognizable characters.

All output functions such as echo or print should be filtered with htmlspecialchars (or htmlentities with appropriate charset) before rendering.

Method 2 – Replacement Technique

function xss_clean($data){
 // Fix &entity\n;
 $data=str_replace(array('&','<','>'),array('&','<','>'),$data);
 $data=preg_replace('/(&#*\w+)[\x00-\x20]+;/u','$1;',$data);
 $data=preg_replace('/(&#x*[0-9A-F]+);*/iu','$1;',$data);
 $data=html_entity_decode($data,ENT_COMPAT,'UTF-8');
 // Remove any attribute starting with "on" or xmlns
 $data=preg_replace('#(<[^>]+?[\x00-\x20"\'\])(?:on|xmlns)[^>]*+>#iu','$1>',$data);
 // Remove javascript: and vbscript: protocols
 $data=preg_replace('#([a-z]*)[\x00-\x20]*=([`\'\"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu','$1=$2nojavascript...',$data);
 $data=preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu','$1=$2novbscript...',$data);
 $data=preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u','$1=$2nomozbinding...',$data);
 // Remove IE expression() style
 $data=preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'\"]*.*?expression[\x00-\x20]*\([^>]*+>#i','$1>',$data);
 $data=preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'\"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i','$1>',$data);
 $data=preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'\"]*.*?script[\x00-\x20]*:*[^>]*+>#iu','$1>',$data);
 // Remove namespaced elements
 $data=preg_replace('#</?\w+:\w[^>]*+>#i','',$data);
 do{
   $old_data=$data;
   $data=preg_replace('#</?(applet|base|gsound|link|embed|frame|frameset|iframe|layer|link|meta|object|script|style|title|xml)[^>]*+>#i','',$data);
 }while($old_data!==$data);
 return $data;
}
</code>
<p><strong>Method 3 – Global Filtering Function</strong></p>
<code>
<?php
// PHP universal filter for injection and XSS
$_GET && SafeFilter($_GET);
$_POST && SafeFilter($_POST);
$_COOKIE && SafeFilter($_COOKIE);

function SafeFilter(&$arr){
    $ra = array(
        '/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/',
        '/script/', '/javascript/', '/vbscript/', '/expression/', '/applet/', '/meta/', '/xml/',
        '/blink/', '/link/', '/style/', '/embed/', '/object/', '/frame/', '/layer/', '/title/',
        '/bgsound/', '/base/', '/onload/', '/onunload/', '/onchange/', '/onsubmit/', '/onreset/',
        '/onselect/', '/onblur/', '/onfocus/', '/onabort/', '/onkeydown/', '/onkeypress/', '/onkeyup/',
        '/onclick/', '/ondblclick/', '/onmousedown/', '/onmousemove/', '/onmouseout/', '/onmouseover/',
        '/onmouseup/', '/onunload/'
    );
    if(is_array($arr)){
        foreach($arr as $key=>$value){
            if(!is_array($value)){
                if(!get_magic_quotes_gpc()){
                    $value = addslashes($value);
                }
                $value = preg_replace($ra,'',$value);
                $arr[$key] = htmlentities(strip_tags($value));
            }else{
                SafeFilter($arr[$key]);
            }
        }
    }
}
?>
</code>
<p>The article concludes by encouraging readers to like and share if they found the content helpful.</p>
phpXSSCode Exampleweb securityhtmlspecialcharsInput Sanitization
Laravel Tech Community
Written by

Laravel Tech Community

Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.