Hide your Web stack

Hiding your web stack may be very useful to discourage most of the malicious users to try to do harm on your website. We’re going to list a few measures we can take to hide these informations from a malicious user who could take advantage of them.

Let’s say we’re running a website using Symfony2 on a classic LAMP/LEMP stack (the “E” is for (e)nginx). We’re going to use the Symfony2 Standard Edition with the AcmeDemoBundle unremoved (and loaded even in production) as an application example.

We’ll go from the top (the Symfony framework) to the bottom (our web server).


There is a good Chrome Extension called Wappalyzer that can gather indications from a page URL, header’s, etc… to tell which technologies it’s using. It may be helpful to measure the progress our app is making through complete anonymousness.

Section intitulée anonymize-symfony2Anonymize Symfony2

Section intitulée skipping-the-obviousSkipping the obvious

Change the default favicon, do not deploy the app_dev.php nor the config.php in production, customize error 404 and 50x pages…

Section intitulée the-default-web-assetsThe default web assets

Public assets packaged with the application, like the stylesheets and the images used by the Web Debug Toolbar are of no use in production and shouldn’t be deployed. Accessing a particular file like /bundles/framework/css/exception.css which has a rich history on Github could help determine the version of Symfony2 used by our application. We can either configure our webserver to serve 404 pages for URLs starting with /bundles (if we use Assetic, see below) or choose to not deploy them.

Section intitulée our-own-public-assetsOur own public assets

Let’s say our AcmeDemoBundle has a few assets on its own: a demo.css and a main.js files. If we don’t use Assetic, our main.js file may be included like this:

<script src="{{ asset('/bundles/acmedemo/js/main.js') }}" type="text/javascript"></script>

effectively displaying:

<script src="/bundles/acmedemo/js/main.js" type="text/javascript"></script>

The use of the folder hierarchy /bundles/bundlename/js/... is pretty characteristic of the Symfony2 framework (and may also leak infos about the logical organization of your application). The best way to avoid this is to use Assetic which allows us to compile a list of *.js into one (idem for *.css files).

With this syntax, all of our *.js files are combined and served from the standard URL /js/main.js:

{% javascripts '@AcmeDemoBundle/Resources/public/js/*' output='js/main.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

We can do the same for *.css files.

There is no easy way to do the same with images: the AssetsInstallCommand hardcodes the prefix url to /bundles/acmedemo. It uses the Bundle::getName() method which is final, meaning we can’t override the “acmedemo” part. If we have access to our webserver config, we can rewrite the /bundles part in /media for instance. With Apache:

RewriteRule ^/media /bundles

With nginx:

rewrite ^/media/(.*)$ /bundles/$1 last;

Section intitulée make-php-keep-a-low-profileMake PHP keep a low profile

By default the cookie is named PHPSESSID. We can change that by editing the config.yml file like this:

        name: acmedemo

Section intitulée remove-unnecessary-http-headersRemove unnecessary HTTP headers

PHP advertises itself through the HTTP header X-Powered-By, we can change that by adding

expose_php = off

to our php.ini config file.

Section intitulée hide-app-phpHide app.php

In most configurations, even if / is rewritten into /app.php, we can still access /app.php directly. Most frameworks name their front controller index.php so this could be a good indication that our app is running Symfony2. We could rename app.php or if we want to completely hide the fact that we’re running PHP, we need to tell Apache or nginx to serve a 404 if someone access /app.php directly.

Section intitulée keep-apache-or-nginx-quietKeep Apache or nginx quiet

Section intitulée remove-unnecessary-http-headersRemove unnecessary HTTP headers

Nginx advertises its name and version with the HTTP header Server. You can remove the version with:

server_tokens off

in the nginx.conf file but if it’s not enough for you, you can modify the sources and recompile nginx to get rid of the remaininx “nginx” string as described here.

The same modification can be achieved with Apache by setting the following directive in the /etc/apache2/conf.d/security file:

ServerTokens Prod

With Apache, there even is a module, mod_security that allows us to set the Server header to anything we want through the SecServerSignature option.

Another way to achieve this would be to install a reverse-proxy like Varnish that allows you to manipulate headers freely and the server signature in particular.

Section intitulée customize-error-pagesCustomize error pages

Error pages are a simple way when not customized to detect the webserver in use. You can either remove sensitive informations by setting ServerSignature to off in the /etc/apache2/conf.d/security file or make apache serve custom 404 and 50x pages. With nginx, setting server_tokens to off hides nginx version in all visible places.


At the end of day, a webserver is what it is and there may be no easy way to absolutely hide its nature from malicious users but this is where hackers often start and these measures can be a good starting point to harden your web app.

Commentaires et discussions

Ces clients ont profité de notre expertise