Category Archives: Security

Suppressing forms authentication redirects

One of the most terrible pain points of correctly implementing authentication is to define how to handle unauthorized requests. So for example neither unauthenticated users, nor users who are not the members of the Admins group can request the /admin URL.

Thankfully the FormsAuthenticationModule in ASP.NET provides a built-in solution to this problem. When the module is initialized, it subscribes to the EndRequest event with the OnLeave event handler, and when the HTTP status code is 401, this event handler redirects the user to the login page. This is a very convenient feature for classic requests, however it may cause serious headaches for Ajax.

When the module redirects the request, the client receives a HTTP 302 Redirect header instead of the original 401 Unauthorized error code. As defined in the standard, the XMLHttpRequest client transparently follows the redirect and downloads the content from the URI specified in the Location header, which is usually the Login.aspx page. So when the success handler of the XHR is called, it will see the HTML markup of the login page as the result of the call, and the result code will be 200 OK which indicates success. Well, how you can handle this easily?

Until .NET 4.0  you had no other option to fix this behavior than adding a custom HTTP module to the ASP.NET pipeline. But ASP.NET 4.5 introduced a new HttpResponse.SuppressFormsAuthenticationRedirect property, which you can set to true to avoid the redirect, and force the FormsAuthenticationModule to send the original 401 error code to the browser. Because this property is attached to the Response, you cannot set it globally, but instead you have to flip this switch in every handler that requires this behavior. If you want to set it for every response, then you can implement this in the Application_EndRequest handler in global.asax.

Now it is the client’s task to handle the specific error code as required, for example by displaying a login box or a warning message in JavaScript. But you already have that logic, haven’t you?

 

Technorati-címkék: ,,

How unique is your machine key?

Most cryptography related features of the ASP.NET platform relies on the machine key, therefore it is very important to assign unique machine keys to independent applications. Thankfully the default configuration looks like ensuring this both for the validation key and the decryption key:

<machineKey validationKey="AutoGenerate,IsolateApps" 
decryptionKey="AutoGenerate,IsolateApps" />

The AutoGenerate option frees you from manually setting the keys, and the IsolateApps options ensures that unique keys are generated for every application.

But not always!

ASP.NET will definitely generate a key, but it is neither the validation key, nor the decryption key, but instead a base key (let’s call it the machine key), which is then transformed into the validation key and the decryption key. The base machine key is stored in the registry in the following location:

HKCU\Software\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeyV4

Note that this key sits in the HKEY_CURRENT_USER hive, so the generated machine key belongs to the user’s that runs the application profile. This means that if you have two applications in IIS which are running with the same process identity, they will use the same machine key! One more reason why you should forget the SYSTEM, LOCAL SERVICE and NETWORK SERVICE accounts, and run all your web applications with separate ApplicationPoolIdentity. This also shows that the application pool is the real app isolation boundary.

Having two applications that share the same base key is not necessary a problem, because the keys used to validate and encrypt are created with additional transformations from this key. This transformation is determined by the second modifier after AutoGenerate. If you set IsolateApps, the runtime will use the value of the HttpRuntime.AppDomainAppVirtualPath property, which is different for two applications sitting in different virtual directories on the same website, so the generated keys will be different (which is good).

On the other hand, if you have two applications in the same path but on different websites, the value of this property will be the same for both apps. For example for an app that sits in the site root, the value is “/”, so IsolateApps does not provide you the isolation you expect!

To solve this problem ASP.NET 4.5 introduced a new modifier called IsolateByAppId, which uses the HttpRuntime.AppDomainAppId property instead of the AppDomainAppVirtualPath. The value of this property is something like this:

/LM/W3SVC/3/ROOT 

The “3” in the middle is the ID of the site in my case, and “ROOT” means that the app sits in the site root.

To summarize: the default AutoGenerate,IsolateApps setting does not necessarily provides you with unique keys, but if you host your apps in their own application pools which are running with ApplicationPoolIdentity, and you use IsolateByAppId instead of IsolateApps you can be sure, that your apps will use unique autogenerated keys.

