⬆️ ⬇️

Guide to writing secure PHP applications in 2018



The year 2018 is approaching, and techies - in particular, web developers - must reject many of the old techniques and beliefs in the development of secure PHP applications. This is especially true for anyone who does not believe that such applications are possible at all.



This guide is an addition to the PHP: The Right Way e-book with a strong emphasis on security, and not general PHP programming issues (such as code style).



Content



  1. PHP versions
  2. Dependency Management with Composer

    • Featured Packages
  3. HTTPS and browser security

    • Security headers
    • Subresource Integrity
    • Document Relationships
  4. Developing secure PHP applications

    • Interaction with databases
    • File upload
    • Cross-site scripting (XSS)
    • Cross-site request forgery (CSRF)
    • XML attacks (XXE, XPath deployments)
    • Deserialization and implementation of PHP objects
    • Password Hashing
    • General Cryptography
  5. Special cases

    • Searchable Encryption
    • Token-based authentication without side channels
    • Developing secure APIs
    • Security Event Logging with Chronicle
  6. A few words from the author
  7. Sources


PHP versions



In short: nothing can be done, but in 2018 you will use PHP 7.2 , and in the beginning of 2019 plan to switch to 7.3.



PHP 7.2 released November 30, 2017



At the time of this writing, only PHP 7.1 and 7.2 are actively supported by language developers, and for PHP 5.6 and 7.0, security patches will be released for about a year.



Some operating systems have long-term support for PHP versions that are no longer supported, but this is generally considered to be a bad practice. For example, backporting security patches without incrementing version numbers makes it difficult to evaluate the security system for PHP only.



Accordingly, regardless of the promises of vendors, always try to use only an actively supported version of PHP, if possible. Even if you spend some time working with a version for which only security patches are released, regular version updates will save you from many unpleasant surprises.



Dependency management



In short: use Composer.



Composer is a masterpiece dependency management solution for the PHP ecosystem. In PHP: The Right Way, a whole section is devoted to getting started with Composer , we highly recommend reading it.



If you do not use Composer to manage dependencies, then sooner or later (hopefully late, but, most likely, early) you will find yourself in a situation where one of the libraries you depend on is very outdated and criminals will actively exploit vulnerabilities in old versions.



Important: Do not forget to update your dependencies as software is developed. Fortunately, this can be done in one line:



composer update 


If you are doing something special that requires the use of PHP extensions (written in C), then you cannot install them using Composer. You will also need a PECL.



Featured Packages



Regardless of what you create, surely these dependencies will be useful to you. This is in addition to what most PHP developers recommend (PHPUnit, PHP-CS-Fixer, etc.).



roave / security-advisories



The Roave security-advisories package uses the Friends of PHP repository so that your project does not depend on any packages with known vulnerabilities.



 composer require roave/security-advisories:dev-master 


Or you can upload your composer.lock file to Sensio Labs as a standard automated vulnerability assessment procedure to receive alerts for any obsolete packages.



vimeo / psalm



Psalm is a static analysis tool that helps identify possible bugs in your code. Although there are other good tools (for example, the wonderful Phan and PHPStan ), but if you need support for PHP 5, then Psalm is one of the best static analysis tools for PHP 5.4+.



Using Psalm is simple:



 # Version 1 doesn't exist yet, but it will one day: composer require --dev vimeo/psalm:^0 # Only do this once: vendor/bin/psalm --init # Do this as often as you need: vendor/bin/psalm 


If this is the first time you apply this code to an existing database, you will see a lot of red marks. If you do not create a WordPress-scale application, then it is unlikely that you will have to perform the Hercules feat in order to pass all these tests.



Regardless of which static analysis tool you choose, we recommend integrating it into the continuous integration workflow (if possible), so that the tool runs after each code change.



HTTPS and browser security



In short: HTTPS to test , and security headers .



In 2018, sites will no longer work via unsecure HTTP. Fortunately, you could get TLS-certificates for free and automatically update them thanks to the ACME protocol and the certification company Let's Encrypt .



Integrating ACME into your web server is a couple of trivia.





You might think, “Okay, I have a TLS certificate. Now we need to spend several hours searching for configurations to make the site safe and fast. ”



Not! Mozilla will help you . To create the recommended cipher suite for your audience, you can use the configuration generator.



HTTPS (HTTP over TLS) is completely uncontested if you want to make your site secure. The use of HTTPS instantly eliminates several types of attacks on your users (implementation of man-in-the-middle content, interception of data, replay attacks and manipulation of sessions for the sake of user substitution).



Security headers



Although the use of HTTPS on your server has many advantages in terms of security and performance, you can go even further and use other browser security features. Most of them involve sending HTTP response headers with content.





Similarly, if you use the built-in PHP session management properties (which is recommended), you may want to call session_start() :



 session_start([ 'cookie_httponly' => true, 'cookie_secure' => true ]); 


Then, when sending credentials, your application will use only secure HTTPS flags, which will prevent successful XSS attacks by stealing user cookies, they will be sent only via HTTPS. A couple of years ago we already wrote about secure PHP sessions .



Sub-resources integrity



