Part 3: PHP Security Mini Guide – XSS and Password Storage

Cross-Site Scripting (XSS)

Cross-site Scripting or XSS is a vulnerability in which client-side code is injected into the output of a web application and executed in the user’s browser. The impact of successful exploitation varies from redirecting to malicious websites to stealing credentials, cookies and CSRF tokens. It is one of the most common vulnerabilities found in web applications.

Next is an example of vulnerable PHP code. User input ($_GET), is being outputted unescaped which leads to the simplest form of reflected XSS:

Insecure code sample:

$search = $_GET['search'];
echo 'You have searched for: '.$search;

If instead of a search term we pass a small Javascript code <script>alert("xss");</script>, it gets executed as soon the page is rendered:

PHP Security

Sometimes we see a blend of inline PHP and Javascript code which can also be easily exploited:

Insecure code sample:

<script type="text/javascript"> 
var term = <?php echo $_GET['search']; ?> 

In this example the <script> tag is not needed since the argument will be injected directly into Javascript.

Cross site scripting can be exploited in many ways which means that blacklisting is not a good idea. The recommended solution is to escape data before output. When escaping (encoding), HTML characters are converted into HTML entities which means that the browser will not interpret them as code but as data. PHP has two functions which can be used for escaping data, htmlspecialchars() and htmlentities().

htmlspecialchars(): This function converts the following special characters ,,&,<,> to &, , , < and >.

htmlentities(): This function is very similar to htmlspecialchars() with the only difference being that it encodes ALL characters which have HTML entity equivalents.

As of PHP 5.6, “UTF-8” is the default character set for both functions.

Secure code sample:

$search = $_GET['search'];
echo 'You have searched for: '.htmlspecialchars($search, ENT_QUOTES , 'UTF-8');

// Alternatively we can use the filter_var() function:
echo 'You have searched for: '.filter_var($search, FILTER_SANITIZE_FULL_SPECIAL_CHARS);

Using either function, results in the ‘malicious’ code being parsed by the browser as text:

PHP security

In the source code we can see the HTML encoded characters which prevent the script from executing:

PHP Security

Password Storage

Passwords are meant to be private/secret, thus securely storing them is a very important aspect of a web application. This adds an extra layer of security to avoid breach of confidentiality in case of an attack. In the event of an attacker exploiting an SQL Injection vulnerability on a web application and dumping the database, having a “strong” password hash will make their job harder (to crack it), thus protecting from further exploitation.

Two of the most important factors in password storage (other than having a strong password) is the hashing algorithm being used as well as the use of Salt.

Salt is a string added to a password before it is hashed. If the salt is long and random, then it makes some common cracking methods such as Dictionary Attacks and Lookup tables, ineffective.

One of the most widely used and fastest hashing algorithms is MD5. It is also one of the fastest to crack. Even though MD5 is weak, people still use it to date for password hashing:

Insecure code sample:

$password = 'j0hnny140';
$hash = md5($password);
Result: 3451f3e47aaa22beee5af1ebf5ae6828

Instead of md5(), you should use the password_hash() function. This function by default currently produces a 60-character string based on the CRYPT_BLOWFISH algorithm (BCRYPT) and provides strong hashes. Also, as of PHP 7.0.0 the option to pass a custom Salt has been deprecated and the default generated Salt is being preferred/used.

The function accepts 3 parameters. The password to hash, the hashing algorithm and additional options such as Cost. In the following example we will use the default hashing algorithm:

Secure code sample: Hash Creation

$password = 'j0hNny$140!';
$hash = password_hash($password, PASSWORD_DEFAULT);
Result:  $2y$10$VNA4syJiHTI6XXQVQCpZX.amjcHOjOZxIUImLafsSxedGZkumFVqC

A hash can be verified with the password_verify() function.

Secure code sample: Hash Verification

$password = 'j0hNny$140!';
$hash = '$2y$10$VNA4syJiHTI6XXQVQCpZX.amjcHOjOZxIUImLafsSxedGZkumFVqC';
if(password_verify($password, $hash)) {
	// Do action if the password is successfully verified


  • Do not use weak hashing algorithms such as MD5 and SHA1
  • Do not use (or reuse) weak salts (let password_hash() set the salts)
  • Do not create your own crypto or hashing functions
  • Do not store encrypted passwords as an attacker might get access to the private key
  • By increasing the “cost” value (work factor) in the password_hash() function you can get much stronger hashes (setting the value too high can affect server performance)

For more information on password_hash() click here.