The simplest way to test these setting is to use the localtest.me domain to create two separate websites, and then create a simple webpage that uses Milan Negovan’s code to retrieve and display the autogenerated keys.

 

Technorati-címkék: ,,

Run as Administrator on Windows 8.1

I use Windows 8.1 for four weeks now with a non-admin account of course. When I need admin privileges, I start the app with the well-known Run as Administrator option. I search for the app:

RunAs-1

Then comes the usual UAC dialog:

RunAs-2

Where I enter my user name and password:

RunAs-3

And when I hit Enter, nothing happens. Nothing. What? It worked for years, how can it be broken now?

I was angry about it, because this was the only issue in Windows 8.1 that made me upset on a daily basis. Then, after weeks, I suddenly realized that it was my fault: I didn’t read the instructions. Do you see now?

Note to self: RTFM!

 

Technorati-címkék: ,

ASP.NET 4.0 forms authentication issues with IE11

As I mentioned earlier, solutions that rely on User-Agent sniffing may break, when a new browser or a new version of an existing browser is released. Unfortunately because ASP.NET also contains browser-specific code, the new Internet Explorer 11 may cause some problems there as well.

Lucky coincidence, that one day after my previous post Eric Lawrence published an article about IE11 and User-Agent sniffing. Some interesting facts from his article:

  • The IE team deliberately designed the UA string to cause most sniffing logic to interpret it either Gecko or WebKit and not as previous IE version.
  • During the summer the ASP.NET team published a set of patches to fix the IE11 issues in earlier .NET versions. For example KB2836939 is for .NET 4.0, and you can find more links in Eric’s article.

The issue we experienced was on an older server that was running ASP.NET 4.0. IE11 sent the forms authentication cookie to the server, but the server completely ignored it. In the web.config file the forms element didn’t contain the cookieless attribute, because the default UseDeviceProfile worked perfectly before, however now we had to set it to UseCookies to make the authentication work with IE11 as well.

The patch mentioned earlier was not installed on this server, and we have not seen similar issues on .NET 4.5.

By the way setting cookieless="UseCookies" explicitly is a good security practice.

 

Technorati-címkék: ,,,

Outlook 2013 Folder Pane Disappears

Unfortunately there is a bug in the Office 2013 September Public Update which you may notice when you start Outlook and see an empty folder pane:

outlook-2013-folder-pane

Don’t worry, your folders are not lost, you can minimize the unused screen estate, and you can even work with your folders using the minimized features of the folder pane. So Outlook remains usable, but very uncomfortable.

Microsoft confirmed the bug and they are working hard to update the update: Outlook 2013 Folder Pane Disappears After Installing September 2013 Public Update

So far the solution is to uninstall the KB2817630 update from the Programs and Features window. However, it is tricky! The same update appears twice in the list and you have to uninstall both:

KB2817630

The other trick is that after the removal the patch may be reinstalled when your system is updated next time. Although Microsoft removed the patch from the Windows Update servers, if you have a local WSUS installation you have to manually Decline this update.

 

Technorati-címkék: ,,

Installing WSUS on Windows Server 2012

Installing Windows Server Update Services on Windows Server 2012 is not a complicated task in theory: because it is now a part of the OS, you even don’t have to download it, just click through the step-by-step installation wizard in Server Manager. If you don’t believe me, take a look at this step-by-step tutorial with screenshots.

For me it was not that easy. I’m obviously an install-failed-error magnet, but according to the tremendous amount of forum posts in the topic, I’m not alone. So here are my solutions.

A kind notice:
The solutions below worked for me, but use them on your own risk!

The server is a fresh WS 2012 install, promoted to a domain controller. I have not found any documentation that would state that this is a non-supported scenario.

NTFS permissions

According to the Prepare for Your WSUS Deployment TechNet article, the NT Authority\Network Service account must have Full Control permissions to the following folders, otherwise the WSUS Administration snap-in may fail:

  • %windir%\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files
  • %windir%\Temp

The beauty is that – as you can read in the doc – the first folder might not exist if IIS is not installed. WSUS requires IIS, and fortunately the installer is smart enough to install the Web Role as well. What’s more, it installs only those IIS components that are required to WSUS, so if you follow the “minimal install” principle, you probably want to let the WSUS installer setup IIS as well. But how to set the folder permissions before starting the installer, if the installer creates the folder?

