Setting HTTP Headers in Java Server Faces (JSF)

In my last post  I discussed using HTTP headers to control browser caching of sensitive data. The post can be found here. The examples provided in that post were all ASP.Net and so I thought I’d cover how to explicitly set your HTTP Response headers when you are using the Java JSF framework.

Adding Headers via Code

You can set HTTP response headers directly in your code via the HTTPServletResponse object, as below:

import javax.servlet.http.HttpServletResponse;

ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
HttpServletResponse response =  (HttpServletResponse)context.getResponse();
response.setHeader("TestHeader", "hello");

This results in the addition of this header in the HTTP Response: TestHeader: hello.

Adding Headers in your XHTML Markup

Alternatively you can set it directly on each XHTML page via an event tag, as shown in the example below:

<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('TestHeader', 'hello')}" />
  <h:form>
    <h:panelGrid columns="2">
      <h:outputText value="Name"></h:outputText>
      <h:inputText value="#{LoginBean.name}"></h:inputText>
      <h:outputText value="Password"></h:outputText>
      <h:inputSecret value="#{LoginBean.password}"></h:inputSecret>
    </h:panelGrid>
    <h:commandButton action="welcome" value="Submit" />
</h:form>

This again results in the addition of this header in the HTTP Response: TestHeader: hello.

Adding Headers via a custom Web Filter

In order to implement a solution across your whole web application and for the ability to set headers for different resource types (not just facelets), a web filter may be what you need. Filters intercept your requests and responses to dynamically transform them or use the information contained in them. A good guide to Filters is this official Oracle one – The Essentials of Filters, or check this one out – Servlet Filter to Set Response Headers .

Here is the source code for a simple filter that sets a custom header and can be used to explicitly set HTTP response headers as required:

package Filters; 
import java.io.IOException; 
import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServletResponse;

public class HeaderFilter implements Filter 
{ 
    public void init(FilterConfig fc) throws ServletException {} 

    public void doFilter(ServletRequest req, ServletResponse res, 
            FilterChain fc) throws IOException, ServletException 
    { 
        HttpServletResponse response = (HttpServletResponse) res; 
        response.setHeader("TestHeader", "hello"); 
        fc.doFilter(req, res); 
    } 

    public void destroy() {} 
}

