Labels

Tuesday, February 3, 2009

Enabling Windows Authentication

Hi,

 

This blog post summarizes - Enabling Windows Authentication within an Intranet ASP.NET Web application.

 

Steps:

 

Though it is simple, best to document it here.

 

·          “Integrated Windows Authentication” (formerly called NTLM authentication) enabled within IIS.

·          In Web.config - <authentication> section which sets the mode to “Windows”. 

·          In Web.config - <authorization> section that denies access to “anonymous” users visiting the site.

 

<configuration>

    <system.web>

        <authentication mode="Windows" />

         <authorization>

             <deny users="?"/>

          </authorization>    

    </system.web>

</configuration>

 

 

Obtaining the Logged-in Username:

 

In Asp.net Page:

 

Dim username As String
username = User.Identity.Name 

 

In C# class:

 

Dim User As System.Security.Principal.IPrincipal
User = System.Web.HttpContext.Current.User

 

Dim username As String
username = User.Identity.Name

 

 

Looking up Role/Group information for a User

 

If User.IsInRole("DOMAIN\managers") Then
     Label1.Text = User.Identity.Name & " is a manager"
Else
     Label1.Text = User.Identity.Name & " is not a manager"
End If

 

 

Hope this helps.

 

Arun Manglick

 

Forms Authentication Blocks Static Resources on Login Page - Images (.jpg, .gif, etc) & CSS

Hi,

 

This blog post summarizes – How Forms Authentication Blocks Static Resources on Login Page - Images (.jpg, .gif, etc) & CSS

 

Problem –

 

Mostly building a secure website using forms-authentication is achieved using below.

 

<authorization>

     <deny users="?"/>

</authorization>

 

·          This tells ASP.NET to block all anonymous (non logged-in) users from accessing the web-site, and instead redirect them to a login.aspx page.

·          Because the above authorization directive is not scoped within a <location> element, it applies to all content on the site (except for the login.aspx page).

·          The issue results – Images & CSS does not work on their login.aspx page.

 

 

How to Fix This –

 

Fixing this is pretty easy.  Just add a new authorization rule to your root web.config site that grants access to the stylesheet and/or other file resources that you want to allow anonymous access to.  For example, the below configuration section denies access to all resources except stylesheet.css:

 

<system.web>

   <authorization>

       <deny users="?"/>

   </authorization>

</system.web>

 

<location path="stylsheet.css">

    <system.web>

        <authorization>

            <allow users="*"/>

        </authorization>

    </system.web>

 

<location path="ImagesDirectory">

    <system.web>

        <authorization>

            <allow users="*"/>

        </authorization>

    </system.web>

</location>

 

 

 

Hope this helps.

 

Arun Manglick

 

Always set the "applicationName" property with Providers

Hi,

 

This blog post summarizes - Implement Role Based Security within an Intranet ASP.NET Web application.

 

Scenario:

 

You develop an ASP.NET 2.0 application locally using the new ASP.NET 2.0 Membership, Roles or Profile features.  You create several new users and everything works fine.

 

You then copy the application to a remote server (or even another directory on your local server) and run the application.  For some reason it appears that you are able to connect to your membership database just fine – but when you try to login it doesn’t let you.  It doesn’t throw a connection error, but rather when you attempt to login you get an error message that says something like: “Login attempt unsuccessful, please try again.”

 

Cause:

 

The reason this usually happens is because a membership (or roles or profile) provider has been added in the application’s web.config file – but without an applicationName attribute being specified.

Assume below that the applicationName in bold was missing:

 

      <membership>

            <providers>

                <clear/>

                <add name="AspNetSqlMembershipProvider"

                    type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                    connectionStringName="LocalSqlServer"

                    enablePasswordRetrieval="false"

                    enablePasswordReset="true"

                    requiresQuestionAndAnswer="true"

        requiresUniqueEmail="false"

                    passwordFormat="Hashed"

                    maxInvalidPasswordAttempts="5"

                    minRequiredPasswordLength="7"

                    minRequiredNonalphanumericCharacters="1"

                    passwordAttemptWindow="10"

                    passwordStrengthRegularExpression=""

                    applicationName="/"  <!-- Assume it to be missing -- >

                />

            </providers>

      </membership>

 

 

When no applicationName attribute is configured, ASP.NET uses the Application Vroot Path within the web-server to automatically calculate the applicationName to use when adding data to an ASP.NET Application Service database.

 

To see this in action, you can open up your ASPNETDB database, and look within the aspnet_Applications table: See the first & second row.

 

Because I didn’t specify an “applicationName” attribute when I registered users within my application, it calculated the application name as /Security Cafe Site – Which is the name of Application Virtual Path under root.

 

 

 

 

Even this Will do –

 

This works fine when the application continues to run in the “/Security Cafe Site” application virtual path.  But if it is copied to another location or server with a different virtual path -  That is why you’ll get a “Login attempt unsuccessful, please try again.” message when you try to login.

 

 

Resolution –

 

Set the application name to the name of your WebSite name.

 

           

