Raza Ali

Raza Ali I am Currently working as an Architect and a Consultant at a Microsoft partner company. I have broad technology interests but find myself more interested in ASP.Net and backend server techonlogies. This blog for me is a means to share whatever I come to learn. Hope you find something useful here.
E-mail me Send mail
MCTS

Recent comments

Recent posts

Files I've Posted

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010

Sign in

Saving/Retrieving images from SQL Server for ASP.Net Website

by raza 8/5/2008 10:52:46 PM

For one of the projects I am working on these days, I had to provide image hosting from the database meaning the images will be stored in the the database when the user uploads them to my site and is fetched from the database on request. After looking around for hardly 15mins the solution was there. SQL Server's image type came to the rescue but I read in some article that MS was going to discontinue this type and only keep the varbinary for such purposes. While its available in SQL Server 2005, why not take advantage of it.

Let's say we have the following table structure which represents the different sites of an organization. Each site has its own logo and needs to be displayed on different pages of our site.

image

Let's first look at how to save the image in the database when it is uploaded from one of the pages in the website (in case its desktop application there is one less step here).

image

If this were a desktop application I would have probably written:

image

Let me explain the code here a little. The fuLogoImage is a FileUpload control and the code is simply checking if a file was uploaded through the control when the pages was submitted, using its HasFile property. If it was, then simply get the file as a byte[] and use ADO.Net to write this array to a Image column in the database. In my case here I have used a Typed Dataset, hence the "ta" before the Sites representing the TableAdapter for the Sites table. Notice I simply passed the byte[] without any additional work. You can also simply write a Insert command with the byte[] placed at the right column and ADO.Net will take care of the streaming to database.

Once the file is stored in the database in the LogoImage column, we can then start writing code to display it in our pages. Since the images are being generated dynamically for a given ID, we need a method to bypass the static URL requirement of the image tag. Luckily, there is such a trick and it is called a HTTP Handler. Everything you service in a web server is handled through an HTTP handler, including your aspx pages. If you go to IIS Manager and open the properties of any application, then open the configuration tab, you will see a window like this:

image

which shows the handler for each type of file the web server (IIS) will be dealing with. Basically, IIS itself does not know how to deal with a file so it passes on the handing responsibility to one of these programs depending on the extension. If I double click the aspx handler description, I see:

image

So, all http commands for the aspx file are being handled by the asp_isapi.dll extension. This extension is responsible for all the aspx page interpretation, session handling and other features that you like so much. Coming back to our topic, we need to write a handler just like that to generate images dynamically. For that we will use the ASP.Net generic http handler. If you go to your project and add new item, you will see:

image

Adding this will give you something like this:

image

The handler implements the IHttpHandler interface which has the capability to handle all the Http commands like Get and Post etc. I you would like session handing capabilities then you can also implement the IRequiresSessionState or IReadOnlySessionState interface. Now, the IHttpHandler contains a function called ProcessRequest where we need to implement our custom logic. I am passing the image ID as a query string parameter to the handler and the code looks like the following:

public void ProcessRequest (HttpContext context) 
    {
        byte[] image;
        
        context.Response.ContentType = "image/jpeg";

        decimal siteid = decimal.Parse(context.Request.QueryString["SiteID"]);

        if (siteid == -1)
            image = GetNoLogo(context);
        else
        {
            ECFormsTableAdapters.Lkp_SitesTableAdapter taSites = new ECFormsTableAdapters.Lkp_SitesTableAdapter();
            ECForms.Lkp_SitesDataTable dtSites = taSites.GetDataBySiteID(siteid);

            if (dtSites.Count == 0 || dtSites[0].LogoImage == null)
                image = GetNoLogo(context);
            else
                image = dtSites[0].LogoImage;
        }
        context.Response.BinaryWrite(image);
    }

Here I fetch the image from the database and then simply write it to the response stream using the proper content type description. In my example I have placed the limit that the image has to be a jpg, as visible from the content type header. The GetNoLogo function basically replaces the image with a default logo that is stored in the file system. The code looks something like this:

