Saturday, December 25, 2010

Guarding Against Session Hijacking in ASP.NET

In this tutorial we're going to be looking at something all .Net developers need to be aware of and guard against, and that is prevent session hijacking. Session hijacking is a form of hacking attack that involves accessing a users session state. While the damage can be as small as having access to someone's shopping cart data, or as severe as hijacking a session that contains a users personal, or financial, information. This kind of attack is generally carried out in two forms:
•ID Guessing
•Solen ID's
Session ID guessing is harder for an ASP.NET website because ASP.NET employs a random 120-bit number, but stealing a session ID is more prevalent. There are three main ways hackers steal session ID's:
•Cross-Site Scripting (XSS)
•Main-In-The-Middle Attack
•Gain access to the users cookie
The main reason stealing session ID's from an ASP.NET application takes such little skill from the hacker is because ASP.NET doesn't encode any information in the session cookie other than the ID itself. If the server receives a Request with a valid ID it accepts the Request, no questions asked. Though it is impossible to create a fool-proof defense against such attacks, the developer can take certain steps to make them harder to pull off, and that is what this tutorial looks at.
In this tutorial we will look at intercepting the session cookie (before ASP.NET sees it), taking the MAC (Message Authentication Code), and creating our own MAC, based on the session ID, the users IP address and their User Agent. Our class will also rely on a validation key that is stored in the web.config file. The key will be based on a MD5 hash of a string, and should be different for all applications this is used for. Make sure your key is long and random, shorter keys are easier to guess. We will also be creating a custom Exception that will be used in the class.
Before we start, here's a short method you can use to create the MD5 hash for your validation key. It employs the MD5CryptoServiceProvider Class in the System.Security.Cryptography Namespace:
view sourceprint?
01 /// <summary> 
02 /// method to generate a MD5 hash of a string 
03 /// </summary> 
04 /// <param name="strToHash">string to hash</param> 
05 /// <returns>hashed string</returns> 
06 public string GenerateMD5(string str) 
07 { 
08     MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); 
09   
10     byte[] byteArray = Encoding.ASCII.GetBytes(str); 
11   
12     byteArray = md5.ComputeHash(byteArray); 
13   
14     string hashedValue = ""; 
15   
16     foreach (byte b in byteArray) 
17     { 
18         hashedValue += b.ToString("x2"); 
19     } 
20   
21     return hashedValue; 
22 }
Now that we have the creation of the key covered, lets start making our secure session class. First and foremost, as with all classes you write, you need to make sure you have the proper Namespace's for your class, in this case we need seven of them:
view sourceprint?
1 using System; 
2 using System.Web; 
3 using System.Text; 
4 using System.Web.Security; 
5 using System.Configuration; 
6 using System.Security.Cryptography; 
7 using System.Globalization;
Now we need any global variables, in this case we have a single global, the variable that will hold the value of
our key
view sourceprint?
1 private static string secretKey = null;
This class is designed to operate completely silent, meaning it works in the background with zero interaction
from the developer whatsoever. Our class inherits the IHttpModule Interface.
First thing we will do is call the Init() and Dispose() Methods of the IHttpModule. In the Init() Method we will
first check the value of our global variable secretKey, if it doesn't have a value we will initialize it.
We then wire up two Event Handlers, these will handle the BeginRequest Event and the EndRequest Event of
the HttpApplication Class. The Dispose() method is a blank method, but it is required when inheriting from the
IHttpModule Interface.
view sourceprint?
01 /// <summary> 
02 /// method to initialize our class when the page is initialized 
03 /// </summary> 
04 /// <param name="application"></param> 
05 public void Init(HttpApplication application) 
06 { 
07     //find out of we have a validation key, if we dont initialize it 
08     if (secretKey == null) secretKey = GetKey(); 
09   
10     //register event handlers for the BeginRequest and EndRequest events 
11     application.BeginRequest += new EventHandler(onbeginRequest); 
12     application.EndRequest += new EventHandler(onendRequest); 
13 } 
14   
15 public void Dispose()  
16 {  
17 }
Now the Event Handler, we have two to write up:
•onbeginRequest: Handles all transactions at the start of the request cycle
•onendRequest: Handles all transactions at the very end of the request cycle
onbeginRequest is where we do the bulk of our work. The first thing we do is grab the current Request, this
allows us access to all the information we need, including the current ASP.NET_SessionID cookie. Once we
have the cookie in our possession we first check it's length, if it's less than 24 long we throw an exception
because that tells us the cookie doesn't have a MAC attached. If we make it past that check we then grad
the session ID and the MAC value off of the cookie (using string manipulation), then compare the MAC value
with our generated MAC. If they don't match we throw an exception because something's happened to the
cookie. Barring any errors we quickly assign the session ID to the value of the cookie, all before ASP.NET
see's it.
onbeginRequest:
view sourceprint?
01 /// <summary> 
02 /// method for handling the HttpApplication.BeginRequest event 
03 /// </summary> 
04 /// <param name="sender"></param> 
05 /// <param name="e"></param> 
06 public void onbeginRequest(Object sender, EventArgs e) 
07 { 
08     //get the current Request 
09     HttpRequest currentRequest = ((HttpApplication)sender).Request; 
10   
11     //get the ASP.NET_SessionId cookie from the Request object 
12     HttpCookie requestCookie = RetrieveRequestCookie(currentRequest, "ASP.NET_SessionId"); 
13   
14     //check to see if the cookie exists (if == null) 
15     if (requestCookie != null) 
16     { 
17         //if the length is less than 24 we dont have a MAC so we need to throw an exception
            (our custom exception) 
18         if (requestCookie.Value.Length <= 24) throw new SessionerrorException("Invalid Session"); 
19   
20         //get the session id
21         string sessionID = requestCookie.Value.Substring(0, 24); 
22   
23         //get the MAC 
24         string mac = requestCookie.Value.Substring(24); 
25   
26         //create a new MAC based on the session id and some of the users info (user agent, etc) 
27         string macCompare = CreateMAC(sessionID, currentRequest.UserHostAddress,
                                                                                              currentRequest.UserAgent, secretKey); 
28   
29         //check to see if the MAC's match, if not we have a problem 
30         if (String.CompareOrdinal(mac, macCompare) != 0)
                                           throw new SessionerrorException("Invalid Session"); 
31   
32         //set the cookies value to the session id
33         requestCookie.Value = sessionID; 
34     } 
35 }
In the onendRequest we grab the response cookie and make sure it isn't null (that would mean someone
has hijacked the session), if all is OK we append our newly created MAC value to the end of the cookie,
and this can be compared during the next BeginRequest Event, which will be the next page load for the
application.
onendRequest:
view sourceprint?
01 /// <summary> 
02 /// method for handling the HttpApplication.EndRequest event 
03 /// </summary> 
04 /// <param name="sender"></param> 
05 /// <param name="e"></param> 
06 public void onendRequest(Object sender, EventArgs e) 
07 { 
08     //capture the current request 
09     HttpRequest currentRequest = ((HttpApplication)sender).Request; 
10   
11     //get the session cookie 
12     HttpCookie sessionCookie = RetrieveResponseCookie(((HttpApplication)sender).Response,
                                                                                                                                   "ASP.NET_SessionId"); 
13   
14     //make sure the cookie isnt null 
15     if (sessionCookie != null) 
16     { 
17         //add our newly generated MAC to the cookie at the end of the request 
18         sessionCookie.Value += CreateMAC(sessionCookie.Value, currentRequest.UserHostAddress,
                                                            currentRequest.UserAgent, secretKey); 
19     } 
20 }
In our Init() we called a method GetKey, which we use to initialize our secretKey variable. This method
simply checks the web.config file for the SessionKey section and returns the value. An exception will be
thrown if this value doesn't exist in the web.config:
view sourceprint?
01 /// <summary> 
02 /// method for retrieving the validation key from the web.config 
03 /// </summary> 
04 /// <returns></returns> 
05 private string GetKey() 
06 { 
07     //get the key 
08     string validationKey = ConfigurationManager.AppSettings["SessionKey"]; 
09   
10     //check for a null or empty key. If so throw our exception 
11     if (validationKey == null || validationKey == String.Empty)
                                            throw new SessionerrorException("SessionKey not found. Application  
12   
13 ending"); 
14   
15     //return the key 
16     return validationKey; 
17 }
We have four more methods to look at in this class (before we get to our custom Exception class).
They are
•RetrieveRequestCookie: Used to retrieve the current Request cookie.
•RetrieveResponseCookie: Used to retrieve the current Response cookie.
•FindTheCookie: Used to find a cookie by it's name.
•CreateMAC: Used to generate our custom MAC value for the session cookie.
view sourceprint?
01 /// <summary> 
02 /// method for retrieving the Request cookies 
03 /// </summary> 
04 /// <param name="currentRequest"></param> 
05 /// <param name="cookieName"></param> 
06 /// <returns></returns> 
07 private HttpCookie RetrieveRequestCookie(HttpRequest currentRequest, string cookieName) 
08 { 
09     HttpCookieCollection cookieCollection = currentRequest.Cookies; 
10     return FindTheCookie(cookieCollection, cookieName); 
11 } 
12   
13 /// <summary> 
14 /// method for retrieving the Response cookies 
15 /// </summary> 
16 /// <param name="currentResponse"></param> 
17 /// <param name="cookieName"></param> 
18 /// <returns></returns> 
19 private HttpCookie RetrieveResponseCookie(HttpResponse currentResponse, string cookieName) 
20 { 
21     HttpCookieCollection cookies = currentResponse.Cookies; 
22     return FindTheCookie(cookies, cookieName); 
23 }
FindTheCookie takes an HttpCookieCollection and a name as a parameter. From there it loops the length
of the HttpCookieCollection passed to it comparing each cookie name with the name provided. If it finds
a match it returns that HttpCookie, otherwise it returns null
view sourceprint?
01 /// <summary> 
02 /// method for retrieving a cookie by it's name 
03 /// </summary> 
04 /// <param name="cookieCollection">the cookie collection to search</param> 
05 /// <param name="cookieName">the cookie we're looking for</param> 
06 /// <returns></returns> 
07 private HttpCookie FindTheCookie(HttpCookieCollection cookieCollection, string cookieName) 
08 { 
09     for (int i = 0; i < cookieCollection.Count; i++) 
10     { 
11         if (string.Compare(cookieCollection[i].Name, cookieName, true, CultureInfo.InvariantCulture) == 0) 
12             return cookieCollection[i]; 
13     } 
14   
15     return null; 
16 }
Now we just need to generate a MAC for our session cookie. This is done by appending the current session
id with the first segment of the users IP address and his User Agent. We then use the HMACSHA1 Class to
generate a new MAC for the cookie:
view sourceprint?
01 /// <summary> 
02 /// method for generating a new MAC for our session cookie 
03 /// </summary> 
04 /// <param name="id">current session id</param> 
05 /// <param name="ipAddress">ip address of the current Request</param> 
06 /// <param name="userAgent">current user's User Agent</param> 
07 /// <param name="validationKey">validation key from the web.config</param> 
08 /// <returns></returns> 
09 private string CreateMAC(string id, string ipAddress, string userAgent, string validationKey) 
10 { 
11     //create an instance of the StringBuilder with a max length of 512 
12     StringBuilder sb = new StringBuilder(id, 512); 
13   
14     //append the first segment of the user's ip address to the string 
15     sb.Append(ipAddress.Substring(0, ipAddress.IndexOf('.', ipAddress.IndexOf('.') + 1))); 
16   
17     //append the users User Agent to the string 
18     sb.Append(userAgent); 
19   
20     using (HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(validationKey))) 
21     { 
22         return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()))); 
23     } 
24 }
Creating a custom Exception class is fairly straight forward so I am just going to post the code with little or
no explanation. For more information on creating your own exception classes, read this on Creating Custom
Exception In .Net, it has some pretty good information in it.
view sourceprint?
01 [Serializable] 
02 public class SessionerrorException : Exception 
03 { 
04     public SessionerrorException() : base("Invalid Session") { }  
05   
06     public SessionerrorException(string message) : base(message) { } 
07   
08     public SessionerrorException(string message, Exception inner) : base(message, inner) { } 
09   
10     protected SessionerrorException(SerializationInfo info, StreamingContext context)
                                                                                                                : base(info, context) { } 
11 }
Now that we have the coding part done, that is creating the module and the custom Exception, there
are some things we need to add to the web.config file in order to wrap this up. First is the validation
key, which should be a long random string generated with a MD5 hash. This should be placed in the
<appSettings> section of your web.config file, and should look like this
view sourceprint?
1 <appSettings> 
2     <add key="SessionKey" value="3595381625A3DCC07E84E626939254834E0FD16B"/> 
3 </appSettings>
My particular key is a MD5 hash based on a 11 character word (that will remain a secret). The last thing
we need to do is register this HttpModule in our web.config. As you can image this needs to go in the
<httpModules> section of the web.config. That looks like this
view sourceprint?
1 <httpModules> 
2     <add name="SecureSession" type="RLM.Core.Components.Security.SecureSession, SecureSession"/> 
3 </httpModules>
The syntax for registering a module is
Quote
<httpModules>
<add name="YourName" type="YourNamespace.YourClassName, YourProjectName"/>
</httpModules>
There you have it, a way to fight session hijacking in your ASP.NET applications. Remember, there is no
100% foolproof way to prevent this, this class is simply meant as one way to make it harder for hackers
to hijack your users sessions, and thus giving them access to the users information.

