More and more applications are moving from desktop to the web, where they are particularly exposed to security risks. They are often tied to a database backend, and thus need to be properly secured, even though most of the time they are designed to restrict access to authenticated users only. PHP is used to develop a lot of these web applications, including several dedicated to AdwCleaner management.
There is no magic unique solution to harden a web application, but as always in security, it’s a matter of layers including:
Applying the latest security patch and updates
Sending the correct HTTP headers
Hardening the language stack
Hardening the OS
Taking network security measures
Since we’re in 2017, we’ll consider that security patches and updates are applied properly so this article will focus on several must-have HTTP headers, as well as how we harden our web stack at a PHP level in an effective and easy way for the AdwCleaner web management application.
Securing a web application using HTTP headers
There are a lot of standard HTTP headers for various uses (like encoding and caching) and a lot of them aim to enforce smart security behaviors, like mitigating XSS, for HTTP clients (i.e web browsers). Here are a few useful ones.
A website suffering of XSS, without the proper HTTP headers in place to mitigate it.
Strict-Transport-Security
This instructs the browser to connect to the website using HTTPS directly for a certain period of time using the max-age directive. It can also be applied to subdomains with includeSubDomains directive.
This header aims to have a fine-grained control over when the referrer is transmitted. Several directives are available, from no-referrer to completely disable the referrer header to strict-origin-when-cross-origin, which means that the full URL is sent with any request made in TLS in the same domain. (Whereas only the domain is sent as referrer if the request is made on a different domain or subdomain.) Finally, if the request is made in HTTP, the referrer is not sent.
It’s a handy header especially to reduce internal URL leaks to external services.
It enforces the MIME type of resources, and states that they shouldn’t be changed. If the MIME type is not the one advertised with the Content-Type header, then the request is dropped in order to mitigate MIME confusion attacks. There’s only one directive: nosniff.
This header controls whether or not the page can be loaded as an iframe or an object. There are different directives, from DENY to forbid this behaviour, to SAMEORIGIN, which allows it only from the same origin (domain or subdomain), and ALLOW-FROM which allows the operator to specify a whitelist of origins.
This controls how the page should be handled by crawling bots (i.e search engines). Several directives exist: the noindex, nofollow, nosnippet, noarchive directives will avoid the page to be indexed in search results and instruct the crawler to not follow the links of the page. The crawler will also not store any copy of the page.
This legacy header instructs the browser to block any detected XSS request when set to 1; mode=block. It’s now superseded by the Content-Security-Policy header, but is still useful on older web browsers. This header would have mitigated the XSS on the website at the beginning of this article.
Content-Security-Policy
This powerful header allows the operator to define rules specifying how the webpage resources can be loaded and where from. It’s particularly efficient against XSS. For instance, it’s possible to enforce loading resources on HTTPS only using default-src: https:, or to forbid any inline scripts with the directive default-src: ‘unsafe-inline’.
It’s possible to create more complex rules, for instance:
base-uri ‘none’; Forbid the usage URI. default-src ‘self’; Will use the origin as fallback for any fetch directive which is not specified. frame-src; forbid any external content to be loaded using iframes. connect-src ‘self’; Forbid ping, Fetch, XMLHttpRequest, WebSocket, and EvenSource to load external content. form-action ‘self’; Enforce the forms submissions to the origin. frame-ancestors ‘none’; As X-Frame-Options: Deny, it forbids loading the page using iframes, objects, embed, or applets. img-src ‘self’ data:; Allow tags to use data uris from the origin only. media-src ‘none’; Forbid loading any