Someday in the future you will be working on a project that uses CDNs to unload traditional Javascript / CSS frameworks and libraries into a central location. Not surprisingly, security experts predicted an obvious problem: if many sites use CDN to provide part of their content, then cracking the CDN and swapping data will allow embedding arbitrary code on thousands (if not millions) of sites.



Therefore, we invented the integrity of subresources (subresource integrity).



Sub-Resource Integrity (SRI) allows you to pin a hash of the contents of the file, which the CDN should provide you. The current implementation of SRI allows only cryptographic hash functions to be used, so attackers will not be able to generate malicious versions of the content, resulting in the same hashes as those of the original files.



Real-world example: Bootstrap v4-alpha uses SRI in the sample code of their CDN .



 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous" /> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous" ></script> 


Document Relationships



Web developers often set the target attribute to hyperlinks (for example, target="_blank" to open a link in a new window). But if you don’t also pass the rel="noopener" tag, then let the landing page take control of the source page .



Don't do that



 <a href="http://example.com" target="_blank">Click here</a> 


This will allow example.com to gain control over the current page.



Do this



 <a href="https://example.com" target="_blank" rel="noopener noreferrer">Click here</a> 


In the new window opens example.com , but does not gain control over the current window.



For further study .



Developing secure PHP applications



If security software is new to you, you can start by introducing A Gentle Introduction to Application Security.



Many security experts from the very beginning draw the attention of developers to resources like OWASP Top 10 . But many common vulnerabilities can be considered as special cases of the same high-level problems (code / data is not adequately separated, erroneous logic, unsafe operating environment, broken cryptographic protocols).



We believe that if we instill in the safety of the neophytes a simpler, more fundamental understanding of security problems and their solutions, this will help improve the security situation in the long run.



Interaction with databases



Read more: Preventing SQL injections in PHP applications



If you write SQL queries yourself, make sure that you use prepared statements (prepared statements) and that any information provided by the network or file system is passed as parameters, and not concatenated into a query string. Also make sure you avoid emulated prepared expressions.



The best choice is EasyDB .



DO NOT DO THIS:



 /*  : */ $query = $pdo->query("SELECT * FROM users WHERE username = '" . $_GET['username'] . "'"); 


Do this:



 /*   SQL-: */ $results = $easydb->row("SELECT * FROM users WHERE username = ?", $_GET['username']); 


There are other layers of abstractions in the databases that provide an equivalent level of security (EasyDB uses PDO under the hood, but by all means tries to turn off the emulation of prepared expressions in favor of real prepared expressions to prevent problems). As long as user input does not affect the structure of requests (this also applies to stored procedures), you are safe.



File upload



Read more: How to safely allow users to upload files



Accepting user files is risky, but it can be done safely by taking a number of precautions. In particular, close direct access to downloadable files so that they cannot be executed or interpreted.



Downloaded files must have read-only or read-only or write attributes and never be executable.



If your site has a root directory for documents /var/www/example.com , then you should not store the downloaded files in /var/www/example.com/uploaded_files .



It is better to store them in a separate directory, to which there is no direct access (for example, /var/www/example.com-uploaded/ ), so that they are not randomly executed as server scripts and do not open the door to remote code execution.



A cleaner solution is to move your root directory for files one level down (i.e., at /var/www/example.com/public ).



Another problem with downloadable files is related to their safe download .





Cross-site scripting (XSS)



Read more: Everything you need to know about PHP cross-site scripting prevention



In an ideal world, preventing XSS would be as easy as SQL injection. We would have an easy-to-use API for separating the structure of a document from its contents.



Unfortunately, in the real world, most web developers generate long HTML and send it in an HTTP response. This is not only characteristic of PHP, it’s just the reality of web development.



Closing XSS vulnerabilities is a completely solvable task. However, the contents of the browser security section unexpectedly gaining more importance. In short:



  1. Always shield at the exit , never at the entrance . If you keep clean data in the database, then a SQL vulnerability is found somewhere, the attacker can completely bypass your XSS protection by simply contaminating the malicious code with seemingly clean records.
  2. If your framework has a template engine that offers automatic context filtering, then use it. Your framework will work safer.
  3. echo htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); - this is a safe and effective way to stop all XSS attacks on a page using UTF-8, but all HTML will be prohibited.
  4. If you want to use Markdown instead of HTML, do not select HTML.
  5. If you need to allow some HTML and you do not use the template engine (see point 1), use HTML Purifier . HTML Purifier is not suitable for shielding the context of HTML attributes.


Cross-site request forgery (CSRF)



Cross-site request forgery is a type of attack with delegate substitution: you can trick the user browser and force it to execute a malicious HTTP request with elevated user privileges.



In general, this problem is easily solved:



  1. Use https. This is a necessary condition.
  2. Add a basic question-answer type authentication.

    • Add a hidden attribute to each form.
    • Generate cryptographically secure random values ​​(tokens).
    • Check the provided hidden form attributes and check with the expected values.


We wrote the Anti-CSRF library, which goes even further:





If your framework does not care about CSRF vulnerabilities, then use Anti-CSRF.



