Version information in HTTP headers
CWE-200OWASP A05:2021Updated September 4, 20264 min read
Headers such as Server, X-Powered-By and X-AspNet-Version state which software and which version you run. That lets an attacker establish in one lookup which known vulnerabilities apply to you. Removing them fixes nothing, but it does make automated searching for vulnerable targets considerably less effective.
Attackers rarely find a target first and a vulnerability second. More often it runs the other way round: a known problem exists in a specific version, and there is automated searching for systems running it. Whoever puts their version number in the headers volunteers for that search. What leaks is specific, and removing it is straightforward.
What leaks in the headers?
With every HTTP response a server sends a number of headers that have nothing to do with your application and everything to do with the software beneath it. The best known is Server, which names the web server and often the exact version. Alongside it there are X-Powered-By for the programming language, and framework-specific variants naming the content management system or application platform.
We speak of version information in HTTP headers when those values are detailed enough for an attacker to look up known vulnerabilities directly. The difference between Server: nginx and Server: nginx/1.18.0 (Ubuntu) is precisely that: the first says which product, the second says which product, which version and which operating system.
The comparison with a nameplate only half works, because a nameplate is useful. This is more like a sign stating which lock is in the door and what year it was made. To the resident it adds nothing; to whoever has read that brand’s recall notices, it does.
Which headers give away your versions?
Vulnerable:
HTTP/1.1 200 OK
Server: Apache/2.4.29 (Ubuntu)
X-Powered-By: PHP/7.2.24
X-AspNet-Version: 4.0.30319
X-Generator: Drupal 9.3.6 (https://www.drupal.org)
Four headers, four pieces of information an attacker had to do nothing for. They now know which web server, which language version, which application platform and which content management system are running. The next step is a lookup in a public vulnerability database, which yields a list of known problems per version and sometimes ready-made attack code.
What this mainly does is make you visible to large-scale automated searching. There is continuous scanning of the internet for systems running a specific vulnerable version. Your organisation does not have to be interesting for that; it is enough that you answer.
Safe:
# nginx: product name only, no version
server_tokens off;
# And strip what the application behind it adds
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Generator;
# Apache
ServerTokens Prod
ServerSignature Off
// And in the application itself
app.disable('x-powered-by');
The response is now considerably quieter:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Content-Type-Options: nosniff
A reverse proxy is the most reliable place to enforce this, because there you can clean up the headers of every backend service at once, including components whose configuration is not in your hands.
What is the impact of version information in headers?
The severity is low, and that is a fair assessment: no attack becomes possible through these headers. What they do is shorten an attacker’s reconnaissance phase from some investigation to a single glance.
The practical significance lies in the scale at which scanning now happens. When a serious vulnerability in a widely used product becomes known, automated searching for systems running the vulnerable version starts within hours. Systems stating their version end up on that list. Systems that do not have to be investigated with more effort, and in large-scale scanning that effort is usually not taken.
There is a second effect reaching beyond these headers themselves. A response running entirely on default settings is, to an experienced tester, a signal: if this simple adjustment was not made, chances are more default settings have been left in place. The headers are then less a vulnerability than an indicator.
How do you detect version information in headers?
A tester examines the headers of several different responses: the home page, a static file, an API endpoint and an error page. Those often reveal different information, because they are handled by different components; a static file comes from the web server, an API response from the application behind it.
They also look at less obvious sources: HTML comments with a generator tag, meta tags, the filenames of default stylesheets, and cookie names revealing the platform. Error pages are requested too, because those are often a server default page with version information in the footer. Whether the headers are cleaned up on routes served outside the reverse proxy is checked as well. AssistSec then uses the versions found to test whether known vulnerabilities apply to them, because that is ultimately what matters, not the header itself.
How do you prevent version information in headers?
- Disable the sending of version information in your web server.
- Remove
X-Powered-Byand framework-specific headers in the application or at the reverse proxy. - Clean the headers centrally at the proxy, so components you do not manage are covered too.
- Check the headers on different kinds of response: pages, static files, APIs and error pages.
- Replace default error pages with your own, without version information or server name.
- Remove generator meta tags and revealing HTML comments from your templates.
- Treat this as an addition to keeping your components up to date, never as a replacement.
- Check the setting again after a migration or upgrade, because default values return then.
Sources
Frequently asked questions
Is this not just security through obscurity?
Partly, and it is never presented as a replacement for patching. The difference is scale: attackers scan the internet automatically for specific versions. Whoever does not state that version falls outside that selection and is at most investigated deliberately, which is a real difference in exposure.
Can an attacker not determine the version anyway?
Often yes, with more effort. Behaviour on unusual requests, the order of headers, the exact wording of error pages and the presence of default files reveal a great deal. Removing the header raises the bar; it does not make identification impossible.
Which headers should I remove?
Server or X-Powered-By carrying a version number, and framework-specific variants such as X-AspNet-Version, X-AspNetMvc-Version, X-Generator and X-Drupal-Cache. Also look at what your own application adds; internal build numbers do not belong there either.
May I omit the Server header entirely?
Yes, it is not mandatory. Some servers do not allow full removal but do allow shortening to the product name without a version. A reverse proxy can rewrite or strip the header before the response goes out if needed.
Related articles
- VulnerabilitiesCWE-1188A05:2021Default web server files reachableSample pages, admin consoles and installation files left after setup reveal your platform and are sometimes directly abusable.
- VulnerabilitiesCWE-200A01:2021Information disclosureInformation disclosure explained: how stack traces, .git directories, source maps and over-sharing API responses leak data, and how to stop it.
- VulnerabilitiesCWE-1104A06:2021Outdated and vulnerable componentsAn outdated library or web server carries publicly known vulnerabilities with ready-made exploits. Learn how to keep that manageable.
- VulnerabilitiesCWE-16A05:2021Security misconfigurationSecurity misconfiguration explained: how default passwords, debug modes and open cloud buckets let attackers in, and how to harden your systems.
- VulnerabilitiesCWE-209A05:2021Detailed error messagesStack traces and database errors reveal your technology, paths and queries. Learn what an attacker takes from them and how to handle errors.