Thursday, December 23, 2010

Get .NET Framework version

Check .NET Framework versions
This class has two functions. One gets you the latest version installed. The other one returns a boolean
based on whether a specific version is installed or not.
public class NETVersionChecker
{
    public struct DOTNETVersionInfo
    {
        public double FrameworkVersion;
        public int ServicePack;
    }
    public static bool CheckRequiredDOTNETVersion(DOTNETVersionInfo required)
    {
        bool reslt = false;
        double tmpFramework = 0;
        int tmpSP = 0;
        try
        {
            RegistryKey installed_versions = Registry.LocalMachine.OpenSubKey
            (@"SOFTWARE\Microsoft\NET Framework Setup\NDP", false);
            string[] version_names = installed_versions.GetSubKeyNames();
            string tmpBaseVersion;
            //check each installed version
            foreach (string ver in version_names)
            {
                //set default values
                tmpFramework = 0;
                tmpSP = 0;
                tmpBaseVersion = string.Empty;
                try
                {
                    //version names start with 'v', eg, 'v3.5' which needs to be
                     trimmed off before conversion
                    string tmpFullVersion = ver.Remove(0, 1);
                    //now remove the minor versions 2.0.5725
                    if (tmpFullVersion.Length > 3)
                    {
                        tmpBaseVersion = tmpFullVersion.Remove(tmpFullVersion.IndexOfAny
                               ((".").ToCharArray(), 2), tmpFullVersion.Length - 3);
                    }
                    else //its just 3 digit version
                    {
                        tmpBaseVersion = tmpFullVersion;
                    }
                    double basicVersion = 0;
                    if (double.TryParse(tmpBaseVersion, out basicVersion))
                    {
                        tmpFramework = basicVersion;
                    }
                }
                catch
                {
                    tmpFramework = 0;
                }
                //The service pack key might not exist so it might throw an error
                try
                {
                    tmpSP = Convert.ToInt32(installed_versions.OpenSubKey(ver)
                       .GetValue("SP", 0));
                }
                catch { }
                if (tmpFramework == required.FrameworkVersion && tmpSP
                    == required.ServicePack)
                {
                    reslt = true;
                    break;
                }
            }
        }
        catch (Exception exp)
        {
            string message = "Error occured:" + exp.Message;
            if (exp is System.Security.SecurityException)
            {
                message += "\n Unable to find .NET Framework version. \n The user does
                  not have the permissions required to access the registry key:\n"
                   + @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP";
            }
            MessageBox.Show(message);
        }
        return reslt;
    }
    public static DOTNETVersionInfo GetLatestDOTNETVersion()
    {
        DOTNETVersionInfo dnVer;
        dnVer.FrameworkVersion = 0;
        dnVer.ServicePack = 0;
        try
        {
            RegistryKey installed_versions = Registry.LocalMachine.OpenSubKey
               (@"SOFTWARE\Microsoft\NET Framework Setup\NDP", false);
            string[] version_names = installed_versions.GetSubKeyNames();
            //version names start with 'v', eg, 'v3.5' which needs to be trimmed off
               before conversion
            double Framework = Convert.ToDouble(version_names[version_names.Length - 1]
                               .Remove(0, 1), CultureInfo.InvariantCulture);
            dnVer.FrameworkVersion = Framework;
            //The service pack key might not exist so it might throw an error
            int SP = 0;
            try
            {
                SP = Convert.ToInt32(installed_versions.OpenSubKey(version_names
                                        [version_names.Length - 1]).GetValue("SP", 0));
            }
            catch { }
            dnVer.ServicePack = SP;
        }
        catch(Exception exp)
        {
            string message = "Error occured:" + exp.Message;
            if (exp is System.Security.SecurityException)
            {
                message += "\n Unable to find .NET Framework version. \n The user does
                         not have the permissions required to access the registry key:\n"
                        + @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP";
            }
            MessageBox.Show(message);
        }
        return dnVer;
    }

How to prevent a drag and drop text and Copy paste text in your textbox control

You can prevent a drag and drop value from your page and a copy paste text in your textbox.

for ex. I have a textbox


<asp:TextBox ID="txtSeqNumber" runat="server"  Width="150">

How i can prevent this? Most of the time this error is reported by testers. The simplest solution is as
follows

Just add following javascript in the code



<asp:TextBox ID="txtSeqNumber" runat="server" onDrop="blur();
return false;" onpaste="return false"  Width="150">

Because of blur() function the txtSeqNumber lost its focus and we are not able to darg and drop the text.
 
 

Fixed or Sticky header or ASP.NET webpage

If you want the header section of the website to be fixed on top with menus and other information,
you can simply use the below CSS to do so:

        .header
{
background-color:Gray;
width:100%;
height:100px;
position:fixed;
top:0px;
margin:0px;
padding:0px;
}
body
{
padding-top:110px;
margin:0px;
}

Use it like:

        <div class="header">   
Header content goes here...

</div> <div>
Body content goes here...
</div>

This can be used with Master pages too.

Use of comment when retrieving a specific HTML content part of your compile aspx page

I have one requirement when developing a module.

I need to retrive HTML content of Budget part from the whole rendered HTML Page and save it in to database.
This HTML content needs to displayed in a pop up window on the click of Details link of another page.

Firstly, I think by adding this Budget part into panel or
div tag and then finding the budget part by the id of
 div/panel control be helful. However this Budget part also had div and panel cotrols inside it. So it is very
difficult to get the indexOf ending div tag.

I got a very good solution for this. If you add <!-- Start Budget part --> and <!-- End Budget Part --> in your
aspx code, you can get these comments in the compile page HTML code. Another comment like <%—Start
Budget part --%> <%-- End Budget part --%> will not occur in the compile page HTML.

Example.


int startind = strResult.IndexOf("<!-- Start Budget part -->");
strResult = strResult.Substring(startind);

int endind = strResult.IndexOf("<!-- End Budget part -->");

strResult = strResult.Substring(0, endind);

And thus I only need to find the indexof <!-- Start Budget part --> and <!-- End Budget Part --> and made a
substring to retrieve the content.

Wednesday, December 15, 2010

Creating a custom membership provider

Now that we have enough information on login controls and the underlying provider model that they use,
let's create a custom membership provider to get existing login controls to work against a custom data store.

Note The custom provider will use a SQL Server database called TestDB. TestDB will have a table named
Users with the fields UserID, UserName, and Password and other information, such as e-mail ID and address.
  1. Start Microsoft Visual Studio 2005.
  2. Create a class library project, and give it a name, for example, CustomMembershipProviderLib.
  3. Add a source file to the project, for example, CustomMembershipProvider.cs.
  4. Include System.Web and System.Configuration in the references section.
  5. Verify that the following namespaces are included in the CustomMembershipProvider.cs file.
6. Using System;
7. Using System.Web;
8. Using System.Data;
9. Using System.Configuration;
10.using System.Collections;
11.using System.Web.Security;
12.using System.Collections.Specialized;
Using System.Data.SqlClient;
  1. Inherit the CustomMembershipProvider class with the MembershipProvider class.
14.class CustomMembershipProvider :
MembershipProvider
  1. As we already know, MembershipProvider is an abstract class, so we need to override all the abstract
  2. Methods in the CustomMembershipProvider class. There is a very cool feature in Visual Studio 2005
  3. That does this automatically. As soon as you extend any abstract class, just right-click Abstract class,
  4. And then click Implement Abstract Class. This automatically places declarations for all the abstract
  5. Methods. You will notice that the body for each method contains a common line of code.
Throw new Exception("The method or operation is not implemented.");
This indicates what features are supported by the custom provider.

Note Implementation for the Initialize method is mandatory.
  1. In the custom provider, we will concentrate on providing a few features such as the following:
    • Creating a new user by using the CreateUserWizard control
    • Validating the user credentials by using the Login control
We will implement these features one by one. First, implement the Initialize method. This method is
Called by ASP.NET when the provider is loaded. Also, providers are loaded when the application uses
Them for the first time, and they are created once per application domain.
Public override void Initialize(string name,NameValueCollection config)
{
// Verify that config isn't null
If (config == null)
Throw new ArgumentNullException("config");

// Assign the provider a default name if it doesn't have one
If (String.IsNullOrEmpty(name))
Name = "AspNetCustomMembershipProvider";

// Add a default "description" attribute to config if the
// attribute doesn't exist or is empty
If (string.IsNullOrEmpty(config["description"]))
{
Config.Remove("description");
Config.Add("description", "Custom SQL Provider");
}

// Call the base class's Initialize method
Base.Initialize(name, config);
}
  1. Next, implement the ValidateUser method. It takes the input user name and password and verifies that
  2. The membership data source contains a matching user name and password. If the method returns true, the
  3. Login control allows the user to pass through the verification. Otherwise, it asks for the credentials again.
24.public override bool ValidateUser(string username, string password)
25.{
26. SqlConnection CNN = null;
27. SqlCommand cmd = null;
28. Bool userExists = true;
29. Try
30. {
31. CNN = new SqlConnection();
32. CNN.ConnectionString = "connection string for the existing data source";
33. CNN.Open();
34. String selectQry = "Select query for username and password";
35. Cmd = new SqlCommand(selectQry, CNN);
36. SqlDataReader rdr = cmd.ExecuteReader();
37. If (!rdr.Read())
38. UserExists = false;
39. }
40. Catch (Exception ex)
41. {
42. Throw ex;
43. }
44. Finally
45. {
46. Cmd.Dispose();
47. CNN.Close();
48. }
49. Return userExists;
}
  1. Implement one more method called CreateUser that is called by the CreateUserWizard control. It takes
  2. Input, such as user name, password, e-mail address, and other information, and adds a new user to the existing
  3. Membership data source. It returns the MembershipUser object, which represents a newly created user. It also
  4. Sets MembershipCreateStatus, which tells whether the user was successfully created. If the user was not
  5. Successfully created, we can specify the reason.
55.public override MembershipUser CreateUser(string username, string
56. Password, string email, string passwordQuestion, string
57. PasswordAnswer, bool isApproved, object providerUserKey,
58. out MembershipCreateStatus status)
59.{
60. SqlConnection cnn = null;
61. SqlCommand cmd = null;
62. MembershipUser newUser = null;
63. try
64. {
65. cnn = new SqlConnection();
66. cnn.ConnectionString = "connection string for the existing data source";
67. cnn.Open();
68. string insertQry = "Prepare the Insert query...";
69. cmd = new SqlCommand(insertQry, cnn);
70. cmd.ExecuteNonQuery();
71.
72. // Right now I am giving default values for DateTime
73. // in Membership constructor.
74. newUser = new MembershipUser(
75. "AspNetCustomMembershipProvider",
76. username, null, String.Empty, String.Empty,
77. String.Empty, true, false, DateTime.Now,
78. DateTime.Now, DateTime.Now, DateTime.Now,
79. DateTime.Now
80. );
81. status = MembershipCreateStatus.Success;
82. }
83. catch (Exception ex)
84. {
85. status = MembershipCreateStatus.ProviderError;
86. newUser = null;
87. throw ex;
88. }
89. finally
90. {
91. cmd.Dispose();
92. cnn.Close();
93. }
94. return newUser;
}
  1. The rest of the methods look like those given below. If you wish, you can implement any of them.
96.// MembershipProvider Properties
97.public override string ApplicationName
98.{
99. get { throw new NotSupportedException(); }
100. set { throw new NotSupportedException(); }
101. }
102.
103. public override bool EnablePasswordRetrieval
104. {
105. get { return false; }
106. }
107.
108. public override bool EnablePasswordReset
109. {
110. get { return false; }
111. }
112.
113. public override int MaxInvalidPasswordAttempts
114. {
115. get { throw new NotSupportedException(); }
116. }
117.
118. public override int MinRequiredNonAlphanumericCharacters
119. {
120. get { throw new NotSupportedException(); }
121. }
122.
123. public override int MinRequiredPasswordLength
124. {
125. get { throw new NotSupportedException(); }
126. }
127.
128. public override int PasswordAttemptWindow
129. {
130. get { throw new NotSupportedException(); }
131. }
132.
133. public override MembershipPasswordFormat PasswordFormat
134. {
135. get { throw new NotSupportedException(); }
136. }
137.
138. public override string PasswordStrengthRegularExpression
139. {
140. get { throw new NotSupportedException(); }
141. }
142.
143. public override bool RequiresQuestionAndAnswer
144. {
145. get { return false; }
146. }
147.
148. public override bool RequiresUniqueEmail
149. {
150. get { return false; }
151. }
152.
153. public override MembershipUser GetUser(string username,
154. bool userIsOnline)
155. {
156. throw new NotSupportedException();
157. }
158.
159. public override MembershipUserCollection GetAllUsers(int pageIndex,
160. int pageSize, out int totalRecords)
161. {
162. throw new NotSupportedException();
163. }
164.
165. public override int GetNumberOfUsersOnline()
166. {
167. throw new NotSupportedException();
168. }
169.
170. public override bool ChangePassword(string username,
171. string oldPassword, string newPassword)
172. {
173. throw new NotSupportedException();
174. }
175.
176. public override bool
177. ChangePasswordQuestionAndAnswer(string username,
178. string password, string newPasswordQuestion,
179. string newPasswordAnswer)
180. {
181. throw new NotSupportedException();
182. }
183.
184. public override bool DeleteUser(string username,
185. bool deleteAllRelatedData)
186. {
187. throw new NotSupportedException();
188. }
189.
190. public override MembershipUserCollection
191. FindUsersByEmail(string emailToMatch, int pageIndex,
192. int pageSize, out int totalRecords)
193. {
194. throw new NotSupportedException();
195. }
196.
197. public override MembershipUserCollection
198. FindUsersByName(string usernameToMatch, int pageIndex,
199. int pageSize, out int totalRecords)
200. {
201. throw new NotSupportedException();
202. }
203.
204. public override string GetPassword(string username, string answer)
205. {
206. throw new NotSupportedException();
207. }
208.
209. public override MembershipUser GetUser(object providerUserKey,
210. bool userIsOnline)
211. {
212. throw new NotSupportedException();
213. }
214.
215. public override string GetUserNameByEmail(string email)
216. {
217. throw new NotSupportedException();
218. }
219.
220. public override string ResetPassword(string username,
221. string answer)
222. {
223. throw new NotSupportedException();
224. }
225.
226. public override bool UnlockUser(string userName)
227. {
228. throw new NotSupportedException();
229. }
230.
231. public override void UpdateUser(MembershipUser user)
232. {
233. throw new NotSupportedException();
}
  1. Compile the class library project. It will generate the DLL output.
  2. Open an existing Web site, or create a new Web site.
  3. Add the DLL reference in the Web site.
  4. Register the provider in the Web.config file as follows.
238.
239.
240.
241.
242.
  1. Add a Web Forms page named Login.aspx where the Login control can be used.
244.
245.
246.
247.
  1. Add another Web Forms page named CreateUser.aspx where the CreateUserWizard control can be used.
249.
250.
251.
252.
  1. Run both of the Web Forms pages, and you will see the output.
If you are not using Visual Studio, you can perform the following steps:
  1. Open any text editor.
  2. Create a file named CustomMembershipProvider.cs, and follow the instructions given in steps 5 through 17.
  3. Create a directory under the wwwroot folder.
  4. Start Microsoft Internet Information Services (IIS) Manager, and mark the new directory as the virtual root directory.
  5. Also, ensure that it is configured to run under the Microsoft .NET Framework 2.0 in case another version of the .NET
  6. Framework is installed on the computer.
  7. Copy the Web Forms pages and Web.config in that directory.
  8. Create an App_Code folder under the new directory.
  9. Copy the CustomMembershipProvider.cs file in the App_Code folder.
  10. Run the CreateUser.aspx Web Forms page from IIS Manager.