In the near future, SameSite cookies will allow you to stop CSRF attacks with much less effort.



XML attacks (XXE, XPath deployments)



There are two main vulnerabilities that manifest themselves in applications that handle XML a lot:



  1. External Entities XML (XXE).
  2. XPath injection


XXE attacks, among other things , can be used as a launching pad for local / remote file injection exploits.



Early versions of Google Docs were vulnerable to XXE attacks, but they are little known outside of business applications that handle large amounts of XML.



The main thing you need to do to protect against XXE attacks:



 libxml_disable_entity_loader(true); 


XPath injection is very similar to SQL injection, only here we are talking about XML documents.



Fortunately, in the PHP ecosystem, there are rarely situations when user input is transmitted in an XPath query.



Unfortunately, this also means that the best available solution (for precompiled and parameterized XPath queries) is missing in the PHP ecosystem.



We recommend using white lists of allowed characters for any data related to XPath queries.



 <?php declare(strict_types=1); class SafeXPathEscaper { /** * @param string $input * @return string */ public static function allowAlphaNumeric(string $input): string { return \preg_replace('#[^A-Za-z0-9]#', '', $input); } /** * @param string $input * @return string */ public static function allowNumeric(string $input): string { return \preg_replace('#[^0-9]#', '', $input); } } // Usage: $selected = $xml->xpath( "/user/username/" . SafeXPathEscaper::allowAlphaNumeric( $_GET['username'] ) ); 


White lists are safer than black ones.



Deserialization and implementation of PHP objects



Read more: Secure (de) serialization in PHP



If you pass untrusted data to unserialize() , then you are asking for two scenarios:



  1. Implementing a PHP object that can be used to launch a POP chain and trigger other vulnerabilities from misused objects.
  2. Memory corruption in the PHP interpreter itself.


Many developers prefer to use JSON serialization instead, which is a noticeable improvement in software security. But keep in mind that json_decode() is vulnerable to DDoS attacks through hash collisions . Unfortunately, a complete solution to the hash DOS problem in PHP has yet to be found .



Migrating from djb33 to Siphash with assignment 1 as the high-order bit for the hash of the string input value, 0 for the integer and with the key requested in advance, will provide full protection against these attacks, it will be provided by CSPRNG.



Unfortunately, the creators of PHP are not willing to partially sacrifice the performance they achieved in PHP 7, so it’s hard to convince them to abandon djb33 (very fast, but unsafe) in favor of SipHash (also fast, though not like djb33, but much safer) . A significant performance degradation may even interfere with the development of future versions, which will not benefit security.



Therefore, it is better to do this:





Password Hashing



Read more: How to safely store user passwords in 2016

Safe password storage used to be a topic of active discussion, but today it is easy to implement, especially in PHP:



 $hash = \password_hash($password, PASSWORD_DEFAULT); if (\password_verify($password, $hash)) { // Authenticated. if (\password_needs_rehash($hash, PASSWORD_DEFAULT)) { // Rehash, update database. } } 


You do not even need to know what the algorithm is, because if you use the latest version of PHP, you will use the latest technologies, and the user passwords will be automatically updated as soon as a new algorithm is available by default.



Whatever you do, do not do it like WordPress .



If interested: from PHP 5.5 to 7.2, the default algorithm is bcrypt. In the future, it may be replaced by Argon2, the winner of the Password Hashing Contest .



If you have not used the password_* API before and you need to migrate legacy hashes, then do it that way . Many companies, such as Yahoo , have done wrong. It seems that Apple has recently caused an incorrect implementation of the Legacy hashes update to cause a bug with iamroot .



General Cryptography



We wrote a lot on this topic:





Sodium (libsodium). PHP 7.2 ( 5.2.4), sodium_compat , 7.2.



- . , .



Special cases



, 2018- PHP-. .





: Building Searchable Encrypted Databases with PHP and SQL



, , . , . In particular:



  1. , .
  2. .
  3. ( ) HMAC KDF (, Argon2).
  4. : 3, Bloom-.
  5. 3 4 SELECT.
  6. .


, .





: -:



( ): , SELECT ?



:



  1. .
  2. SELECT.
  3. (constant-time).

    • . , « ».


, -.



API



: Sapient PHP API



SAPIENT , S ecure API EN gineering T oolkit, . Sapient / (shared) HTTPS.



Ed25519 API- , , « » / .



HTTP- , , (, Oauth). , - , , .



Sapient Sodium.



:





Paragon Initiative Enterprises Sapient ( open source ) Sapient.



Chronicle



: Chronicle ,



Chronicle — , . - , , , «».



, Chronicle SIEM, , .



Chronicle (cross-sign) Chronicle / , Chronicle, .



Chronicle , .



Chronicle API, Sapient , — Quill .





, ( open source ), .



.



2015- PHP.



, ( PHP PHP 7.2), , . , .



, , , , , , , . , , ( Scott Helme ) . , .



Of course, this is not an exhaustive guide. , . — , . , PHP-.



Sources



, .



, , , .



, (PCI-DSS, ISO 27001 . .), . , .



— PHP- .





')

Source: https://habr.com/ru/post/344696/



All Articles