Adding Strict-Transport-Security (HSTS) HTTP Header In ColdFusion 2021

0
54


For years, I’ve been using Foundeo’s HackMyCF security product on my server to help me keep my ColdFusion applications secure and up-to-date. Security is one of those feature that tends to rot over time. So, it’s nice to have someone constantly nagging you about actively updating your platform. This morning, I’m finally adding the HTTP Strict-Transport-Security response header (often abbreviated as HSTS) to my ColdFusion blog so that browsers will force connections to be made using HTTPS, never HTTP.

Even though I have logic in my ColdFusion application that automatically redirects users from the HTTP protocol to the HTTPS protocol, the initial request is still being made over a non-secure connection. And, according to the Mozilla Developer Network (MDN), this means that the first request is still vulnerable to attacks:

If a website accepts a connection through HTTP and redirects to HTTPS, visitors may initially communicate with the non-encrypted version of the site before being redirected, if, for example, the visitor types http://www.foo.com/ or even just foo.com. This creates an opportunity for a man-in-the-middle attack. The redirect could be exploited to direct visitors to a malicious site instead of the secure version of the original site.

As such, we can use the Strict-Transport-Security HTTP header to tell the browser to automatically convert requests over to HTTPS before they even leave the user’s computer. This avoids the initial HTTP request altogether.

In ColdFusion, we can use the onRequestStart() event handler in the Application.cfc ColdFusion application component to add HTTP headers to every outgoing request. Here’s an abbreviated version of my framework component:

component 
	output = false
	hint = "I define the application settings and event handlers."
	{

	// Define application settings.
	this.name = "WwwBenNadelCom";
	this.applicationTimeout = createTimeSpan( 2, 0, 0, 0 );
	this.sessionManagement =  false;
	this.setClientCookies = false;

	// Force requests onto the WWW subdomain.
	if ( cgi.server_name == "bennadel.com" ) {

		redirectToWww();

	}

	// Force requests onto the HTTPS protocol.
	if ( ! isSecureHttpRequest() ) {

		redirectToSsl();

	}

	// ... truncated for demo ...

	// ---
	// LIFE-CYCLE METHODS.
	// ---

	/**
	* I initialize every ColdFusion request.
	*/
	public void function onRequestStart( required string script ) {

		// In production, the control-flow (in the pseudo-constructor) has already
		// asserted that the current request is on the WWW subdomain and is being
		// accessed over HTTP. Now, we just need to tell the browser to ALWAYS USE HTTPS.
		// --
		// According to MDN, a value of 63072000 (2 years) is necessary to be included in
		// the preloading site cache for browsers.
		var hstsValue = ( application.config.isLive )
			? "max-age=63072000; preload"
			: "max-age=0"
		;

		cfheader( name = "Strict-Transport-Security", value = hstsValue );

	}

	// ---
	// PRIVATE METHODS.
	// ---

	/**
	* I determine if the incoming request is being made over SSL.
	*/
	private boolean function isSecureHttpRequest() {

		if ( cgi.https == "on" ) {

			return( true );

		}

		var headers = getHttpRequestData( false ).headers;
		var proto = ( headers[ "X-Forwarded-Proto" ] ?: "" );

		return( proto == "https" );

	}


	/**
	* I redirect to the SSL version of the current URL.
	*/
	private void function redirectToSsl() {

		var nextUrl = ( cgi.query_string.len() )
			? "https://#cgi.server_name##cgi.path_info#?#cgi.query_string#"
			: "https://#cgi.server_name##cgi.path_info#"
		;

		location(
			url = nextUrl,
			addtoken = false,
			statuscode = 301
		);

	}


	/**
	* I redirect to the WWW version of the current URL.
	*/
	private void function redirectToWww() {

		var nextUrl = ( cgi.query_string.len() )
			? "https://www.#cgi.server_name##cgi.path_info#?#cgi.query_string#"
			: "https://www.#cgi.server_name##cgi.path_info#"
		;

		location(
			url = nextUrl,
			addtoken = false,
			statuscode = 301
		);

	}

}

As you can see, my Application.cfc already has logic to make sure that the user is accessing the WWW site over HTTPS. However, it’s now also returning the Strict-Transport-Security header to help ensure that the user never makes an HTTP request to my server in the first place.

When a user makes a secure request to the server, the HTTP headers now look like this:

Network activity in Chrome Dev Tools showing the Strict-Transport-Security HTTP header being returned.

Is a small update. But, continually making one small update after another is how we keep our ColdFusion servers secure and our users safe!

Why Not Add This HTTP Header via nginx / Apache / IIS / CloudFlare?

Most ColdFusion application servers sit behind some sort of a web server proxy like nginx, Apache, or Microsoft’s IIS (Internet Information Service). And, many sites then also sit behind some sort of a CDN (Content Delivery Network) proxy. Each one of these proxies is capable of injecting HTTP headers into every response. So, you might be wondering why not just inject security-related HTTP headers there?

The reason: version control. Every aspect of a ColdFusion application that is moved outside of the ColdFusion code is one more thing that has to be remembered if the site is ever moved to a new host. By keeping security headers inside the CFML, we remove any opportunity to accidentally expose security vulnerabilities if-and-when the code is ever moved to a new location or put behind a different CDN.

Frankly, I don’t trust me to remember these things. And, by making sure that security configurations are in the ColdFusion code and are being tracked in the git repository, then I get to remove the weakest link in the chain: Me.





Source link

Leave a reply

Please enter your comment!
Please enter your name here