Hope this helps.

 

Arun Manglick

 

Share Authentication Cookies across ASP.NET V1.1 and ASP.NET V2.0 Applications

Hi,

 

This blog post summarizes – Share Authentication Cookies across ASP.NET V1.1 and ASP.NET V2.0 Applications.

 

 

Forms-authentication model has a powerful security structure - That provides the infrastructure plumbing necessary to issue Authentication Tickets to incoming browsers as Http Cookies, and then automatically decrypt them on each request so that you can identify who the incoming browser user is.

 

Problem –

 

Looking at the above approach – There is always a problem while Sharing The Forms-Authentication Ticket when the pages are accessed across multiple applications.

 

 

How to Fix This –

 

Fixing this is pretty easy. Access the link

 

 

One more Problem –

 

The above fix will not work, if the applications are developed using ASP.NET V1.1 and ASP.NET V2.0 seperately.

 

e.g - Build a set of login/membership pages using ASP.NET V2.0 in an application and then have the rest of the sub site which is still running on ASP.NET V1.1

 

 

How to Fix This –

 

Fixing this require one more step.

 

·          Same as above. Access the link

·          In your ASP.NET 2.0 application(s), you’ll also then need to add the new “decryption” attribute to the <machineKey /> element and set its value to be “3DES”.  By default, ASP.NET V2.0 uses a new (stronger) encryption/decryption algorithm.  Changing the value to be “3DES” will have it revert back to the older V1 behavior and allow the cookies to be shared.

 

 

Hope this helps.

 

Arun Manglick

 

Don't forget to when adding providers

Hi,

 

This blog post summarizes – Usability of <clear/> when adding providers.

 

The <providers> section within the web.config file is implemented as a collection, and so it is possible to register multiple providers at the same time (this is useful when you want to have some users authenticated using one Membership store, and others authenticated using a separate Membership store).

 

By default ASP.NET 2.0 registers a set of default SQL Express providers within the root web.config file on your machine that create a SQL Express database within your /app_data directory to store/manage membership/role/profile data when you first access it.  Because this is registered at the machine-wide level, all provider collections by default inherit this registration.  Unless you explictly <clear/> or override the inherited value, your application will have this default membership/role/profile provider registered.

 

Because the above web.config file simply added a new provider -- and didn't clear or replace the default provider registration -- the above application now has two Membership providers configured.  When you do a Membership.CreateUser() call in your code, ASP.NET will attempt to create the user in both membership databases.  If you don't have SQL Express installed on your system, the create-user attempt will fail for this database - which leads to the errors and/or weird behavior above.

 

Solution –

 

Use <clear/> before adding new provider.

 

<membership>

            <providers>

                <clear/>

                <add name="AspNetSqlMembershipProvider"

                    type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

                    connectionStringName="LocalSqlServer"                   

                    applicationName="/"                />

            </providers>

      </membership>

 

           

Hope this helps.

 

Arun Manglick

 

ASP.NET 2.0 Application Services - Use SQL Server 2000 or SQL Server 2005

Hi,

 

This blog post summarizes - Enabling Windows Authentication within an Intranet ASP.NET Web application.

 

Steps:

 

ASP.NET 2.0 includes a number of built-in "Building Block" application services.  We call them "building blocks" because they are useful core frameworks for enabling super-common scenarios with web applications today – and as a result can provide significant productivity wins and time-savings for developers.

 

These include:

 

·          A Membership API for managing usernames/passwords and secure credential management,

·          A Roles API that supports mapping users into logical groups,

·          A Profile API for storing arbitrary properties about both authenticated and anonymous users visiting a web site (for example: their zipcode, gender, theme preference, etc),

·          A Personalization API for storing control customization preferences (this is most often used with the WebPart features in ASP.NET 2.0),

·          A Health Monitoring API that can track and collect information about the running state and any errors that occur within a web application, and

·          A Site Navigation API for defining hierarchy within an application and constructing navigation UI (menus, treeviews, bread-crumbs) that can be context specific based on where the current incoming user is in the site.

 

These APIs are designed to be pluggable and implementation agnostic. This means that the APIs do not hardcode the details of where data is stored with them. 

Instead, these APIs call into "Providers", which are classes that implement a specific "Provider Contract" – which is defined as an abstract class with a defined set of methods/properties that the API expects to be implemented.

 

e.g

 

System.Web.Security.SqlMembershipProvider;

System.Web.Security.ActiveDirectoryMembershipProvider;

System.Web.Profile.SqlProfileProvider;

System.Web.Security.SqlRoleProvider;

System.Web.UI.WebControls.WebParts.SqlPersonalizationProvider;

System.Web.XmlSiteMapProvider;

 

 

ASP.NET 2.0 ships with a number of built-in providers including:

 

·          A SQL Express provider for going against local SQL Express Databases,

·          SQL 2000/2005 providers that work against full-blown SQL Servers,

·          An Active Directory Provider that can go against AD or ADAM implementations, and

·          An XML provider in the case of site navigation that can bind against XML files on the file-system.

 

