Achieving an A+ grading at Qualys SSL Labs (Forward Secrecy in IIS)

At FundApps we love the SSL Labs tool from Qualys for checking best practice on our SSL implementations. They recently announced a bunch of changes introducing stricter security requirements for 2014, and a new A+ grade – so I was curious what it would take to achieve the new A+ grading. There are a few things required to now achieve A grading and then beyond:

  • TLS 1.2 required
  • Keys must be 2048 bits and above
  • Secure renegotiation
  • No RC4 on TLS 1.1 and 1.2 (RC4 has stuck around longer than it would be liked in order to mitigate the BEAST attack)
  • Forward secrecy for all browers that support it
  • HTTP Strict Transport Security with a long max age (Qualsys haven’t defined exactly what this is, but we use a 1 year value).

We’re using IIS so the focus of this entry is how to achieve an A+ grading in IIS 7/8.

Forward Secrecy & Best Practice Ciphers

Attention to Forward Secrecy has been increasing in recent time – the key benefit being if, say, the NSA obtain your keys in the future, this will not compromise previous communications that were encrypted using session keys derived from your long term key.

To set up support for Forward Secrecy, the easiest approach (in a Windows/IIS world) is to download the latest version of the IIS Crypto tool. This makes it really easy to get your SSL Ciphers in the right order and the correct ones enabled rather than messing directly with the registry.

Once downloaded, if you click the ‘Best Practice’ option, this will enable ECHDE as the preferred cipher (required for forward secrecy). The tool does also keep SSL 3.0, RC4 and 3DES enabled in order to support IE 6 on Windows XP. If you don’t require this, you can safely disable SSL 3.0, TLS_RSA_WITH_RC4_128_SHA and TLS_RSA_WITH_3DES_EDE_CBC_SHA in the cipher list. We also disable MD5.

HTTP Strict Transport Security

The other part of the equation is enabling a HTTP Strict Transport header. The idea with this is to stop man-in-the-middle attacks whereby they transparently convert a secure HTTPS connection to a plain HTTP connection. Visitors can see the connection is insecure, but there is no way of knowing that the connection *should* have been secure. By adding a HTTP Strict Transport header (which is remembered by the browser and stored for a specified period), then provided first communication with the server is not tampered with (by stripping out the header), the browser will prevent non-secure communication from then on.

Doing this is simple – but you need to ensure that you only return a Strict-Transport-Security header on a HTTPS connection. Any requests on HTTP should *not* have this header, and should be 301 redirect-ing to the HTTPS version. especially if your website only responds to HTTPS requests in the first place (we use a seperate website to redirect from non-HTTPS requests).

In our case, we have a seperate website already responsible for the non-HTTP redirection, so it was simply a case of adding the following in our system.webServer section of the web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
       <add name="Strict-Transport-Security" value="max-age=31536000" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

If you have to deal with both HTTPS and non-HTTPS, then implementation section on WikiPedia gives an example of how.

The end result? An A+ grading from the SSL Labs tool.

Updating Azure Virtual Network to use point-to-site feature

Scott recently announced support for point-to-site VPN connections into Azure – awesome! But what might not be so clear is how to enable it on your existing Virtual Network configuration – because you can’t make changes (at least through the UI) to your virtual network after it has been deployed and is in use.

Fortunately, there appears to be a workaround.

1) Export your existing configuration (go to the Networks view in the Azure management portal, and click the Export button).

2) Modify the XML with the following. Firstly, you need to add a new “GatewaySubnet” entry, which should be inside the address space of your virtual network. You then need to add a “VPNClientAddressPool” node, with an AddressPrefix outside the address space of your virtual network.

<VirtualNetworkSite name="XNetwork" AffinityGroup="NorthEurope">
  <AddressSpace>
    <AddressPrefix>10.4.0.0/16</AddressPrefix>
  </AddressSpace>
  <Subnets>
    ...
    <Subnet name="GatewaySubnet">
      <AddressPrefix>10.4.1.0/24</AddressPrefix>
    </Subnet>
  </Subnets>
  <Gateway>
    <VPNClientAddressPool> 
      <AddressPrefix>10.0.0.0/24</AddressPrefix> 
    </VPNClientAddressPool>
    ...
  </Gateway>
</VirtualNetworkSite>

3) Go to Networks | Virtual Network | Import Configuration and re-upload your XML file.

Sorted! Now you can continue to configure point-to-site connectivity from the network dashboard as described here.

Configure Visual Studio 2012 to use 64 bit version of IIS Express

By default Visual Studio (as a x86/32bit process) will always launch the 32bit version of IIS Express. If you have components that specifically require running under 64bit, you can can configure Visual Studio 2012 to use IIS Express x64 version by setting the following registry key:

