Introduction
This article will expand on the already existing knowledge base of ASP.NET Forms Authentication and how to protect specific sub-directories or web pages. If you need to know what Forms Authentication is, I suggest you learn about it elsewhere.
The Problem
If you've used Forms Authentication, then you know how powerful it can be with ASP.NET, C#, and Framework 2.0. I came across an issue when I developed a web application and built an administration module to manage the website. Usually, I put all the pages for administration in a sub-directory called "Admin". This article will show you how to protect particular sub-directories or pages using almost no code.
The Solution
The Website Structure
Let us start with our fictional website with many pages and many subdirectories. The one sub-directory we want is ~/Admin.
1. Web.config - Secure the Directory/Page
First, we need to protect the directory or page. In the root directory in the web.config file, add this anywhere, but within the configuration
tags. I usually add it to the bottom:
<configuration>
...
<location path="Admin">
<system.web>
<authorization>
<allow users="AdminLogin"/>
<allow users="AdminBackupLogin"/>
<deny users="?" />
</authorization>
</system.web>
</location>
...
</configuration>
If we want to secure a page, we would just change "Admin" to "Page1.aspx" or "~/Directory/Page2.aspx". Also, note that I've set the users I will allow and those I will not allow.
"?" means all unauthenticated, anonymous users. "*" means everyone. Be sure to have "deny" at the end; otherwise, the parsing will stop at "deny" and will never read the "allow". So I will allow the users AdminLogin and AdminBackupLogin, but deny anyone who is unauthenticated or anonymous.
2. Create a Login Form
Next, we need to create AdminLogin.aspx in the root directory. Or for that matter, any directory that is not protected.
HTML
<table>
<tr>
<td>Admin Login ID: </td>
<td><asp:TextBox ID="Username" runat="server" Width="150" /></td>
</tr>
<tr>
<td>Admin Login Password: </td>
<td><asp:TextBox ID="Password" runat="server"
Width="150" TextMode="Password" /></td>
</tr>
<tr><td></td>
<td><asp:Button ID="btnSubmit" runat="server"
Text="Login" OnClick="btnSubmit_Click" /></td>
</tr>
<tr>
<td colspan="2">
<asp:Label ID="lblMessage" runat="server"
ForeColor="red" Text=""></asp:Label>
</td>
</tr>
</table>
Code-Behind
public void btnSubmit_Click(object sender, EventArgs e)
{
lblMessage.Text = "";
if (FormsAuthentication.Authenticate(Username.Text, Password.Text))
{
FormsAuthentication.RedirectFromLoginPage(Username.Text, false);
}
else
{
lblMessage.Text = "<b>Something went wrong...</b> please " +
"re-enter your credentials...";
}
}
3. Web.config - Attach the Login Form to Forms Authentication and Assign Valid Users
Edit Web.config again, but this time, we are going to attach the login form AdminLogin.aspx.
<configuration>...
<system.web>
...
<authentication mode="Forms">
<forms name="AdminLogin123"
loginUrl="AdminLogin.aspx"
protection="All"
path="/" >
<credentials passwordFormat="Clear">
<user name="AdminLogin" password="AdminPassword" />
<user name="AdminBackupLogin" password="AdminBackupPassword" />
</credentials>
</forms>
</authentication>
...
</system.web>
...
</configuration>
Authentication is now in Forms mode. I've created a <Forms>
object with a unique name for the cookies. LoginURL
is the location of the login form, Protection
will encrypt and authenticate the cookie, and Path
is the client storage of the cookie.
The next important element is the list of users we will use to authenticate against. Please remember that this is a high-level example, so the login name and password are stored in clear text; however, storing password in clear text is unreasonable for security. Valid values include Cear, SHA1, and MD5. SHA1 and MD5 are both hashing algorithms that make storing passwords in the Web.config more secure.
If I wanted to, I could use a SQL query connection to validate in a database table instead of keeping a long list of valid users, or even do LDAP lookup. For SQL validation, I would have to modify AdminLogin.aspx, and instead of using FormsAuthentication.Authenticate
to validate user credentials, it will call a local method (i.e., CheckValidity
) which makes use of a SQL query to determine whether the credentials are valid. The sample code follows:
Bool CheckValidity(String username, String password)
{
SqlConnection conn = new SqlConnection ("server=localhost;
database=weblogin; uid=sa; pwd=");
try
{
conn.Open();
String sqlQuery = "select count (*) from users
where username =\' " + username +
"\' and password=\' " + password + "\' ";
SqlCommand command = new SqlCommand(sqlQuery, conn);
int count = (int)command.ExecuteScalar();
return (count>0);
}
catch (SqlException e)
{
return false;
}
finally
{
conn.Close();
}
}
Windows Authentication
I could have easily used Windows Authentication. In order to use Windows Authentication, make sure the security properties of your website is set to Basic, Digest Authentication, or Integrated Windows Authentication. Integrated Windows Authentication is the usual setting. This will get the credentials of the connecting user, and compare to the Windows Domain, and authenticate by returning a token. This is all done by IIS, thus no code is needed.
Windows authentication looks like this:
<configuration>...
<system.web>
...
<authentication mode="Windows"/>
...
</system.web>
...
</configuration>
And securing the directory or pages to specific users will look like this:
<configuration>
...
<location path="Admin">
<system.web>
<authorization>
<allow users="domain\user1, domain\user2"/>
<deny users="?" />
</authorization>
</system.web>
</location>
...
</configuration>
Conclusion
This article solves the issue of leaving the website open, but protecting key pieces with Forms Authentication. We could easily use Windows Authentication to protect the same items. Please let me know if you would like me to add anything else.