private byte[] GetNoLogo(HttpContext context)
    {
        byte[] imageNoLogo;
        string nologopath = context.Server.MapPath("images/nologo.jpg");
        
        lock (lockObject)
        {
            FileStream fs = new FileStream(nologopath, FileMode.Open,FileAccess.Read);
            imageNoLogo = new byte[fs.Length];
            fs.Read(imageNoLogo, 0, (int)fs.Length);
            fs.Close();
        }        
        return imageNoLogo;
    }

I have put a lock around the file access to prevent multiple threads from simultaneously opening the file. I should use some caching strategy here but for the moment it is good enough. Now to use it in the asp.net code I define a image tag anywhere in the html

<asp:Image ID="ImgLogo" runat="server" Height="52px" />

and make the src look like this through the code in Page_Load:

ImgLogo.ImageUrl = string.Format("ImageHandler.ashx?SiteID={0}", SelectedSite);

and that's it! When the browser renders this image it calls the associated url for image and at the web server this url is a handler that dynamically renders the image using the parameter. Create a site with dynamic images from the database now. Obvious advantages include:

- Single storage of all files.

- Centralized management

- Database searching and meta data storage facility for the images

- Centralized backup and recovery

Some disadvantages include:

- Performance hit

- Single point of failure

But then, the choice is yours. Depending on the situation you have to pick one over another.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

ASP.Net | SQL Server

Using AD membership provider in ASP.Net

by raza 7/21/2008 11:31:44 AM

Recently I needed to provide authentication from the Active Directory for a custom ASP.Net form. I was pleasantly surprised to find that there is a ready-made authentication provider available for active directory, similar to the database one.

Despite the provider the second problem was that I had to put the authentication inside a custom form where I could not use the Login controls that are integrated with the membership providers for ASP.Net. Then with little investigation I found out that it was very simple to do that as well and all the functionality of the provider could be accessed from outside the controls. I became a fan of ASP.Net once again.

The steps to use it in the custom form are very simple. Since this is forms authentication, you put in the relevant entry in web.config.

<authentication mode="Forms">
  <forms
      name=".ADAuthCookie"       
      timeout="10" />
</authentication>

This put ASP.Net in forms authentication mode, hence next step is to define the details of which forms will be used for this purpose.

<forms name=".ASPXAUTH" loginUrl="login.aspx" 
       defaultUrl="default.aspx" protection="All" timeout="30" path="/" 
       requireSSL="false" slidingExpiration="true"
       cookieless="UseDeviceProfile" domain="" 
       enableCrossAppRedirects="false">
  <credentials passwordFormat="SHA1" />
</forms>

The above tag specifies the default.aspx page as the url it gets redirected to once the user is authenticated. The authentication is actually done on the login.aspx page. The rest of the parameters define what will be the behavior of the authentication mode, for example, that there will be a 30min timeout with sliding window, it will not use SSL and cookie creation will depend on the client device.

The next step is to secure all the files so that only a select few are available without authentication and the rest can be accessed only after the user has successfully logged in. The few available anonymously contains the login page.

<authorization> 
  <deny users="?" />
  <allow users="*" />
</authorization>
 
The above tags say that all anonymous accesses should be denied and all authenticated access allowed. The reason for saying both of them is that you can choose what to deny and what to allow. For example you can deny all then allow a selected number of files and directories. This kind of rule logic is common in firewall configurations.
 
Since we are authenticating through AD, we need to define it as a data source. This data source will first of all have a connection string.
 

<connectionStrings> <add name="ADConnectionString" connectionString="LDAP://testdomain.test.com/DC=testdomain,DC=test,DC=com" /> </connectionStrings>

The only thing that needs explanation here is the connectionstring parameter. It is defining that AD will be accessed through LDAP protocol and what follows the // is the server name holding AD, you don't necessarily need to specify the actual server name if the domain name resolves to it. The DC part specifies the domain name. So if its contoso.com then it will be DC=contoso, DC=com. The reason I understand for this is that AD is a hierarchical database and hence we are defining this part of the connection string in a tree fashion. After the connection string we need to define the actual membership provider that will service all authentication queries.