reg add HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0\WebProjects /v Use64BitIISExpress /t REG_DWORD /d 1

You should note that this feature is not supported and has not been fully tested by Microsoft – you have been warned!

Cisco VPN Client for Windows 8

There isn’t currently a version of Cisco’s VPN client that supports Windows 8, and after installation I received an error message complaining that the “VPN Client failed to enable virtual adapter.”.

Fortunately, there is a way to get this “legacy” VPN client to work, with a small registry change:

  • Open up the registry editor by typing regedit in Run prompt
  • Browse to the Registry Key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\CVirtA
  • Edit the DisplayName entry and remove the leading characters from the value data upto “%;” i.e.
    • For x86, change the value data from something like “@oem8.inf,%CVirtA_Desc%;Cisco Systems VPN Adapter” to “Cisco Systems VPN Adapter”
    • For x64, change the value data from something like “@oem8.inf,%CVirtA_Desc%;Cisco Systems VPN Adapter for 64-bit Windows” to “Cisco Systems VPN Adapter for 64-bit Windows”

Then you can try connecting again – this did the trick for me.

MSDTC gotcha’s with Virtual Machines

Setting up some new infrastructure with a web and seperate db tier, I was hit with the usual MSDTC woes.

Error messages progressed bit by bit as I opened things up:

Attempt #1: The partner transaction manager has disabled its support for remote/network transactions.

Attempt #2: Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool.

Attempt #3: The MSDTC transaction manager was unable to push the transaction to the destination transaction manager due to communication problems. Possible causes are: a firewall is present and it doesn’t have an exception for the MSDTC process, the two machines cannot find each other by their NetBIOS names, or the support for network transactions is not enabled for one of the two transaction managers.

I couldn’t get past the final error though. DTCPing is a very useful tool if you’re struggling with this, along with this TechNet article on what settings should be in place. One warning popped up that sent me in the right direction:

WARNING:the CID values for both test machines are the same while this problem won’t stop DTCping test, MSDTC will fail for this

As it happens, both machines were from an identical VM clone, and therefore had identical “CID” values. You can check this by going to HKEY_CLASSES_ROOT\CID. Look for the key that has a description of “MSDTC”.

Having found Brian’s article who had done the hard work previously, this set me on my way – essentially you just need to uninstall and reinstall MSDTC on both of the machines. The following worked for me:

  1. Run “msdtc -uninstall” (from an admin prompt)
  2. Run “msdtc -install”
  3. Reconfigure MSDTC again from Component Services\My Computer\Distributed Transaction Coordinator\Local DTC (right click, properties)

And off you go… (don’t forget to enable the predefined DTC rules for local hosts in advanced firewall settings too)

Migrating old websites & Rewrite maps in IIS 7

If you’re migrating to a new website and need to map old IDs to new IDs, I’ve just discovered that the UrlRewrite plugin in IIS has a great feature I hadn’t come across before called rewriteMaps. This means instead of writing a whole bunch of indentical looking rewrite rules, you can write one – and then simply list the ID mappings.

The syntax of the RegEx takes a bit of getting used to, but in our case we needed to map

/(various|folder|names|here)/display.asp?id=[ID]

to a new website url that looked like this:

/show/[NewId]

You can define a rewriteMap very simply – most examples I saw included full URLs here, but we just used the ID maps directly:

<rewriteMaps>
  <rewriteMap name="Articles">
    <add key="389" value="84288" />
    <add key="525" value="114571" />
    <add key="526" value="114572" />
  </rewriteMap>
</rewriteMaps>

You can reference a rewriteMap using {MapName:{SomeCapturedValue}}, so if SomeCapturedValue equalled 525 then you’d get back 114571 in the list above.

Because we’re looking to match a querystring based id, and you can’t match queryString parameters in the primary match clause, we needed to add a condition, and then match on that captured condition value instead, using an expression like this:

http://www.newdomain.com/show/{Articles:{C:1}}/

The final rule XML follows:

<rule name="Redirect rule for Articles" stopProcessing="true">
  <match url="(articles|java|dotnet|xml|databases|training|news)/display\.asp" />
  <conditions>
    <add input="{QUERY_STRING}" pattern="id=([0-9]+)" />
  </conditions>
  <action type="Redirect" url="http://www.developerfusion.com/show/{Articles:{C:1}}/" appendQueryString="false" />
</rule>

Debugging Powershell and Psake commands and parameters

Getting commands and parameters in Powershell and Psake can be pretty troublesome at times. The echoargs helper from the PowerShell Community Extensions can be a lifesaver. If, for instance, you are calling

msbuild.exe /t:Build /p:SomeTroublesomeParametersHere