The beauty of the model is that if you don't like the existing providers that ship in the box, or want to integrate these APIs against existing data-stores you are already using, then you can just implement a provider and plug it in.  Just implment the new Provider contract as a class and register it in your application's web.config file.

 

 

Default SQL Express Providers vs SQL 2005

 

Using SQL Express:

 

The above mentioned ASP.NET 2.0 application services are configured to use the built-in SQL Express provider.  This provider automatically creates a new DB (ASPNETDB.mdf), the first time when either of these application services are used.

 

The tables within this DB for all the APIs are as below.

 



 

Now, to use the above API services in our aplication, need is to specify the API and the DB connection in Web.Config

 

<configuration>

<connectionStrings>

<clear />

<add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;

                                           AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"

                                           providerName="System.Data.SqlClient" />

</connectionStrings>

</configuration>                  

 

 

 

<system.web>

 

<profile defaultProvider=" AspNetSqlProfileProvider">

<providers>

<add name="AspNetSqlProfileProvider"

        connectionStringName="LocalSqlServer" applicationName="/"

        type="System.Web.Profile.SqlProfileProvider, System.Web,

        Version=2.0.0.0, Culture=neutral,

        PublicKeyToken=b03f5f7f11d50a3a" />

</providers>

</profile>

 

<membership defaultProvider="SqlMembershipProvider" userIsOnlineTimeWindow="15″>

<providers>

    <clear/>

    <add name="SqlMembershipProviderOther"

         type="SqlProviderOneShot.SqlMembershipProvider"

         requiresQuestionAndAnswer="false"

         connectionStringName=" LocalSqlServer"

         applicationName="EmailEmail"

         enablePasswordRetrieval="false"

         enablePasswordReset="true"

         requiresUniqueEmail="true"

         passwordFormat="Hashed"

         minRequiredNonalphanumericCharacters="0″

         writeExceptionsToEventLog="false"

         minRequiredPasswordLength="1″

         passwordStrengthRegularExpression=""

         passwordAttemptWindow="10″

         maxInvalidPasswordAttempts="8″/>

</providers>

</membership>

 

 

<roleManager defaultProvider="MyRoleProvider" >

      <providers>

        <add

          name="MyRoleProvider"

          type="System.Web.Security.SqlRoleProvider"

          connectionStringName=" LocalSqlServer " />

     </providers>

</roleManager>

 

</system.web>

 

 

Using SQL Server 2000/2005:

 

Now, if we want to use the full-blown SQL Server than we need to create a new DB and then create Tables similar to the tables in the ASPNETDB.mdf .

 

Steps:

 

·          Create or obtain a blank SQL database instance

·          Create ASP.NET schemas like ASPNETDB.mdf within SQL DB. To do this run the aspnet_regsql.exe on .Net command prompt. This utility can be run in either a GUI based mode or with command-line switches. Better you choose GUI based.

 

For reference follow the link - http://weblogs.asp.net/scottgu/archive/2005/08/25/423703.aspx

 

 

·          Point your web.config file at the new SQL Database

 

ASP.NET 2.0 ships with a built-in connection string called "LocalSqlServer" which by default is configured to use a SQL Express database, and which by default the Membership, Roles, Personalization, Profile and Health Monitoring services are configured to use. To use SQL database is to just replace the connectionstring value of this "LocalSqlServer" setting in your app's local web.config.

 

<connectionStrings>

        <remove name="LocalSqlServer"/>

        <add name="LocalSqlServer" connectionString="Data Source=localhost;Initial Catalog=appservicesdb;Integrated Security=True" providerName="System.Data.SqlClient"/>

</connectionStrings>

 

 

 

 

 

Hope this helps.

 

Arun Manglick

 

Override Custom Error Message for Developers Only

Hi,

 

Here in this post I’ll summarize the details of this approach.

 

Below is the sequence in which the exception/errors are handled in ASP.Net Application.

 

 

·          Page_Error Event Handler  - Remove further propgation by Server.ClearError()

·          ErrorPage attribute at the Page Level

·          Application_Error in Global.asax -  Remove further propgation by Server.ClearError()

·          Custom Errors in Web.config (If Any) – And if the page is not found at this last level it handles the error to the OS.

 

 

Let’s consider you have configured the Custom Errors approach. I.e You have not used either of first three. In this case whenever exception/errors occurs, a custom page will be flushed to the user.

 

However, suppose you need that – Custom Errors should only be shown to Non-Developers. The developers should see a long techy message. To do this use Application_Error as below.

 

 

void Application_Error(object sender, EventArgs e)

    {

        if (Context != null && Context.User.IsInRole("Developer"))

        {

            Exception err = Server.GetLastError();

            Response.Clear();

 

            Response.Write("<h1>" & err.InnerException.Message & "</h1>");

            Response.Write("<pre>" & err.ToString & "</pre>");

 

            Server.ClearError();

        }

    }

 

 

 

 

Hope this helps.

 

 

Thanks & Regards,

Arun Manglick || Senior Tech Lead