2017-09-18 New technologies: HAProxy! This page is a collection of instructions to remove unnecessary server headers which may be reported as part of a Penetration Test performed by a security engineer or reported via automated tools. I have catalogued these remediation instructions for many technologies in this single site to save the vast amounts of searching required for some of the more obscure technologies. Instructions for both load balancers/proxies and individual application servers are provided. Both a short solution and a longer one giving more details along with alternate and (perhaps) more thorough corrective actions is provided. This page is periodically updated (though the timestamp may not change). Why bother? In general, excessive headers are bad: They expose what version of software is running on the server, reducing the work an attacker needs to do before trying to attack the system. Headers are the same for a normal user or an attacker. So, a known long string of characters in an encrypted data stream might aid an attacker in cracking open the encrypted TLS connection of another user. It’s a general waste of bandwidth and processing power. The solution for many headers generated many technologies is presented here. If you have other headers that keep popping up and would like them documented on this blog, pass them along to me to publishing. Remember, “Defense in Depth”™ – if you use multiple chained servers (e.g., F5 → nginx → Tomcat), you should remove the headers at each level. Major technologies groupings in this document are: Proxy, Tunnel, Router, & Load Balancer Unix native servers Java JavaScript PHP & Ruby technologies Firewalls, Load Balancers, WAFs, & Other Weirdness Microsoft technologies See the Table of Contents for the full list of headers and technologies presented. Proxy, Tunnel, Router, & Load Balancer Rather than hooking application servers up directly to the web ports, various proxies and tunnels are used on the front-end. For example, Tomcat may live on port 8080 and only listens for localhost connections while Apache handles ports 80 & 443 and will directly serve static pages, handle TLS, and only pass along the jsp/servlet requests to Tomcat. This reduces load on Tomcat while also speeding up the machine since Apache is faster than Tomcat (yes, even with super-advanced JIT, Apache is still a little faster). Thus, here are some common configurations for the front-end proxy / tunnel / load balancing server which removes the headers from the upstream servers. Configurations are provided for Apache and nginx in Proxy mode and FastCGI mode. I’d appreciate hints on HTTP.SYS for IIS from any readers. The kHTTPd and Tux servers (and similar Solaris and HPUX kernel servers) are effectively dead, so no advice is provided here. As the servers can front-end disparate technologies, such as Apache load balancer in front of an ASP.Net farm, consider removing headers in both locations for Defense-in-Depth. Even if the current technology stack does not involve all headers, add them anyway. You never know what developers may do in the future. Use the IIS configurations elsewhere in this document while also adding directives to Apache to remove them on the front-end. Thus, consider simply adding removal directives for all of these headers: Server † X-Powered-By X-AspNetMvc-Version X-AspNet-Version X-Drupal-Cache X-Drupal-Dynamic-Cache X-Generator X-Runtime X-Rack-Cache † Remember that Apache and nginx running in proxy mode still adds the Server header on their own even if removed from upstream servers. You will need to configure the servers itself separately per elsewhere in this blog post. HAProxy as a Router Short Answer Add this to HAProxy’s config: Apache frontend https-in http-response del-header Server http-response del-header X-Powered-By http-response del-header X-AspNetMvc-Version http-response del-header X-AspNet-Version http-response del-header X-Drupal-Cache http-response del-header X-Drupal-Dynamic-Cache http-response del-header X-Generator http-response del-header X-Runtime http-response del-header X-Rack-Cache 12345678910 frontend https-in http-response del-header Server http-response del-header X-Powered-By http-response del-header X-AspNetMvc-Version http-response del-header X-AspNet-Version http-response del-header X-Drupal-Cache http-response del-header X-Drupal-Dynamic-Cache http-response del-header X-Generator http-response del-header X-Runtime http-response del-header X-Rack-Cache Long Answer Using HAProxy’s del-header command will allow a mass deletion of the insecure headers, for example: Apache frontend https-in mode http bind *:443 various-other-configs http-response del-header Server http-response del-header X-Powered-By http-response del-header X-AspNetMvc-Version http-response del-header X-AspNet-Version http-response del-header X-Drupal-Cache http-response del-header X-Drupal-Dynamic-Cache http-response del-header X-Generator http-response del-header X-Runtime http-response del-header X-Rack-Cache 123456789101112 frontend https-in mode http bind *:443 various-other-configs http-response del-header Server http-response del-header X-Powered-By http-response del-header X-AspNetMvc-Version http-response del-header X-AspNet-Version http-response del-header X-Drupal-Cache http-response del-header X-Drupal-Dynamic-Cache http-response del-header X-Generator http-response del-header X-Runtime http-response del-header X-Rack-Cache For an alternate solution, you could use the rspidel command to delete whole header lines as a regular expression with something like this: Apache rspidel ^(Server|X-Powered-By|X-AspNetMvc-Version|X-AspNet-Version|X-Drupal-Cache|X-Drupal-Dynamic-Cache|X-Generator|X-Runtime|X-Rack-Cache):.*$ 1 rspidel ^(Server|X-Powered-By|X-AspNetMvc-Version|X-AspNet-Version|X-Drupal-Cache|X-Drupal-Dynamic-Cache|X-Generator|X-Runtime|X-Rack-Cache):.*$ nginx as Reverse Proxy Short Answer When using nginx as a proxy, use proxy_hide_header in nginx.conf to remove the header: Apache location / { proxy_hide_header Server; proxy_hide_header X-Powered-By; proxy_hide_header X-AspNetMvc-Version; proxy_hide_header X-AspNet-Version; proxy_hide_header X-Drupal-Cache; proxy_hide_header X-Drupal-Dynamic-Cache; proxy_hide_header X-Generator; proxy_hide_header X-Runtime; proxy_hide_header X-Rack-Cache; } 1234567891011 location / { proxy_hide_header Server; proxy_hide_header X-Powered-By; proxy_hide_header X-AspNetMvc-Version; proxy_hide_header X-AspNet-Version; proxy_hide_header X-Drupal-Cache; proxy_hide_header X-Drupal-Dynamic-Cache; proxy_hide_header X-Generator; proxy_hide_header X-Runtime; proxy_hide_header X-Rack-Cache;} Long Answer nginx can be used as a proxy to send traffic to another listener, such as a NodeJS instance. As part of the configuration, proxy_hide_header can be used to remove headers set by the upstream server. Here is a sample nginx.conf file demonstrating the removal of the many server headers from the upstream server: Apache # Redirect 80 --> 443 server { listen 80; listen [::]:80; server_name veggiespam.com www.veggiespam.com; return 301 https://$host$request_uri; } # HTTPS proxies to local instance server { listen 443; listen [::]:443; server_name veggiespam.com www.veggiespam.com # Put TLS configuration here location / { proxy_pass http://localhost:4567; proxy_pass_header Set-Cookie; proxy_redirect off; proxy_set_header Accept-Encoding ''; proxy_set_header Referer $http_referer; proxy_set_header Host $host; proxy_http_version 1.1; # Remove Headers begin proxy_hide_header Server; proxy_hide_header X-Powered-By; proxy_hide_header X-AspNetMvc-Version; proxy_hide_header X-AspNet-Version; proxy_hide_header X-Drupal-Cache; proxy_hide_header X-Drupal-Dynamic-Cache; proxy_hide_header X-Generator; proxy_hide_header X-Runtime; proxy_hide_header X-Rack-Cache; # end Remove Headers proxy_set_header Cookie $http_cookie; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_no_cache $http_pragma $http_authorization; proxy_cache_bypass $http_pragma $http_authorization; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404; } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546 # Redirect 80 --> 443server { listen 80; listen [::]:80; server_name veggiespam.com www.veggiespam.com; return 301 https://$host$request_uri;} # HTTPS proxies to local instanceserver { listen 443; listen [::]:443; server_name veggiespam.com www.veggiespam.com # Put TLS configuration here location / { proxy_pass http://localhost:4567; proxy_pass_header Set-Cookie; proxy_redirect off; proxy_set_header Accept-Encoding ''; proxy_set_header Referer $http_referer; proxy_set_header Host $host; proxy_http_version 1.1;# Remove Headers begin proxy_hide_header Server; proxy_hide_header X-Powered-By; proxy_hide_header X-AspNetMvc-Version; proxy_hide_header X-AspNet-Version; proxy_hide_header X-Drupal-Cache; proxy_hide_header X-Drupal-Dynamic-Cache; proxy_hide_header X-Generator; proxy_hide_header X-Runtime; proxy_hide_header X-Rack-Cache;# end Remove Headers proxy_set_header Cookie $http_cookie; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_no_cache $http_pragma $http_authorization; proxy_cache_bypass $http_pragma $http_authorization; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404; }} Apache as Reverse Proxy Short Answer For Apache as a proxy, use mod_header’s Header unset in httpd.conf to remove headers: Apache Header unset Server Header unset X-Powered-By Header unset X-AspNetMvc-Version Header unset X-AspNet-Version Header unset X-Drupal-Cache Header unset X-Drupal-Dynamic-Cache Header unset X-Generator Header unset X-Runtime Header unset X-Rack-Cache 123456789 Header unset Server Header unset X-Powered-By Header unset X-AspNetMvc-Version Header unset X-AspNet-Version Header unset X-Drupal-Cache Header unset X-Drupal-Dynamic-Cache Header unset X-Generator Header unset X-Runtime Header unset X-Rack-Cache Long Answer Apache is sometimes used as proxy to forward traffic to load balancers or a running Tomcat (or other) server instance. As part of the configuration, the Apache module mod_header’s Header unset directive can be used to remove headers set by the upstream server. Here is a sample httpd.conf file demonstrating the removal of several headers from the upstream server: Apache ServerName www.veggiespam.com ServerAdmin evil-kitten@veggiespam.com ProxyRequests Off ProxyPreserveHost Off AllowEncodedSlashes On KeepAlive Off Order deny,allow Allow from all # Remove Headers begin Header unset Server Header unset X-Powered-By Header unset X-AspNetMvc-Version Header unset X-AspNet-Version Header unset X-Drupal-Cache Header unset X-Drupal-Dynamic-Cache Header unset X-Generator Header unset X-Runtime Header unset X-Rack-Cache # end Remove Headers ProxyPass / http://localhost:5984/ example ProxyPassReverse / http://localhost:5984/ 1234567891011121314151617181920212223 ServerName www.veggiespam.com ServerAdmin evil-kitten@veggiespam.com ProxyRequests Off ProxyPreserveHost Off AllowEncodedSlashes On KeepAlive Off Order deny,allow Allow from all # Remove Headers begin Header unset Server Header unset X-Powered-By Header unset X-AspNetMvc-Version Header unset X-AspNet-Version Header unset X-Drupal-Cache Header unset X-Drupal-Dynamic-Cache Header unset X-Generator Header unset X-Runtime Header unset X-Rack-Cache# end Remove Headers ProxyPass / http://localhost:5984/ example ProxyPassReverse / http://localhost:5984/ nginx with FastCGI Short Answer When FastCGI runs with nginx, use fastcgi_hide_header in nginx.conf to remove the header: Apache location / { fastcgi_hide_header Server; fastcgi_hide_header X-Powered-By; fastcgi_hide_header X-AspNetMvc-Version; fastcgi_hide_header X-AspNet-Version; fastcgi_hide_header X-Drupal-Cache; fastcgi_hide_header X-Drupal-Dynamic-Cache; fastcgi_hide_header X-Generator; fastcgi_hide_header X-Runtime; fastcgi_hide_header X-Rack-Cache; } 1234567891011 location / { fastcgi_hide_header Server; fastcgi_hide_header X-Powered-By; fastcgi_hide_header X-AspNetMvc-Version; fastcgi_hide_header X-AspNet-Version; fastcgi_hide_header X-Drupal-Cache; fastcgi_hide_header X-Drupal-Dynamic-Cache; fastcgi_hide_header X-Generator; fastcgi_hide_header X-Runtime; fastcgi_hide_header X-Rack-Cache;} Long Answer nginx can be combined with FastCGI to forward requests to another listener, such as a PHP or NodeJS. The fastcgi_hide_header command can be used to remove headers set by the upstream server. Here is a sample nginx.conf file demonstrating the removal of many headers from the upstream server: Apache # Redirect 80 --> 443 server { listen 80; listen [::]:80; server_name veggiespam.com www.veggiespam.com; return 301 https://$host$request_uri; } # HTTPS proxies to local instance server { listen 443; listen [::]:443; server_name veggiespam.com www.veggiespam.com # Put TLS configuration here location ~ \.php$ { include /etc/nginx/fastcgi_params; fastcgi_pass localhost:4567; # Remove Headers begin fastcgi_hide_header Server; fastcgi_hide_header X-Powered-By; fastcgi_hide_header X-AspNetMvc-Version; fastcgi_hide_header X-AspNet-Version; fastcgi_hide_header X-Drupal-Cache; fastcgi_hide_header X-Drupal-Dynamic-Cache; fastcgi_hide_header X-Generator; fastcgi_hide_header X-Runtime; fastcgi_hide_header X-Rack-Cache; # end Remove Headers } } 123456789101112131415161718192021222324252627282930313233 # Redirect 80 --> 443server { listen 80; listen [::]:80; server_name veggiespam.com www.veggiespam.com; return 301 https://$host$request_uri;} # HTTPS proxies to local instanceserver { listen 443; listen [::]:443; server_name veggiespam.com www.veggiespam.com # Put TLS configuration here location ~ \.php$ { include /etc/nginx/fastcgi_params; fastcgi_pass localhost:4567;# Remove Headers begin fastcgi_hide_header Server; fastcgi_hide_header X-Powered-By; fastcgi_hide_header X-AspNetMvc-Version; fastcgi_hide_header X-AspNet-Version; fastcgi_hide_header X-Drupal-Cache; fastcgi_hide_header X-Drupal-Dynamic-Cache; fastcgi_hide_header X-Generator; fastcgi_hide_header X-Runtime; fastcgi_hide_header X-Rack-Cache;# end Remove Headers }} Squid as Reverse Proxy / Accelerator Short Answer When using Squid in reverse proxy mode, add these lines: Apache reply_header_access X-Powered-By deny all reply_header_access Server deny all reply_header_access X-AspNetMvc-Version deny all reply_header_access X-AspNet-Version deny all reply_header_access X-Drupal-Cache deny all reply_header_access X-Drupal-Dyanmic-Cache deny all reply_header_access X-Generator deny all reply_header_access X-Runtime deny all reply_header_access X-Rack-Cache deny all 123456789 reply_header_access X-Powered-By deny allreply_header_access Server deny allreply_header_access X-AspNetMvc-Version deny allreply_header_access X-AspNet-Version deny allreply_header_access X-Drupal-Cache deny allreply_header_access X-Drupal-Dyanmic-Cache deny allreply_header_access X-Generator deny allreply_header_access X-Runtime deny allreply_header_access X-Rack-Cache deny all Long Answer Most of the time, Squid is used as a proxy for user activity, such as in a corporate single egress configuration or for a user who just wants to reduce advertisements by having Squid filter them out. However, Squid can also by used as a reverse proxy or content accelerator/cacher much like Apache or nginx or F5 could be implemented. As such, add these settings to the Squid configuration: Apache http_port 80 accel defaultsite=veggiespam.com no-vhost cache_peer 192.168.42.42 parent 80 0 no-query originserver name=myAccel acl our_sites dstdomain veggiespam.com http_access allow our_sites cache_peer_access myAccel allow our_sites cache_peer_access myAccel deny all # Remove Headers begin reply_header_access Server deny all reply_header_access X-Powered-By deny all reply_header_access X-AspNetMvc-Version deny all reply_header_access X-AspNet-Version deny all reply_header_access X-Drupal-Cache deny all reply_header_access X-Drupal-Dyanmic-Cache deny all reply_header_access X-Generator deny all reply_header_access X-Runtime deny all reply_header_access X-Rack-Cache deny all # end Remove Headers 1234567891011121314151617181920 http_port 80 accel defaultsite=veggiespam.com no-vhost cache_peer 192.168.42.42 parent 80 0 no-query originserver name=myAccel acl our_sites dstdomain veggiespam.comhttp_access allow our_sitescache_peer_access myAccel allow our_sitescache_peer_access myAccel deny all # Remove Headers beginreply_header_access Server deny allreply_header_access X-Powered-By deny allreply_header_access X-AspNetMvc-Version deny allreply_header_access X-AspNet-Version deny allreply_header_access X-Drupal-Cache deny allreply_header_access X-Drupal-Dyanmic-Cache deny allreply_header_access X-Generator deny allreply_header_access X-Runtime deny allreply_header_access X-Rack-Cache deny all# end Remove Headers F5 BigIP as Proxy Use the HTTP::header remove rules inside of when HTTP_RESPONSE to remove the insecure header. For example: when HTTP_RESPONSE { HTTP::header remove Server HTTP::header remove X-Powered-By HTTP::header remove X-AspNetMvc-Version HTTP::header remove X-AspNet-Version HTTP::header remove X-Drupal-Cache HTTP::header remove X-Drupal-Dyanmic-Cache HTTP::header remove X-Generator HTTP::header remove X-Runtime HTTP::header remove X-Rack-Cache } 1234567891011 when HTTP_RESPONSE { HTTP::header remove Server HTTP::header remove X-Powered-By HTTP::header remove X-AspNetMvc-Version HTTP::header remove X-AspNet-Version HTTP::header remove X-Drupal-Cache HTTP::header remove X-Drupal-Dyanmic-Cache HTTP::header remove X-Generator HTTP::header remove X-Runtime HTTP::header remove X-Rack-Cache} Unix Server: Apache/2.2.x … Short Answer Set these in httpd.conf: Apache ServerTokens Prod ServerSignature Off 12 ServerTokens ProdServerSignature Off Long Answer Apache’s Server header ranges from the ultra detailed ‘Server: Apache/2.2.15 (CentOS) DAV/2 PHP/5.3.3 mod_ssl/2.2.15 OpenSSL/1.0.1e-fips Phusion_Passenger/4.0.59 mod_perl/2.0.4 Perl/v5.10.1’ to the simplistic ‘Server: Apache’. It is best to use the minimal version. To configure, add this to httpd.conf or equivalent file: Apache ServerTokens ProductOnly # (... or just "Prod") ServerSignature Off 12 ServerTokens ProductOnly # (... or just "Prod")ServerSignature Off These two options will reduce the Apache identification to just “Apache” with no version indicators. Both work for Apache 1.x and 2.x. To fully remove the header, one option is to modify the code and compile the server to completely remove the server header. This is a bad idea as your organization will now have the added responsibility of patching the code each time there is a security update. It is best to rely on your Apache vendor’s packages for your patch management. Another full-removal mechanism is to use mod_security’s SecServerSignature to modify the Server header value value with something like: Apache SecServerSignature "Microsoft-IIS/8.0" 1 SecServerSignature "Microsoft-IIS/8.0" A third option is to use mod_headers and modify the Server header directly in httpd.conf with something like: Apache Header set Server "Microsoft-IIS/8.0" ## Or, remove the header completely with this next line: Header unset Server 1234 Header set Server "Microsoft-IIS/8.0" ## Or, remove the header completely with this next line:Header unset Server However, it should be noted that the fact Apache is used can be ascertained via other means and fingerprinting techniques. For example, Apache sets the Date header in a different spot in the HTTP header list than IIS will send it. There are also various TCP handshake tricks that can be done to determine the Operating System (e.g., IIS on Linux will tip off a determined attacker). The fact that a system uses PHP probably means that Apache or nginx are being used anyway. So, removing the Server header completely via the complex means above is not worth the trouble. Server: nginx/1.8.x Short Answer In nginx.conf, add this line: Apache server_tokens off; 1 server_tokens off; Long Answer Edit the nginx.conf configuration file add a server_tokens off line in the top level of the file: Apache listen 80; server_name 127.0.0.1; server_tokens off; 123 listen 80;server_name 127.0.0.1;server_tokens off; Like Apache, setting server_tokens off will remove the version of nginx but not that fact that nginx is being used. To further remove the header, it is possible to re-compile the code with a new header. Another option is to use a module called “ngx_headers_more”. This nginx add-on allows for more control over the Server header. So, to delete the Server header, add this to the nginx.conf: more_clear_headers Server; 1 more_clear_headers Server; Or even set a custom server header, like so: more_set_headers 'Server: Microsoft-IIS/8.0'; 1 more_set_headers 'Server: Microsoft-IIS/8.0'; Be sure to dynamically load the module or to re-compile the server as required per the version of nginx. Java Server: Apache-Coyote/1.1 & X-Powered-By (Tomcat) Short Answer In $CATALINA_HOME/conf/server.xml, add addributes xpoweredby="false" and server=" " to <Connector> as follows: XHTML <Connector port="8080" protocol="HTTP/1.1" redirectPort="8443" enableLookups="false" xpoweredby="false" server=" " /> 12 <Connector port="8080" protocol="HTTP/1.1" redirectPort="8443"enableLookups="false" xpoweredby="false" server=" " /> Long Answer It is not possible to completly remove the Server header from Tomcat/Coyote, but you can set the value to something else. It must be at least one character, like a space, otherwise static files like images or CSS will still transmit “Server: Apache-Coyote/1.1”. To modify, edit the $CATALINA_HOME/conf/server.xml configuration file and add an attribute called server to <Connector> and set a value. Tomcat disables X-Powered-By by default; but if the header is showing up, add the attribute+value xpoweredby="false" to the <Connector>. However, we recommend to always add the setting just in case. Overall, the settings may look like: XHTML <Connector port="8080" protocol="HTTP/1.1" redirectPort="8443" enableLookups="false" xpoweredby="false" server=" " /> 12345678 <Connector port="8080" protocol="HTTP/1.1" redirectPort="8443" enableLookups="false" xpoweredby="false" server=" "/> It is possible to use a fake value, like server="Microsoft-IIS/8.0" for a server header. X-Powered-By: Servlet/3.0 (WebSphere 8.5.x and others) Short Answer Set the com.ibm.ws.webcontainer.disablexPoweredBy property to true. Long Answer The best solution is to set the com.ibm.ws.webcontainer.disablexPoweredBy property to true. It is possible to modify X-Powered-By via the com.ibm.ws.webcontainer.xPoweredBy to a value such as ‘PHP/5.1.6-3+b2’. But, this is not recommended – just disable it. Another option, if using the IBM HTTPd Server along with WebSphere, is to configure the server to unset the X-Powered-By header via mod_headers. Since IBM HTTPd Server is really Apache, use the same technique as shown above with Apache, mod_headers, and PHP: Apache Header unset X-Powered-By 1 Header unset X-Powered-By $WSEP (WebSphere proxy request) Short Answer Add this property to server.xml: XHTML <webContainer com.ibm.ws.webcontainer.suppresserrorpageodrheader="true"/> 1 <webContainer com.ibm.ws.webcontainer.suppresserrorpageodrheader="true"/> Long Answer The $WSEP header is generally produced when the WebSphere Application Server is performing some type of proxying action during a request or response. However, we have observed it in situations where the server owner was not aware of any proxy configuration. To disable the appearance of the header, add this property to server.xml: XHTML <webContainer com.ibm.ws.webcontainer.suppresserrorpageodrheader="true"/> 1 <webContainer com.ibm.ws.webcontainer.suppresserrorpageodrheader="true"/> Server: Glassfish & X-Powered-By: Servlet/3.0 JSP/2.2 Short Answer For the Server header, add a product.name flag to the JVM options or command line: Shell -Dproduct.name=" " ; # make this empty or another value 1 -Dproduct.name=" " ; # make this empty or another value For X-Powered-By, add this to web.xml: XHTML <context-param> <param-name>com.sun.faces.sendPoweredByHeader</param-name> <param-value>false</param-value> </context-param> 1234 <context-param> <param-name>com.sun.faces.sendPoweredByHeader</param-name> <param-value>false</param-value></context-param> Long Answer A detailed explanation of the above is provided on the Bistro 2.0! blog (new URL, sigh, Oracle blogs.oracle.com). X-Powered-By: Servlet 2.x; JBoss-4.x / JBoss-5.x Short Answer Edit web.xml and remove the <context-param> setting for “X-Powered-By”. Basically, delete this: XHTML <init-param> <param-name>X-Powered-By</param-name> <param-value>Servlet 2.4; JBoss-4.2.3.GA (build: SVNTag=JBoss_4_2_3_GA date=200807181439)/JBossWeb-2.0</param-value> </init-param> 1234 <init-param> <param-name>X-Powered-By</param-name> <param-value>Servlet 2.4; JBoss-4.2.3.GA (build: SVNTag=JBoss_4_2_3_GA date=200807181439)/JBossWeb-2.0</param-value></init-param> Server: Jetty(9.x.x…) To remove Jetty 9.x’s Server header while running in standalone mode, add this to start.ini: jetty.httpConfig.sendServerVersion=false 1 jetty.httpConfig.sendServerVersion=false Server: Jetty 8.x & 7.x To remove Jetty 8.x’s or 7.x’s Server header while running in embedded mode, instantiate the server with the follow lines of code: Java Server server = new Server(port); server.setSendServerVersion(false); 12 Server server = new Server(port);server.setSendServerVersion(false); The solution is a little more verbose when running Jetty in standalone mode. The following is to be added to jetty.xml and this code should disable the Jetty header. However, this configuration has not been tested by me, it is theoretical (someone confirm and get back to me). XHTML <Configure id="server" class="org.eclipse.jetty.server.Server"> <Set name="sendServerVersion">false</Set> </Configure> 123 <Configure id="server" class="org.eclipse.jetty.server.Server"> <Set name="sendServerVersion">false</Set></Configure> Nevertheless, if you’re using Jetty 8.x, it is end of life and you should consider upgrading to Jetty 9.x or newer. X-Powered-By: JSF/1.2 — Mojarra and others To remove X-Powered-By in JSF applications, add this to web.xml: XHTML <context-param> <param-name>com.sun.faces.sendPoweredByHeader</param-name> <param-value>false</param-value> </context-param> 1234 <context-param> <param-name>com.sun.faces.sendPoweredByHeader</param-name> <param-value>false</param-value></context-param> JavaScript X-Powered-By: Express Short Answer For Express 3.0 or newer: JavaScript app.disable('x-powered-by'); 1 app.disable('x-powered-by'); However, for a more thorough (and highly recommended) solution, just use Helmet instead: JavaScript var helmet = require('helmet') app.use(helmet()) 12 var helmet = require('helmet')app.use(helmet()) Another option for security plug-in is Lusca, which is similar to Helmet. Long Answer In Express 3.0 or newer, there are two means: JavaScript // option 1: app.disable('x-powered-by'); // option 2: app.set('x-powered-by', false); 12345 // option 1:app.disable('x-powered-by'); // option 2:app.set('x-powered-by', false); For older versions of Express, you need to write middleware that removes the header: JavaScript app.use(function (req, res, next) { res.removeHeader("x-powered-by"); next(); }); 1234 app.use(function (req, res, next) { res.removeHeader("x-powered-by"); next();}); However, the Helmet security package can both remove this header and enable many other security features, such as clickjack prevention, some XSS protection, secure caching controls, etc. All by adding this: JavaScript var helmet = require('helmet') app.use(helmet()) 12 var helmet = require('helmet')app.use(helmet()) For more information on Helmet, see https://www.npmjs.com/package/helmet. For another option, you can use the Lusca software instead of Helmet. PHP, Ruby, & Friends X-Powered-By: PHP/5.x.x … Short Answer Add or modify the php.ini to set the expose_php variable to off, like so: PHP expose_php = Off 1 expose_php = Off Long Answer PHP headers give away the version, patch level, and more in something like ‘X-Powered-By: PHP/5.1.6-3+b2’. It is best to remove this exposure by changing the php.ini configuration to shut this off. So, modify the php.ini file and set the expose_php variable to off like this: PHP expose_php = Off 1 expose_php = Off Another option is to use Apache’s mod_headers to disable the header with something like: Apache Header unset X-Powered-By 1 Header unset X-Powered-By The principle of “Defense in Depth” might mean you do both, just in case someone forgets to disable the variable on a server, but in general, modification of the variable is sufficient. X-Drupal-Cache, X-Drupal-Dynamic-Cache, X-Generator Long Answer Drupal installations can produce a few headers with security consequences, including unnecessary caching headers and a detailed version number inside of a “X-Generator” tag. Sending these headers to an end-user browser serve no purpose other than debugging – so remove them. The headers might be needed for caching proxies; so ensure the proxy engine strips the headers. Drupal can also send X-Powered-By, but that is usually caused by the underlying PHP system (see above). To remove the X-Generator head, install the module, “Remove META and Headers” from here. Be warned that there are some issues with some versions of Drupal, so your best bet might be to implement the code manually. There is no native mechanism to remove the X-Drupal-Cache & X-Drupal-Dynamic-Cache that I can discover. Drupal modules like Security Kit and HTTP Response Headers referenced from Chapter Three’s blog add good security headers but do not remove bad ones generated by Drupal. Thus, additional software tools like an Apache or nginx proxy are required. X-Rack-Cache, X-Runtime (Ruby on Rails) Long Answer Ruby on Rails can add “X-Rack-Cache” or “X-Runtime” depending on your setup. These unneeded headers can be removed using the Rack middleware as part of your Rails application. As suggested by Cascadia Enterprise, in application.rb, configure a new function to run during the processing chain by calling config.middleware.insert_before: Ruby require File.expand_path('../boot', __FILE__) require 'rails/all' require 'csv' Bundler.require(:default, Rails.env) module SampleApp class Application < Rails::Application config.middleware.insert_before Rack::Runtime, "HeaderDelete" end end 123456789101112 require File.expand_path('../boot', __FILE__) require 'rails/all'require 'csv' Bundler.require(:default, Rails.env) module SampleApp class Application < Rails::Application config.middleware.insert_before Rack::Runtime, "HeaderDelete" endend And ensure that HeaderDelete class is defined somewhere in your application: Ruby class HeaderDelete def initialize(app, options = {}) @app, @options = app, options end def call(env) r = @app.call(env) r[1].delete "X-Runtime" r[1].delete "X-Rack-Cache" r[1].delete "X-Powered-By" # etc. r[1].delete "Server" # etc. r end end 123456789101112131415 class HeaderDelete def initialize(app, options = {}) @app, @options = app, options end def call(env) r = @app.call(env) r[1].delete "X-Runtime" r[1].delete "X-Rack-Cache" r[1].delete "X-Powered-By" # etc. r[1].delete "Server" # etc. r endend Note that Apache, nginx, and other middleware may add additional downstream Server and X-Powered-By headers that Ruby on Rails will be unable to remove. In those cases, refer the appropriate sections elsewhere in this document to remove. It may also not be possible to remove X-Rack-Cache in some instances, so Apache or nginx proxies will be needed. Firewalls, Load Balancers, WAFs, & Other Weirdness This section contains configurations for when the device itself adds insecure headers. The previous load balancer section discussed how to configure the tunnel to strip the headers added by other servers. Server: BigIP Short Answer Add noserver to the 302 rule: when HTTP_REQUEST { HTTP::respond 302 noserver Location "https://[HTTP::host][HTTP:uri]" } 123 when HTTP_REQUEST { HTTP::respond 302 noserver Location "https://[HTTP::host][HTTP:uri]"} Long Answer Generally, we see this when the server is doing a redirect from HTTP to HTTPS and in no other requests. So, adjust the 302 redirect rule by adding noserver where appropriate. For example: when HTTP_REQUEST { HTTP::respond 302 noserver Location "https://[HTTP::host][HTTP:uri]" } 123 when HTTP_REQUEST { HTTP::respond 302 noserver Location "https://[HTTP::host][HTTP:uri]"} X-Runtime The Runtime header is used by many frameworks to show how the CPU time or Wall time for a request. This may seem innocuous, but this type of data can be used for various timing attacks such during authentication / password validation. The mechanism for removal is framework dependent, but the most common source is Ruby on Rails discussed above or use Apache or nginx in proxy mode to strip it out. Microsoft At the bottom of this section, there is a combined configuration to correct all of the exposed Microsoft headers presented here. Microsoft-IIS/x.x Short Answer for IIS hosted on Azure’s cloud For Azure, add this to the configuration: XHTML <system.webServer> <security> <requestFiltering removeServerHeader="true" /> </security> </system.webServer> 12345 <system.webServer> <security> <requestFiltering removeServerHeader="true" /> </security></system.webServer> For local installs of IIS, there really isn’t a short answer, so… Long Answer for locally-hosted or other IIS There are two solutions from Microsoft, both of which are not quick or simple: use URLRewrite or use URLScan. These solutions are described in Microsoft’s technical blog at remove-unwanted-http-response-headers in the first section. A third solution is to use the IIS Strip Headers module found at dionach.com. This is a native-code plug-in for IIS that can remove the headers from IIS 7.0 until 8.5. It also has the ability to remove some of the other headers mentioned in this document. However, as with the minimalistic Apache header, sometimes the complete solution is not worth the extend effort since a very determined attacker will be able to figure out the version of IIS using specialized software fingerprinting. Microsoft-HTTPAPI/2.0 Short Answer Add this to the registry: Microsoft Registry Windows Registry Editor Version 5.00 [HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters] "DisableServerHeader"=dword:00000001 1234 Windows Registry Editor Version 5.00 [HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters]"DisableServerHeader"=dword:00000001 Long Answer If the system is running self-hosted WCF, then using the tricks for IIS will not remove this header. Thus, edit the registry key HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters and add/edit DisableServerHeader to be a DWORD with value of 1. Or, simply run this registry file: Microsoft Registry Windows Registry Editor Version 5.00 [HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters] "DisableServerHeader"=dword:00000001 1234 Windows Registry Editor Version 5.00 [HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters]"DisableServerHeader"=dword:00000001 However, if registry editing is not allowed, then try something like: C# void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState) { var httpCtx = System.ServiceModel.Web.WebOperationContext.Current; if (httpCtx != null) { httpCtx.OutgoingResponse.Headers.Add("Server",string.Empty); } } 123456 void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState) { var httpCtx = System.ServiceModel.Web.WebOperationContext.Current; if (httpCtx != null) { httpCtx.OutgoingResponse.Headers.Add("Server",string.Empty); }} X-Powered-By: ASP.NET Short Answer Add this to your web.config: XHTML <system.webServer> <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> </system.webServer> 1234567 <system.webServer> <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol></system.webServer> Long Answer With the presence of WebResouce.asdx and *.aspx file on a website, the use of .Net is fairly obvious. However, the fairly long string of 22 known characters may aid attacks against a TLS connection. Thus, it is probably best to remove the header. XHTML <system.web> <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> </system.webServer> 1234567 <system.web> <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol></system.webServer> There could be cases where the above does not work. In those situations: Open the IIS console. Choose the web site. In the IIS section, click on HTTP Response Headers. In the Actions Pane, choose “X-Powered-By” and delete it. X-AspNet-Version: 4.0.detailed.version Short Answer Put this in web.config: XHTML <system.web> <httpRuntime enableVersionHeader="false" /> </system.web> 123 <system.web> <httpRuntime enableVersionHeader="false" /></system.web> Long Answer The X-AspNet-Version header gives the patch-level of .Net installed and makes it much easier for an attacker to determine vulnerabilities in libraries on the server. To remove, add this to web.config: XHTML <system.web> <httpRuntime enableVersionHeader="false" /> </system.web> 123 <system.web> <httpRuntime enableVersionHeader="false" /></system.web> X-AspNetMvc-Version: 3.x Short Answer In Global.asax.cs, this to Application_Start() : C# MvcHandler.DisableMvcResponseHeader = true; 1 MvcHandler.DisableMvcResponseHeader = true; Long Answer In the application’s Global.asax.cs file, add this to the Application_Start() function to disable the header: C# protected void Application_Start() { MvcHandler.DisableMvcResponseHeader = true; } 123 protected void Application_Start() { MvcHandler.DisableMvcResponseHeader = true;} See the reference URL below for more a thorough description of this code. Overall IIS or ASP.NET web.config suggestions XHTML <system.web> <compilation debug="false"> <httpRuntime enableVersionHeader="false" /> <httpCookies httpOnlyCookies="true" requireSSL="true" /> <customErrors mode="RemoteOnly" defaultRedirect="~/Error.aspx" /> </system.web> <system.webServer> <httpProtocol> <customHeaders> <add name="X-Frame-Options" value="DENY" /> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> <security> <!--removes Azure headers--> <requestFiltering removeServerHeader="true" /> </security> </system.webServer> 12345678910111213141516171819 <system.web> <compilation debug="false"> <httpRuntime enableVersionHeader="false" /> <httpCookies httpOnlyCookies="true" requireSSL="true" /> <customErrors mode="RemoteOnly" defaultRedirect="~/Error.aspx" /></system.web> <system.webServer> <httpProtocol> <customHeaders> <add name="X-Frame-Options" value="DENY" /> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> <security> <!--removes Azure headers--> <requestFiltering removeServerHeader="true" /> </security></system.webServer> Other Resources and Credits and references Most of the knowledge in this page has been collected from old reports and generally known concepts over many years. However, some items were researched again and particularly well written documentation needs to be given credit. Thus, I thank those authors: AspNetMvc – Great page for IIS7 Powered By Express – Advice from Stack Overflow Many MS Details – MSDN Blog Self-Host WCF w/o Registry code – Another MSDN Blog Good JBoss Advice – Manuel’s Cheat Sheet Blog I used these in creating the Jetty 7.x and 8.x advice for a friend – Stack Overflow and the old Jetty Reference Wiki Drupal Header Hardening – People who like to secure Drupal Rails Removal Reference – Stack Overflow leads us down a Rabbit Hole This blog post was once mirrored on the Aspect Security blog as official advice for remediation, but that link no longer works after Aspect Security was purchased. About The Author This blog post was written by Jay Ball and published on Jay’s blog at veggiespam.com. Jay is an #infosec professional who does penetration testing, security threat modeling, security compliance, security architecture, and security whiskey in many of the skyscrapers throughout Manhattan and Jersey City. He participates in local infosec organizations such as OWASP, 2600, HackNYC, and more. You can reach Jay via DM on Twitter @veggiespam or via email. Share this:Click to share on Twitter (Opens in new window)Click to share on LinkedIn (Opens in new window)Click to share on Reddit (Opens in new window)Click to share on Pocket (Opens in new window)