if you swap msbuild for echoargs (after placing the extensions in C:\Windows\System32\WindowsPowerShell\v1.0\Modules and calling Import-Module pscx), then you’ll see the exact parameters being passed to the executable:

Arg 0 is </t:Build>
Arg 1 is </p:SomeTroublesomeParametersHere>

However, to make it easier for those dipping into our build scripts, I decided to create a wrapper function so that we would always output parameters to the external functions we call to the console for debugging purposes.

function Invoke-EchoCommand
{
    $cmdName = $args[0];
    $remainingArgs = $args[1..$args.Length]
    Write-Host "Executing $cmdName" -ForegroundColor darkgray
    & "$base_dir/tools/build/echoargs" @remainingArgs | Write-Host -ForegroundColor darkgray
    exec { & $cmdName @remainingArgs } # using exec helper in PSake
}

You can then replace any calls to exec with

Invoke-EchoCommand msbuild.exe /t:Build /p:SomeTroublesomeParametersHere

and you’ll get some helpful debug information over how the parameters were actually passed. The only nifty bit in the function is that it uses the first parameter as the command name, and then passes all remaining arguments on to the executed command.

Saving thumbnails in the original file format with C#

I tripped up on a strange quirk working with the Image and ImageFormat classes recently. The intention was simple – load an Image object from an existing graphic, generate a thumbnail, and save it out in the original format. The Image class in .NET includes a handy “RawFormat” property indicating the correct format to save out in. So far, so easy. Except the object that RawFormat was returning didn’t seem to match any supported ImageFormat, and the Guid was one character out. For example, when loading a JPEG, you got:

b96b3caa-0728-11d3-9d7b-0000f81ef32e

when the Guid for ImageFormat.Jpeg.Guid was in fact

b96b3cae-0728-11d3-9d7b-0000f81ef32e

It turns out that the “RawFormat” seems to change to an internal format the moment you start modifying the original image. So the simple trick is to save the value of the RawFormat property first, do your modifications, and then save out the image using the original RawFormat value.

Automatically tracking outbound links in Google Analytics

Google Analytics supports a nifty feature called “Events”, which is designed to allow you to track non-pageview type events. This is particularly helpful if you have an AJAX type interface on which you want to gather statistics, but another use I’ve found handy is to track clicks on external links to other sites. If you’re using the asyncronous version of the tags (if not, why not), then you should have some code that uses the window._gaq variable. In order to track events aside from the initial page view, you simply need to call the following each time you want to record an event:

window._gaq.push(['_trackEvent','Event Category', 'Event Value']);

Using your favourite javascript library (mine are jQuery and Mootools), it’s easy to hook this up to automatically fire Google event tracking for any external hyperlinks on the site. We simply look for all a tags with href attributes that begin with “http://”. Then, if the href doesn’t contain our current hostname, then we assume it’s an external link. Obviously the logic could be adjusted for your particular needs!

Here’s the mootools version:

document.getElements('a[href^="http://"]').addEvent('click',function(link) {
  var href = link.target.href;
  if(href.indexOf(window.location.host) < 0) {     window._gaq.push(['_trackEvent','Outbound Links', href]);   } });

Or for the jQuery fans amongst you, just swap the first line for:

$('a[href^="http://"]').bind('click',function(link) {

Now, after 24 hours or so, if you check out the "Event Tracking" page under "Content" in Google Analytics, you'll see an outbound link category listing all the external links clicked on the site (assuming Javascript is turned on, of course).

Side note: previous versions recommended by Google included a window.timeout before allowing the redirect to take place - in order to ensure the request to Google to record the click goes out first - as far as I can establish, this is no longer necessary.

Side note 2: By tracking outbound clicks this will affect your bounce rate figures. Essentially an event counts as another activity, so if a visitor lands on one page, and then clicks an external link, that will not count as a "bounce". Whether this is a fair reflection of bounces or not depends on your viewpoint - but something to bear in mind. Unfortunately there's currently no way to log an event that doesn't affect the bounce rate.

Detecting 404 errors after a new site design

We recently re-designed Developer Fusion and as part of that we needed to ensure that any external links were not broken in the process. In order to monitor this, we used the awesome LogParser tool. All you need to do is open up a command prompt, navigate to the directory with your web site’s log files in, and run a query like this:

"c:\program files (x86)\log parser 2.2\logparser" "SELECT top 500 cs-uri-stem,COUNT(*) as Computed FROM u_ex*.log WHERE sc-status=404 GROUP BY cs-uri-stem order by COUNT(*) as Computed desc" -rtp:-1 > topMissingUrls.txt

And you’ve got a text file with the top 500 requested URLs that are returning 404. Simple!