<membership defaultProvider="MyADMembershipProvider"> <providers> <add name="MyADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ADConnectionString" connectionUsername="testdomain\administrator" connectionPassword="password" attributeMapUsername="sAMAccountName" /> </providers> </membership>

It references the assembly required and provide a username/password that will be used to query the active directory server. This user should have enough rights to read the active directory. The last attribute attributeMapUsername is set to "sAMAccountName" which means the user will provide username in the format domain\username. While if this was not set then the default format is username@domain.  This is all there is to it for the setup. Now lets use it in the code.

The first thing I intend to do is to validate a user from active directory using the submitted username and password. Once the user is authenticated we would typically like to redirect user to the first page, which in our case is defined as the default page in forms configuration.

if (Membership.ValidateUser(username, password))
{
//valid user do your thing here
FormsAuthentication.RedirectFromLoginPage(username, false);
}
else
{
//failed, give some message
}

After authentication from the membership provider we can use the FormsAuthentication class to send the user to the default page. The second parameter which is 'false' in our case relates to the 'Remember Me' in the login box, which allows you to put a persistent cookie.

The followingMSDN article gives you some recommendations on making your website more secure:

Security Considerations

Failing to protect authentication tickets is a common vulnerability that can lead to unauthorized spoofing and impersonation, session hijacking, and elevation of privilege. When you use forms authentication, consider the following recommendations to help ensure a secure authentication approach:

  • Restrict the authentication cookie to HTTPS connections. To prevent forms authentication cookies from being captured and tampered with while crossing the network, ensure that you use Secure Sockets Layer (SSL) with all pages that require authenticated access and restrict forms authentication tickets to SSL channels.
  • Partition the site for SSL. This allows you to avoid using SSL for the entire site.
  • Do not persist forms authentication cookies. Do not persist authentication cookies because they are stored in the user's profile on the client computer and can be stolen if an attacker gets physical access to the user's computer.
  • Consider reducing ticket lifetime. Consider reducing the cookie lifetime to reduce the time window in which an attacker can use a captured cookie to gain access to your application with a spoofed identity.
  • Consider using a fixed expiration. In scenarios where you cannot use SSL, consider setting slidingExpiration="false".
  • Enforce strong user management policies. Use and enforce strong passwords for all user accounts to ensure that people cannot guess one another's passwords and to mitigate the risk posed by dictionary attacks.
  • Enforce password complexity rules. Validate passwords entered through the CreateUserWizard control, by setting its PasswordRegularExpression property to an appropriate regular expression. Also configure the membership provider on the server to use the same regular expression.
  • Perform effective data validation on all requests. Perform strict data validation to minimize the possibilities of SQL injection and cross-site scripting.
  • Use distinct cookie names and paths. By ensuring unique cookie names and paths, you prevent possible problems that can occur when hosting multiple applications on the same server.
  • Keep authentication and personalization cookies separate. Keep personalization cookies that contain user-specific preferences and non-sensitive data separate from authentication cookies.
  • Use absolute URLs for navigation. This is to avoid potential issues caused by redirecting from HTTP to HTTPS pages.

Once you are done with the authentication there are a number of features that you might want to explore. For example, one thing I needed was the ability to query the AD for verifying usernames when they are put in one of the admin screens. For that you need to put the line enableSearchMethods ="true" in your membership provider and you will have access to search functions.

MembershipUserCollection users = Membership.FindUsersByName(username);
string email = users[username].Email;

The search function returns you a collection of users matching the criteria and once you get this collection you can get individual users properties set in the AD. For example in this case I needed the email of the user to send an information email regarding the executed process.

I am pasting links to two MSDN articles which will allow you to explore this further. At the end of the first article you will find a table of all the properties you can set in you membership provider which will allow you to use almost all services provided by the AD.

How To: Use Forms Authentication with Active Directory in ASP.NET 2.0

The second article discusses specifically the security considerations of using this membership provider and this might of use when you are looking to tune the security of your site for a specific environment.

How To: Protect Forms Authentication in ASP.NET 2.0

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

ASP.Net

Powered by BlogEngine.NET 1.4.0.0
Theme by Mads Kristensen