Role Services

In a sunny Friday afternoon you may reach this step in the install wizard:

WSUS install wizard: Select role services

If you want to click all three components, be prepared to the following error message:

The following features cannot be installed on the same server: Database, WID Database.

The following features cannot be installed on the same server: Database, WID Database.

A little background info:

  • The WID Database (which is checked by default) means that the setup installs the Windows Internal Database, which is a mini SQL Server engine. It has some limitations, but perfectly suitable for WSUS, especially in a single server scenario.
  • The Database (which is not checked by default) means, that the setup creates the WSUS database in an existing SQL Server instance, which can even reside on another computer. If you want to select this option, please read the WSUS database requirements section of the documentation.

The important is that the default two checks are perfect, you don’t need all three.

Restart without results

The installer may run fine for a while, until:

The request to add or remove features on the specified server failed.

The operation cannot be completed, because the server that you specified requires a restart.

The operation cannot be completed, because the server that you specified requires restart.

Of course you can restart your server, but that won’t solve anything. After restarting the server, you have to restart the WSUS installation which will end with the same results again and again.

The solution is to modify your group policy. Start the Group Policy Management Console and edit the Default Domain Controllers Policy. In the Computer Configuration –> Policies –> Windows Settings –> Security Settings –> Local Policies –> User Rights Assignment branch, edit the Log on as a service setting, and add the following accounts: IIS_WPG, NETWORK, NETWORK SERVICE, SERVICE.

After saving the GPO, don’t forget the update the policy:

gpupdate /target:computer

Post-Installation tasks

After the installation wizard completes, you will see – if you really look for it – a Launch Post-Installation tasks link which you must click. This essentially continues the installation.

Until it fails:

Configuration failed. A log file was created at C:\Users\username\AppData\Local\Temp\tmpXXXX.tmp

wsus-post-installation

I encourage you to look into the log file, because it is detailed and very readable. For example:

Config file did not contain a value "ContentDirectory"
Microsoft.UpdateServices.Administration.CommandException: 
A required configuration value was not found in the system.

Yes, it is missing a setting which you probably entered in the graphical wizard! If you are a command line guru there is a tool for you called wsusutil.exe in the C:\Program Files\Update Services\Tools folder, however it fails with the same error.

Both the GUI and the command line tool are missing the value from the C:\Windows\System32\ServerManager\ComponentConfiguration\UpdateServices-Services.xml file. If you open this file, you will find this (I added line breaks for readability):

<?xml version="1.0" encoding="utf-16"?>
<INSTANCE CLASSNAME="ServerComponent_UpdateServices_Services">
<PROPERTY NAME="ContentDirectory" TYPE="string">
</PROPERTY>
<PROPERTY NAME="ContentLocal" TYPE="boolean">
<VALUE>true</VALUE>
</PROPERTY>
</INSTANCE>

If you have eyes for XML, you see immediately, that that ContentDirectory value is definitely missing. No problem, just add it:

<?xml version="1.0" encoding="utf-16"?>
<INSTANCE CLASSNAME="ServerComponent_UpdateServices_Services">
<PROPERTY NAME="ContentDirectory" TYPE="string">

<VALUE>C:\WSUS</VALUE>
</PROPERTY>
<PROPERTY NAME="ContentLocal" TYPE="boolean">
<VALUE>true</VALUE>
</PROPERTY>
</INSTANCE>

It you run Notepad as administrator, you will be able to save the changes. Click the Launch Post-Installation tasks link again, it will run longer.

Database

For me it ran longer, but failed later. It created another log file in the Temp folder, this time with the following error:

Fatal Error: SqlException (0x80131904): Invalid object name ‘SUSDB.dbo.tbSchemaVersion’.

This means something is wrong with the database. If you read the log you can see, that the database exists, the installer could connect to it, however the specified table is missing.

I checked the C:\Windows\WID\Data folder where I found the SUSDB.mdf and SUSDB_log.ldf files, so the database really existed, probably because it was created by a previous installation attempt. However its 2112 KB size was suspicious, because it closely matched to the size of the model.mdf. My guess was that the database was created, but it is still empty.