Once you’ve coded your filter you need to update your Web.xml file to tell the framework that you want to use this filter. Do this by adding a filter element containing the name and class details. You also need to set a filter-mapping element to map the filter with the request for resources. In the below example which configures the Filter above I have mapped “/*”  meaning all requests will go through the filter but you can configure this to only impact certain resources or file types.

<filter>
  <filter-name>MapThisCustomHeaderFilter</filter-name>
  <filter-class>Filters.HeaderFilter</filter-class>
  <init-param>
    <param-name>value</param-name>
    <param-value>test</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>MapThisCustomHeaderFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

Once configured via the web.xml file the custom header sets this header on all respones: TestHeader : hello.

Advertisement

Preventing Browser Caching using HTTP Headers

Many developers consider the use of HTTPS on a site enough security for a user’s data, however one area often overlooked is the caching of your sites pages by the users browser. By default (for performance) browsers will cache pages visited regardless of whether they are served via HTTP or HTTPS. This behaviour is not ideal for security as it allows an attacker to use the locally stored browser history and browser cache to read possibly sensitive data entered by a user during their web session. The attacker would need access to the users physical machine (either locally in the case of a shared device or remotely via remote access or malware). To avoid this scenario for your site you should consider informing the browser not to cache sensitive pages via the header values in your HTTP response. Unfortunately it’s not quite that easy as different browsers implement different policies and treat the various cache control values in HTTP headers differently.

Taking control of caching via the use of HTTP headers

To control how the browser (and any intermediate server) caches the pages within our web application we need to change the HTTP headers to explicitly prevent caching. The minimum recommended HTTP headers to de-activate caching are:

Cache-control: no-store
Pragma: no-cache

Below are the settings seen on many secure sites as a comparison to above and perhaps as a guide to what we should really be aiming for:

Cache-Control:max-age=0, no-cache, no-store, must-revalidate
Expires:Thu, 01 Jan 1970 00:00:00 GMT
Pragma:no-cache

HTTP Headers & Browser Implementation Differences:

Different web browsers implement caching in differing ways and therefore also implement various subtleties in their support for the cache controlling HTTP headers. This also means that as browsers evolve so too will their implementations related to these header values.

Pragma Header Setting

Use of the ‘Pragma’ setting is often used but it is now outdated (a retained setting from HTTP 1.0) and actually relates to requests and not responses. As developers have been ‘over using’ this on responses many browsers actually started to make use of this setting to control response caching. This is why it is best included even though it has been superseded by specific HTTP 1.1 directives.

Cache-Control ‘No-Store’ & ‘No-Cache’ Header Settings

A “Cache-Control” setting of private instructs any proxies not to cache the page but it does still permit the browser to cache. Changing this to no-store instructs the browser to not cache the page and not store it in a local cache. This is the most secure setting. Again due to variances in implementation a setting of no-cache is also sometimes used to mean no-store (despite this setting actually meaning cache but always re-validate, see here). Due to this the common recommendation is to include both settings, i.e: Cache-control: no-store, no-cache.

Expires Header Setting

This again is an old HTTP 1.0 setting that is maintained for backward compatibility. Setting this date to a date in the past forces the browser to treat the data as stale and therefore it will not be loaded from cache but re-queried from the originating server. The data is still cached locally on disk though and so only provides little security benefits but does prevent an attacker directly using the browser back button to read the data without resorting to accessing the cache on the file system.  For example:  Expires: Thu, 01 Jan 1970 00:00:00 GMT

Max-Age Header Setting

The HTTP 1.1 equivalent of expires header. Setting to 0 will force the browser to re-validate with the originating server before displaying the page from cache. For example: Cache-control: max-age=0

Must-Revalidate Header Setting

This instructs the browser that it must revalidate the page against the originating server before loading from the cache, i.e. Cache-Control: must-revalidate

Implementing the HTTP Header Options

Which pages will be affected?

Technically you only need to turn off caching on those pages where sensitive data is being collected or displayed. This needs to be balanced against the risk of accidently not implementing the change on new pages in the future or making it possible to remove this change accidently on individual pages. A review of your web application might show that the majority of pages display sensitive data and therefore a global setting would be beneficial. A global setting would also ensure that any new future pages added to the application would automatically be covered by this change, reducing the impact of developers forgetting to set the values.

There is a trade off with performance here and this must be considered in your approach. As this change impacts the client caching mechanics of the site there will be performance implications of this change. Pages will no longer be cached on the client, impacting client response times and may also increase load on the servers. A full performance test is required following any change in this area.

Implementing in ASP.net

There are numerous options for implementing the HTTP headers into a web application. These options are outlined below with their strengths/weaknesses. ASP.net and the .Net framework provide methods to set caching controls on the Request and Cache objects. These in turn result in HTTP headers being set for the page/application’s HTTP responses. This provides a level of abstraction from the HTTP headers but that abstraction prevents you setting the headers exactly how you might like them for full browser compatibility. The alternative approach is to explicitly set the HTTP headers. Both options and how they can be implemented are explored below:

Using ASP.net Intrinsic Cache Settings
Declaratively Set Output Cache per ASPX Page

Using the ASPX Page object’s attributes you can declaratively set the output cache properties for the page including the HTTP header values regarding caching. The syntax is show in the example below:

Example ASPX page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CacheTestApp._Default" %> 
<%@ OutputCache Duration="60" VaryByParam="None"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"> 
<title></title> 
</head> 
<body> 
<form id="form1" runat="server"> This is Page 1.</form> 
</body> 
</html>

Parameters can be added to the OutputCache settings via the various supported attributes. Whilst this configuration allows specific targeting of the caching solution by enabling you to define a cache setting for each separate page it has the drawback that it needs changes to be made to all pages and all user controls. In addition developers of any new pages will need to ensure that the page’s cache settings are correctly configured. Lastly this solution is not configurable should the setting need to be changed per environment or disabled for performance reasons.

Declaratively Set Output Cache Using a Global Output Cache Profile

An alternative declarative solution for configuring a page’s cache settings is to use a Cache Profile. This works by again adding an OutputCache directive to each page (and user control) but this time deferring the configuration settings to a CacheProfile in the web.config file.

Example ASPX page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CacheTestApp._Default" %> 
<%@ OutputCache CacheProfile=" RHCacheProfile "%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"> 
<title></title> 
</head> 
<body> 
<form id="form1" runat="server"> 
This is Page 1. 
</form> 
</body> 
</html>

Web.config file:

<system.web> 
<caching> 
<outputCache enableOutputCache="false"/> 
<outputCacheSettings> 
<outputCacheProfiles> 
<OutputCache CacheProfile=" RHCacheProfile"> 
<add name="RHCacheProfile" 
location="None" 
noStore="true"/> 
</outputCacheProfiles> 
</outputCacheSettings> 
</caching> 
</system.web>

This option provides the specific targeting per page and the related drawbacks of having to make changes to every page and user control. This solution does provide the ability to centralise the cache settings in one place (minimising the impact of future changes) and enables caching to be set during installation depending on target environment via the deployment process.

Programmatically Set HTTP Headers in ASPX Pages

Output caching can also be set in code in the code behind page (or indeed anywhere where the response object can be manipulated). The code snippet below shows setting the HTTP headers indirectly via the Response.Cache object:

Response.Cache.SetCacheability(HttpCacheability.NoCache); 
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1)); 
Response.Cache.SetNoStore();
Response.Cache.SetMaxAge(new TimeSpan(0,0,30));

This code would need to be added to each page and so results in duplicate code to maintain and again introduces the requirement for this to be remembered to be added to all new pages as they are developed. It results in the below headers being produced:

Cache-Control:no-cache, no-store

Expires:-1

Pragma:no-cache

 

Programmatically Set HTTP Headers in Global ASAX File

Instead of adding the above code in each page an alternative approach is to add it to the Global ASAX file so as to apply to all requests made through the application.

void Application_BeginRequest(object sender, EventArgs e)
{
	Response.Cache.SetCacheability(HttpCacheability.NoCache);
	Response.Cache.SetExpires(DateTime.Now);
	Response.Cache.SetNoStore();
	Response.Cache.SetMaxAge(new TimeSpan(0,0,30));
}

This would apply to all pages being requested through the application. It results in the below headers being produced:

Cache-Control:no-cache, no-store

Expires:-1

Pragma:no-cache

 

Explicitly define HTTP Headers outside of ASP.net Cache settings.

Explicitly Define HTTP Headers in ASPX Pages

The response object can have its HTTP Headers set explicitly instead of using the ASP.net Cache objects abstraction layer. This involves setting the header on every page:

void Page_Load(object sender, EventArgs e)
{
	Response.AddHeader("Cache-Control", "max-age=0,no-cache,no-store,must-revalidate");
	Response.AddHeader("Pragma", "no-cache");
	Response.AddHeader("Expires", "Tue, 01 Jan 1970 00:00:00 GMT");
}

Again as a page specific approach it requires a change to be made on each page. It results in the below headers being produced:

Cache-Control:max-age=0,no-cache,no-store,must-revalidate

Expires:Tue, 01 Jan 1970 00:00:00 GMT

Pragma:no-cache

Explicitly Define HTTP Headers in Global ASAX File

To avoid having to set the header explicitly on each page the above code can be inserted into the Application_BeginRequest event within the application’s Global ASAX file:

void Application_BeginRequest(object sender, EventArgs e)
{
	Response.AddHeader("Cache-Control", "max-age=0,no-cache,no-store,must-revalidate");
	Response.AddHeader("Pragma", "no-cache");
	Response.AddHeader("Expires", "Tue, 01 Jan 1970 00:00:00 GMT");
}

Again this results in the below headers being produced:

Cache-Control:max-age=0,no-cache,no-store,must-revalidate

Expires:Tue, 01 Jan 1970 00:00:00 GMT

Pragma:no-cache

Environment Specific Settings

It’s useful to be able to set the header values via configuration settings, not least to be able to test this change in a performance test environment via before/after tests.

All of the above changes should be made configurable and be able to be triggered/tweaked via the web.config file (and therefore can be modified via deployment settings).

Useful Links For More Information

Getting A Users Username in ASP.NET

When building an ASP.net application it’s important to understand the authentication solution that you are planning to implement and then ensure that all your developers are aware of it. On a few projects I have noted that some developers lack this knowledge and it can end up causing issues later on in the project once the code is first deployed to a test environment. These problems are usually a result of the differences of running a web project locally and remotely. One problem I found on a recent project was where developers were trying to retrieve the logged on user’s Windows username (within an intranet scenario) for display on screen. Unfortunately the code to retrieve the username from client request had been duplicated and a different solution used in both places, worst still neither worked. Sure they worked on the local machine but not when deployed. It was clear immediately that the developers had not quite grasped the way ASP.net works in this regard. There are several ways of retrieving usernames and admittedly it’s not always clear which is best in each scenario, so here is a very, very, very a quick guide. This post is not a deep dive into this huge subject (I might do a follow up post on that) but merely a quick guide to indicate what user details you get for a user the below objects in the framework.

The members we’re looking at are:
name_badge
1. HTTPRequest.LogonUserIdentity
2. System.Web.HttpContext.Current.Request.IsAuthenticated
3. Security.Principal.WindowsIdentity.GetCurrent().Name
4. System.Environment.UserName
5. HttpContext.Current.User.Identity.Name  (same as just User.Identity.Name)
7. HttpContext.User Property
8. WindowsIdentity

To test we create a basic ASPX page and host it in IIS so we can see what values these properties get for a set of authentication scenarios. The page just calls the various username properties available and writing out the values in the response via Response.Write().

Scenario 1: Anonymous Authentication in IIS with impersonation off.

HttpContext.Current.Request.LogonUserIdentity.Name COMPUTER1\IUSR_COMPUTER1
HttpContext.Current.Request.IsAuthenticated False
HttpContext.Current.User.Identity.Name
System.Environment.UserName ASPNET
Security.Principal.WindowsIdentity.GetCurrent().Name COMPUTER1\ASPNET

As you can see where we’re running with Anonymous Authentication HttpContext.Current.Request.LogonUserIdentity is the anonymous guest user defined in IIS (IUSR_COMPUTER1 in this example) and as the user is not authenticated the WindowsIdentity is set to that of the running process (ASPNET), and the HttpContext.Current.User.Identity is not set.

Scenario 2: Windows Authentication in IIS, impersonation off.

HttpContext.Current.Request.LogonUserIdentity.Name MYDOMAIN\USER1
HttpContext.Current.Request.IsAuthenticated True
HttpContext.Current.User.Identity.Name MYDOMAIN\USER1
System.Environment.UserName ASPNET
Security.Principal.WindowsIdentity.GetCurrent().Name COMPUTER1\ASPNET

Using Windows Authentication however enables the remote user to be authenticated (i.e. IsAuthenticated is true) automatically via their domain account and therefore the HttpContext.Current.Request user is set to that of the remote clients user account, including the Identity object.

Scenario 3: Anonymous Authentication in IIS, impersonation on

HttpContext.Current.Request.LogonUserIdentity.Name COMPUTER1\IUSR_COMPUTER1
HttpContext.Current.Request.IsAuthenticated False
HttpContext.Current.User.Identity.Name
System.Environment.UserName IUSR_COMPUTER1
Security.Principal.WindowsIdentity.GetCurrent().Name COMPUTER1\IUSR_COMPUTER1

This time we’re using Anonymous Authentication but now with ASP.net Impersonation turned on in web.config. The only difference to the first scenario is that now the anonymous guest user IUSR_COMPUTER1 is being impersonated and therefore the System.Environment and Security.Principle are using running under that account’s privileges.

Scenario 4: Windows Authentication in IIS, impersonation on

HttpContext.Current.Request.LogonUserIdentity.Name MYDOMAIN\USER1
HttpContext.Current.Request.IsAuthenticated True
HttpContext.Current.User.Identity.Name MYDOMAIN\USER1
System.Environment.UserName USER1
Security.Principal.WindowsIdentity.GetCurrent().Name MYDOMAIN\USER1

Now with Windows Authentication and Impersonation on everything is running as our calling user’s domain account. This means that the ASP.net worker process will share the privileges of that user.

As you can see each scenario provides a slightly different spin on the results which is what you would expect. It also shows how important it is to get the configuration right in your design and implement it early on in build to avoid confusion. For more information see ASP.NET Web Application Security on MSDN.