The document provides an overview of common PHP security issues across three main categories: PHP language issues, framework issues, and third-party code issues. It then discusses the OWASP Top 10 security risks and how they apply to PHP. The rest of the document offers tips to improve PHP security including input validation, file uploads, database queries, and preventing injections. It cautions against trusting user input and provides examples of insecure code.
3. PHP SECURITY
INTRO
▸ PHP is the most commonly used server-side programming language,
with 81.8% of web servers deploying it, according to W3 Techs.
▸ Like all web languages, there is also a large community of libraries etc.
that contribute to the security (or otherwise) of programming in PHP.
▸ All three aspects (language, framework, and libraries) need to be
taken into consideration when trying to secure a PHP site.
▸ PHP is a 'grown' language rather than deliberately engineered,
making writing insecure PHP applications far too easy and common.
▸ If you want to use PHP securely, then you should be aware of all its
pitfalls.
3
4. PHP SECURITY
OWASP TOP 10
▸ A1-Injection
▸ Injection flaws, such as SQL, OS, XXE, and LDAP injection occur when untrusted data is sent to
an interpreter as part of a command or query. The attacker’s hostile data can trick the
interpreter into executing unintended commands or accessing data without proper
authorization.
▸ A2-Broken Authentication and Session Management
▸ Application functions related to authentication and session management are often
implemented incorrectly, allowing attackers to compromise passwords, keys, or session tokens,
or to exploit other implementation flaws to assume other users’ identities (temporarily or
permanently).
▸ A3-Cross-Site Scripting (XSS)
▸ XSS flaws occur whenever an application includes untrusted data in a new web page without
proper validation or escaping, or updates an existing web page with user supplied data using a
browser API that can create JavaScript. XSS allows attackers to execute scripts in the victim’s
browser which can hijack user sessions, deface web sites, or redirect the user to malicious sites.
4
5. PHP SECURITY
OWASP TOP 10
▸ A4-Broken Access Control
▸ Restrictions on what authenticated users are allowed to do are not properly enforced. Attackers
can exploit these flaws to access unauthorized functionality and/or data, such as access other
users' accounts, view sensitive files, modify other users’ data, change access rights, etc.
▸ A5-Security Misconfiguration
▸ Good security requires having a secure configuration defined and deployed for the application,
frameworks, application server, web server, database server, platform, etc. Secure settings
should be defined, implemented, and maintained, as defaults are often insecure. Additionally,
software should be kept up to date.
▸ A6-Sensitive Data Exposure
▸ Many web applications and APIs do not properly protect sensitive data, such as financial,
healthcare, and PII. Attackers may steal or modify such weakly protected data to conduct credit
card fraud, identity theft, or other crimes. Sensitive data deserves extra protection such as
encryption at rest or in transit, as well as special precautions when exchanged with the browser.
5
6. PHP SECURITY
OWASP TOP 10
▸ A7-Insufficient Attack Protection
▸ The majority of applications and APIs lack the basic ability to detect,
prevent, and respond to both manual and automated attacks. Attack
protection goes far beyond basic input validation and involves automatically
detecting, logging, responding, and even blocking exploit attempts.
Application owners also need to be able to deploy patches quickly to
protect against attacks.
▸ A8-Cross-Site Request Forgery (CSRF)
▸ A CSRF attack forces a logged-on victim’s browser to send a forged HTTP
request, including the victim’s session cookie and any other automatically
included authentication information, to a vulnerable web application. Such
an attack allows the attacker to force a victim’s browser to generate requests
the vulnerable application thinks are legitimate requests from the victim.
6
7. PHP SECURITY
OWASP TOP 10
▸ A9-Using Components with Known Vulnerabilities
▸ Components, such as libraries, frameworks, and other software
modules, run with the same privileges as the application. If a
vulnerable component is exploited, such an attack can facilitate
serious data loss or server takeover. Applications and APIs using
components with known vulnerabilities may undermine application
defenses and enable various attacks and impacts.
▸ A10-Underprotected APIs
▸ Modern applications often involve rich client applications and APIs,
such as JavaScript in the browser and mobile apps, that connect to an
API of some kind (SOAP/XML, REST/JSON, RPC, GWT, etc.). These
APIs are often unprotected and contain numerous vulnerabilities.
7
8. PHP SECURITY
LANGUAGE ISSUES
▸ Weak typing
▸ PHP is weakly typed, which means that it will automatically
convert data of an incorrect type into the expected type. This
feature very often masks errors by the developer or injections
of unexpected data, leading to vulnerabilities.
▸ Try to use functions and operators that do not do implicit type
conversions (e.g. === and not ==). Not all operators have
strict versions (for example greater than and less than), and
many built-in functions (like in_array) use weakly typed
comparison functions by default, making it difficult to write
correct code.
8
9. PHP SECURITY
LANGUAGE ISSUES
▸ Exceptions and error handling
▸ Almost all PHP builtins, and many PHP libraries, do not use
exceptions, but instead report errors in other ways (such as
via notices) that allow the faulty code to carry on running.
▸ This has the effect of masking many bugs. In many other
languages, and most high level languages that compete
with PHP, error conditions that are caused by developer
errors, or runtime errors that the developer has failed to
anticipate, will cause the program to stop running, which is
the safest thing to do.
9
10. PHP SECURITY
LANGUAGE ISSUES
▸ php.ini
▸ The behaviour of PHP code often depends strongly on
the values of many configuration settings, including
fundamental changes to things like how errors are
handled. This can make it very difficult to write code that
works correctly in all circumstances. Different libraries
can have different expectations or requirements about
these settings, making it difficult to correctly use 3rd
party code.
10
11. PHP SECURITY
LANGUAGE ISSUES
▸ Unhelpful builtins
▸ PHP comes with many built-in functions, such as
addslashes, mysql_escape_string and
mysql_real_escape_string, that appear to provide
security, but are often buggy and, in fact, are unhelpful ways
to deal with security problems. Some of these builtins are
being deprecated and removed, but due to backwards
compatibility policies this takes a long time.
▸ PHP also provides an 'array' data structure, which is used
extensively in all PHP code and internally, that is a confusing
mix between an array and a dictionary.
11
12. PHP SECURITY
FRAMEWORK ISSUES
▸ URL routing
▸ PHP’s built-in URL routing mechanism is to use files ending in “.php” in the directory structure. This opens up several
vulnerabilities:
▸ Remote execution vulnerability for every file upload feature that does not sanitise the filename. (In other words,
the web server executes something instead of serving it). Ensure that when saving uploaded files, the content and
filename are appropriately sanitised.
▸ Source code, including config files, are stored in publicly accessible directories along with files that are meant to be
downloaded (such as static assets). Misconfiguration (or lack of configuration) can mean that source code or config
files that contain secret information can be downloaded by attackers. (In other words, the web server serves a
resource which should have been private or executable only). You can use .htaccess to limit access. This is not ideal,
because it is insecure by default, but there is no other alternative.
▸ The URL routing mechanism is the same as the module system. This means it is often possible for attackers to use
files as entry points which were not designed as such. This can open up vulnerabilities where authentication
mechanisms are bypassed entirely - a simple refactoring that pulls code out into a separate file can open a
vulnerability. This is made particularly easy in PHP because it has globally accessible request data ($_GET etc), so
file-level code can be imperative code that operates on the request, rather than needing request handling code to
be within function definitions.
▸ The lack of a proper URL routing mechanism often leads to developers creating their own ad-hoc methods. These
are often insecure and fail to apply appropriate authorization restrictions on different request handling functionality.
12
13. PHP SECURITY
FRAMEWORK ISSUES
▸ Input handling
▸ Instead of treating HTTP input as simple strings, PHP will build arrays from HTTP
input, at the control of the client. This can lead to confusion about data, and can
easily lead to security bugs.
▸ Template language
▸ PHP is essentially a template language. However, it doesn't do HTML escaping by
default, which makes it very problematic for use in a web application.
▸ Other inadequacies
▸ There are other important things that a web framework should supply, such as a
CSRF protection mechanism that is on by default. Because PHP comes with a
rudimentary web framework that is functional enough to allow people to create
web sites, many people will do so without any knowledge that they need CSRF
protection.
13
14. PHP SECURITY
THIRD PARTY PHP CODE
▸ Libraries and projects written in PHP are often insecure
due to the problems highlighted above, especially when
proper web frameworks are not used. Do not trust PHP
code that you find on the web, as many security
vulnerabilities can hide in seemingly innocent code.
▸ Poorly written PHP code often results in warnings being
emitted, which can cause problems. A common solution is
to turn off all notices, which is exactly the opposite of what
ought to be done, and leads to progressively worse code.
14
15. PHP SECURITY
TIPS & COMMON MISTAKES
▸ Update PHP regularly
▸ Check PHP configuration - The behaviour of PHP is strongly affected
by configuration, which can be done through the "php.ini" file, Apache
configuration directives and runtime mechanisms. There are many
security related configuration options.
▸ PHP code should be configured to run using a 'SetHandler' directive.
In many instances, it is wrongly configured using an 'AddHander'
directive. This works, but also makes other files executable as PHP
code - for example, a file name "foo.php.txt" will be handled as PHP
code, which can be a very serious remote execution vulnerability if
"foo.php.txt" was not intended to be executed or came from a
malicious file upload.
15
16. PHP SECURITY
TIPS & COMMON MISTAKES
▸ Untrusted data
▸ All data that is a product, or subproduct, of user input is to
NOT be trusted. They have to either be validated, using the
correct methodology, or filtered, before considering them
untainted.
▸ Super globals which are not to be trusted are $_SERVER,
$_GET, $_POST, $_REQUEST, $_FILES and $_COOKIE.
Not all data in $_SERVER can be faked by the user, but a
considerable amount in it can, particularly and specially
everything that deals with HTTP headers (they start with
HTTP_).
16
17. PHP SECURITY
TIPS & COMMON MISTAKES
▸ File uploads
▸ Files received from a user pose various security threats,
especially if other users can download these files. In particular:
▸ Any file served as HTML can be used to do an XSS attack
▸ Any file treated as PHP can be used to do an extremely
serious attack - a remote execution vulnerability.
▸ Since PHP is designed to make it very easy to execute PHP code
(just a file with the right extension), it is particularly important for
PHP sites (any site with PHP installed and configured) to ensure
that uploaded files are only saved with sanitised file names.
17
18. PHP SECURITY
TIPS & COMMON MISTAKES
▸ Do not use $_REQUEST
▸ Using $_REQUEST is strongly discouraged. This super
global is not recommended since it includes not only
POST and GET data, but also the cookies sent by the
request. All of this data is combined into one array,
making it almost impossible to determine the source of
the data. This can lead to confusion and makes your
code prone to mistakes, which could lead to security
problems.
18
19. PHP SECURITY
TIPS & COMMON MISTAKES - DATABASE
▸ Never concatenate or interpolate data in SQL
▸ Never build up a string of SQL that includes user data,
either by concatenation:
▸ $sql = "SELECT * FROM users WHERE
username = '" . $username . "';";
▸ or interpolation, which is essentially the same:
▸ $sql = "SELECT * FROM users WHERE
username = '$username';";
19
20. PHP SECURITY
TIPS & COMMON MISTAKES - DATABASE
▸ Escaping is not safe
▸ mysql_real_escape_string is not safe. Don't rely on
it for your SQL injection prevention.
▸ Use prepared statements
▸ Prepared statements are very secure. In a prepared
statement, data is separated from the SQL command, so
that everything user inputs is considered data and put
into the table the way it was.
20
21. PHP SECURITY
TIPS & COMMON MISTAKES - DATABASE
▸ ORMs (Object Relational Mappers) are good security practice.
▸ If you're using an ORM (like Doctrine) in your PHP project,
you're still prone to SQL attacks. Although injecting queries in
ORM's is much harder, keep in mind that concatenating ORM
queries makes for the same flaws that concatenating SQL
queries, so NEVER concatenate strings sent to a database.
ORM's support prepared statements as well.
▸ Use UTF-8 unless necessary
▸ Many new attack vectors rely on encoding bypassing. Use
UTF-8 as your database and application charset unless you
have a mandatory requirement to use another encoding.
21
22. PHP SECURITY
TIPS & COMMON MISTAKES - INJECTIONS
▸ Shell Injection
▸ A few PHP functions namely: shell_exec, exec, passthru, system,
backtick operator ( ` ) run a string as shell scripts and commands. Input
provided to these functions (specially backtick operator that is not like a
function). Depending on your configuration, shell script injection can cause
your application settings and configuration to leak, or your whole server to
be hijacked. This is a very dangerous injection and is somehow considered
the haven of an attacker.
▸ Never pass tainted input to these functions - that is input somehow
manipulated by the user - unless you're absolutely sure there's no way for it
to be dangerous (which you never are without whitelisting). Escaping and
any other countermeasures are ineffective, there are plenty of vectors for
bypassing each and every one of them; don't believe what novice
developers tell you.
22
23. PHP SECURITY
TIPS & COMMON MISTAKES - INJECTIONS
▸ Code Injection
▸ All interpreted languages such as PHP, have some
function that accepts a string and runs that in that
language. In PHP this function is named eval(). Using
eval is a very bad practice, not just for security. If you're
absolutely sure you have no other way but eval, use it
without any tainted input. Eval is usually also slower.
▸ Function preg_replace() should not be used with
unsanitised user input, because the payload will be
eval()'ed
23
24. PHP SECURITY
TIPS & COMMON MISTAKES - INJECTIONS
▸ Other Injections
▸ LDAP, XPath and any other third party application that
runs a string, is vulnerable to injection.
▸ Always keep in mind that some strings are not data, but
commands and thus should be secure before passing to
third party libraries.
24
25. PHP SECURITY
TIPS & COMMON MISTAKES - XSS
▸ No Tags
▸ Most of the time, there is no need for user supplied data to contain unescaped HTML tags when
output. For example when you're about to dump a textbox value, or output user data in a cell.
▸ If you are using standard PHP for templating, or echo etc., then you can mitigate XSS in this case
by applying htmlspecialchars to the data, or the following function (which is essentially a
more convenient wrapper around htmlspecialchars). However, this is not recommended.
The problem is that you have to remember to apply it every time, and if you forget once, you have
an XSS vulnerability. Methodologies that are insecure by default must be treated as insecure.
▸ Instead of this, you should use a template engine that applies HTML escaping by default. All
HTML should be passed out through the template engine.
▸ If you cannot switch to a secure template engine, you can use the function below on all untrusted
data.
▸ Keep in mind that this scenario won't mitigate XSS when you use user input in dangerous
elements (style, script, image's src, a, etc.), but mostly you don't. Also keep in mind that every
output that is not intended to contain HTML tags should be sent to the browser filtered with the
following function.
25
26. PHP SECURITY
TIPS & COMMON MISTAKES - XSS
▸ Untrusted Tags
▸ When you need to allow users to supply HTML tags that are used in
your output, such as rich blog comments, forum posts, blog posts
and etc., but cannot trust the user, you have to use a Secure
Encoding library. This is usually hard and slow, and that's why most
applications have XSS vulnerabilities in them. OWASP ESAPI has a
bunch of codecs for encoding different sections of data. There's also
OWASP AntiSammy and HTMLPurifier for PHP. Each of these require
lots of configuration and learning to perform well, but you need
them when you want that good of an application.
26
27. PHP SECURITY
TIPS & COMMON MISTAKES - XSS
▸ Other tips
▸ Don't have a trusted section in any web application. Many developers tend to leave admin areas
out of XSS mitigation, but most intruders are interested in admin cookies and XSS. Every output
should be cleared by the functions provided above, if it has a variable in it. Remove every instance
of echo, print, and printf from your application and replace them with a secure template engine.
▸ HTTP-Only cookies are a very good practice, for a near future when every browser is compatible.
Start using them now. (See PHP.ini configuration for best practice)
▸ The function declared above, only works for valid HTML syntax. If you put your Element Attributes
without quotation, you're doomed. Go for valid HTML.
▸ Reflected XSS is as dangerous as normal XSS, and usually comes at the most dusty corners of an
application. Seek it and mitigate it.
▸ Not every PHP installation has a working mhash extension, so if you need to do hashing, check it
before using it. Otherwise you can't do SHA-256
▸ Not every PHP installation has a working mcrypt extension, and without it you can't do AES. Do
check if you need it.
27
28. PHP SECURITY
TIPS & COMMON MISTAKES - CSRF
▸ CSRF mitigation is easy in theory, but hard to
implement correctly.
▸ First, a few tips about CSRF:
▸ Every request that does something noteworthy,
should be CSRF mitigated. Noteworthy things are
changes to the system, and reads that take a long
time.
▸ CSRF mostly happens on GET, but is easy to happen
on POST. Don't ever think that post is secure.
28
29. PHP SECURITY
TIPS & COMMON MISTAKES - CSRF
▸ For now, mix that with the following tips:
▸ Use re-authentication for critical operations (change password, recovery
email, etc.)
▸ If you're not sure whether your operation is CSRF proof, consider adding
CAPTCHAs (however CAPTCHAs are inconvenience for users)
▸ If you're performing operations based on other parts of a request (neither
GET nor POST) e.g Cookies or HTTP Headers, you might need to add CSRF
tokens there as well.
▸ AJAX powered forms need to re-create their CSRF tokens. Use the function
provided above (in code snippet) for that and never rely on Javascript.
▸ CSRF on GET or Cookies will lead to inconvenience, consider your design
and architecture for best practices.
29
30. PHP SECURITY
TIPS & COMMON MISTAKES - AUTHENTICATION AND SESSION MANAGEMENT
▸ Session Management
▸ PHP's default session facilities are considered safe, the generated
PHPSessionID is random enough, but the storage is not necessarily safe:
▸ Session files are stored in temp (/tmp) folder and are world writable
unless suPHP installed, so any LFI or other leak might end-up
manipulating them.
▸ Sessions are stored in files in default configuration, which is terribly slow
for highly visited websites. You can store them on a memory folder (if
UNIX).
▸ You can implement your own session mechanism, without ever relying on
PHP for it. If you did that, store session data in a database. You could use
all, some or none of the PHP functionality for session handling if you go
with that.
30
31. PHP SECURITY
TIPS & COMMON MISTAKES - AUTHENTICATION AND SESSION MANAGEMENT
▸ Session Hijacking Prevention
▸ It is good practice to bind sessions to IP addresses, that
would prevent most session hijacking scenarios (but not
all), however some users might use anonymity tools (such
as TOR) and they would have problems with your service.
▸ Invalidate Session ID
▸ You should invalidate (unset cookie, unset session
storage, remove traces) of a session whenever a violation
occurs (e.g 2 IP addresses are observed).
31
32. PHP SECURITY
TIPS & COMMON MISTAKES - AUTHENTICATION AND SESSION MANAGEMENT
▸ Rolling of Session ID
▸ You should roll session ID whenever elevation occurs, e.g when a user logs in, the session ID of the session
should be changed, since it's importance is changed.
▸ Exposed Session ID
▸ Session IDs are considered confidential, your application should not expose them anywhere (specially
when bound to a logged in user). Try not to use URLs as session ID medium.
▸ Transfer session ID over TLS whenever session holds confidential information, otherwise a passive attacker
would be able to perform session hijacking.
▸ Session Fixation
▸ Invalidate the Session id after user login (or even after each request) with session_regenerate_id().
▸ Session Expiration
▸ A session should expire after a certain amount of inactivity, and after a certain time of activity as well. The
expiration process means invalidating and removing a session, and creating a new one when another
request is met.
▸ Also keep the log out button close, and unset all traces of the session on log out.
32
33. PHP SECURITY
TIPS & COMMON MISTAKES - AUTHENTICATION AND SESSION MANAGEMENT
▸ Cookies
▸ Handling cookies in a PHP script has some tricks to it:
▸ Never Serialize - Never serialize data stored in a cookie. It can easily be manipulated, resulting
in adding variables to your scope.
▸ Proper Deletion - to delete a cookie safely, use the following snippet:
▸ setcookie ($name, "", 1); setcookie ($name, false);
unset($_COOKIE[$name]);
▸ The first line ensures that cookie expires in browser, the second line is the standard way of
removing a cookie (thus you can't store false in a cookie). The third line removes the cookie
from your script. Many guides tell developers to use time() - 3600 for expiry, but it might not
work if browser time is not correct.
▸ You can also use session_name() to retrieve the name default PHP session cookie.
▸ HTTP Only - most modern browsers support HTTP-only cookies. These cookies are only
accessible via HTTP(s) requests and not JavaScript, so XSS snippets can not access them. They
are very good practice, but are not satisfactory since there are many flaws discovered in major
browsers that lead to exposure of HTTP only cookies to JavaScript.
33
34. PHP SECURITY
TIPS & COMMON MISTAKES - AUTHENTICATION AND SESSION MANAGEMENT
▸ Authentication
▸ Remember Me - many websites implement vulnerable "remember
me" features. Often, these vulnerabilities are trivially exploitable (e.g.
storing userid=13 or username=paul&password=abcdefg).
▸ Never store username/password or any relevant information in the
cookie.
▸ Instead, you should use a random token (at least 16 characters from
random_bytes(), encoded however you like), which should be
stored in only two places: The user's cookie and a database record.
However, even this strategy is fraught with peril: Your database
lookups (like most search operations) can leak timing information,
which is typically visible to an attacker over the network.
34
35. PHP SECURITY
STORING PASSWORDS
▸ When you have to save user’s password in a database you
should never store them in plain text for security
precautions and privacy. If we do hashing the passwords
before saving them to the database we will have a safety
mechanism for not revealing them to the attacker.
▸ One of the most popular but wrong way of hashing
password was using md5() function that calculates md5
hash of a string. Hashing passwords with md5 (or sha1 or
even sha256) is bad because these hashes can get
decrypted.
35
36. PHP SECURITY
STORING PASSWORDS - THE WRONG WAY
<?php
// plain text password
$password = 'secretcode';
// hash the password with md5
$md5 = md5($password);
// Hashing passwords with md5 (or sha1 or even sha256) is bad because these hashes can get
decrypted.
// -----------------------------
// Common solution to preventing decryption is using the salt.
// plain text password
$password = 'secretcode';
// add random characters - the salt
$salt = 'k*jJlrsH:cY]O^Z^/J2)Pz{)qz:+yCa]^+V0S98Zf$sV[c@hKKG07Q{utg%OlODS';
// hash salt and password together
$md5 = md5($salt . $password);
// This is still not good enough though - Rainbow tables
36
37. PHP SECURITY
STORING PASSWORDS - THE RIGHT WAY
<?php
// plain text password
$password = 'secretcode';
// The cost parameter can change over time as hardware improves
$options = ['cost' => 12];
echo password_hash($password, PASSWORD_DEFAULT, $options);
// outputs: $2y$12$3BZAuYlYaaz4hdxuZRsEV.D69wq.oiT18cVhqoxIsmGkd4JbCq8Ai
// The string returned by password_hash() contains not only the hash, but also the
algorithm, cost and salt.
// ----------------------------------
$hash = '$2y$12$3BZAuYlYaaz4hdxuZRsEV.D69wq.oiT18cVhqoxIsmGkd4JbCq8Ai';
if (password_verify('secretcode', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
37