My idea was to delete the database and hopefully the installer will create it again. Deleting the files was surprisingly easy, but when I checked the error.log file in the Log folder after restarting the service, it because obvious that the database server is missing the files.

So the correct solution is to connect to the database server and drop the database. If you don’t want to install SQL Server Management Studio, you can go with the SQLCMD command line tool which is available as a very small standalone installer from here: Microsoft Command Line Utilities 11 for SQL Server

If you don’t read the System Requirements section the MSI installer will warn you that it requires the ODBC Driver 11 for SQL Server as well.

Downloading these two MSI files is not obvious from Internet Explorer at all. First it aggressively blocks the popup windows, and second:

Security Alert: Your current security settings do not allow this file to be downloaded.

wsus-ie-msi-download-error

If you don’t want to fight with the browser, you can download this 7MB content on another computer and move it to the server via Remote Desktop.

Now you have SQLCMD in this folder:

C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn

You will also need the connection string to WID:

np:\\.\pipe\MICROSOFT##WID\tsql\query

I prefer using SQLCMD with a command file, because that makes editing and re-executing SQL commands easy. So you can create a file called wsus.sql with this content:

select name from sys.sysdatabases
drop database susdb
select name from sys.sysdatabases

And run it from the command prompt:

sqlcmd -S np:\\.\pipe\MICROSOFT##WID\tsql\query -i c:\temp\wsus.sql

With this you’ve dropped the WSUS database, but thankfully the WSUS installer will create it again with the correct table structure. After completing the install wizard and the post-installation tasks you can start the WSUS Administration console and run the server configuration wizard.

The WSUS server is installed now, but you still have to configure the clients and setup SSL.

 

Technorati-címkék: ,,

Mitigating DoS attacks in ASP.NET

According to a widely accepted view, there is no effective countermeasure against (Distributed) Denial of Service attacks. End of story.

That’s kinda true, especially if the attack is based on sending numerous small requests to the webserver. However, websites can also be DoS’ed by sending huge HTTP requests, which can cause memory issues on the server. Actually the same can happen when non-malicious users upload files that are much bigger than expected.

You can mitigate this threat by setting the maximum accepted size for a HTTP request. It is important to understand that implementing such checking in an ASP.NET event handler is far too late, because when that handler is called, the request is already arrived to the server. Thankfully both ASP.NET and IIS provides endpoints in the earlier phases of the request pipeline.

ASP.NET provides the maxRequestLength attribute which you can configure in web.config. The default value is 4096 and that means 4096 kilobytes:

<httpRuntime targetFramework="4.5" maxRequestLength="4096"/>

So by default ASP.NET aborts all requests which are larger than 4MB with a HttpException:

HttpException (0x80004005): Maximum request length exceeded.

Unfortunately this exception is far too general, you cannot use any of its properties (beside the localized error message) to identify this special issue, but this workaround works well:

void Application_Error( object sender, EventArgs e )
{
  HttpRuntimeSection section = ConfigurationManager.GetSection( 
"system.web/httpRuntime" ) as
HttpRuntimeSection; int maxRequestLength = section.MaxRequestLength; if( this.Request.ContentLength > maxRequestLength * 1024 ) { this.Server.ClearError(); // Log and redirect to a friendly error page etc. } }

IIS provides the Request Filtering module which (beside many other useful features) also allows you to set the maximum allowed request length in the maxAllowedContentLength attribute. The default is 30000000 here and it means about 28.6MB, because it is measured in bytes:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="30000000" />
    </requestFiltering>
  </security>
</system.webServer>

When this limit is reached, IIS issues a response with HTTP Error 404.13:

iis-error-404-13

You can handle this error as well in global.asx, but this time you need to implement the Application_EndRequest event handler:

protected void Application_EndRequest( object sender, EventArgs e )
{
  if( this.Response.StatusCode == 404 && this.Response.SubStatusCode == 13 )
  {
    // Log, redirect, etc.
  }
}

 

Technorati-címkék: ,,,,,