July 2009 - Posts

August's Toolbox Column Now Online
30 July 09 11:33 AM | Scott Mitchell

My Toolbox column in the August 2009 issue of MSDN Magazine is available online and includes the following reviews:

  • SQL Sets - SQL Sets is a neat tool that allows you to save database queries in a self-contained, portable file format. In a nutshell, you can run a database query, save the results into a file, and then email the file to a colleague who can annotate, sort, filter, and explore the results without needing access to the database or having any knowledge of the SQL syntax. It's a nifty tool for for sharing data with stakeholders without giving them access to the database; it can also be used to compare sets or as a tool for archiving data.
  • Blogs of Note: Stephen Walther on ASP.NET MVC - every developer using ASP.NET MVC should subscribe to author, speaker, and Microsoftie Stephen Walther's blog. They you'll find more than 50 in-depth detailed tips on using ASP.NET MVC along with end-to-end tutorials, and sample chapters from his books.
  • Visual SVN Server - Subversion is a popular and free source code control application. Unfortunately, getting started with Subversion entails a bit of a learning curve. For example, in order to access the Subversion repository over HTTP you need to install and configure the Apache web server. Moreover, many of Subversion's configuration options require tinkering with text-based configuration files. Visual SVN Server is a free product that makes using Subversion as easy as point and click. With a few clicks of the mouse, Visual SVN Server will install and configure Subversion (and Apache) for you. And, once installed, it offers a GUI interface for creating and managing repositories and users.
  • NArrange - NArrange is a free, open source tool that automatically rearranges your C# or Visual Basic source code into a more readable format. From the review: 'By default, NArrange groups constructors, fields, properties, methods, and events into regions and alphabetizes the members within each region. Consecutive blank lines are removed, tabs are converted into spaces, and the using or Import directives within a class file are consolidated and sorted.' You can have NArrange rearrange a particular file, all files in a directory, or all files in a Visual Studio Project or Solution, and NArrange can be launched from the command-line or from within Visual Studio.

This issue did not include a book review.

Enjoy! - http://msdn.microsoft.com/en-us/magazine/ee294454.aspx

As always, if you have any suggestions for products, blogs, or books to review for the Toolbox column, please send them to toolsmm@microsoft.com.

Filed under:
ASP.NET Membership Tip: Requiring New Users To Change Their Password When Logging On For The First Time
27 July 09 10:13 AM | Scott Mitchell

Most Internet-facing websites that support user accounts allow visitors to register an account on their own. Take a site like Facebook, for example. A visitor who wishes to create a new account may do so by visiting the registration page, choosing a username and password, and entering their email address. Implementing such a workflow in an ASP.NET application is relatively straightforward: enable Membership and then create a registration web page, using a CreateUserWizard control to collect user input and create the new account. Out of the box, the CreateUserWizard control prompts a registering user for their username, password, email address, and security question and answer, and then creates a new account and signs the user in once the process completes.

Like the othe Login-related Web controls, the CreateUserWizard can be customized both in its appearance and behavior. There are articles on 4Guys, like Customizing the CreateUserWizard Control, that show how to configure the CreateUserWizard control to include additional questions to the registering user. Examining ASP.NET's Membership, Roles, and Profile - Part 11 explores how to verify a new user's email address by requiring them to click on a link sent in an email message before being signing in for the first time. It's also possible to use the CreateUserWizard control to create user accounts for other people. This is useful for websites that don't allow anonymous users to register, but rather require that the site's administrators manually create each user account.

I recently got an email from a reader who had a site where user accounts were created by a site administrator. Upon creating the account, the new user would receive an email with the username and password the administrator chose for them, along with a link to the sign in page. What this reader wanted to do was require these new users to immediately change their password after signing in for the first time. This functionality is easy to implement with a slight enhancement to a previous article of mine.

Examining ASP.NET's Membership, Roles, and Profile - Part 16 shows how to set up a password expiry policy for an ASP.NET application that uses Membership. In a nutshell, the Membership system exposes a particular user's last password changed date/time via the MembershipUser class's LastPasswordChangedDate property. To quote from the article: “This property is set to the current date and time when the user account is first created or whenever the user changes her password.”

Part 16 shows how to create a page where the user can change their password as well as how to determine if the user's password has expired when they sign on. This latter task is accomplished by creating an event handler for the Login control's Authenticate event and verifying that the number of days since the user last changed their password has not exceeded the password expiry window:

Protected Sub myLogin_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles myLogin.Authenticate
'Are the credentials valid?
If Membership.ValidateUser(myLogin.UserName, myLogin.Password) Then
'Has the password expired?
Dim usrInfo As MembershipUser = Membership.GetUser(myLogin.UserName)

Dim daysSincePwdChange As Integer = Convert.ToInt32(DateTime.Now.Subtract(usrInfo.LastPasswordChangedDate).TotalDays)
If daysSincePwdChange > SecurityUtils.DefaultPasswordExpiryInDays Then
'Password expired, send user to change password
Response.Redirect("~/ChangePassword.aspx?UserName=" & Server.UrlEncode(myLogin.UserName))
e.Authenticated = True 'Credentials valid & password is current
End If
e.Authenticated = False 'Invalid!
End If
End Sub

To force new users to change their password upon signing in for the first time, simply add a condition to the If statement to check whether the user's CreationDate and LastPasswordChangedDate properties are one in the same:

If daysSincePwdChange > SecurityUtils.DefaultPasswordExpiryInDays OrElse userInfo.CreationDate = userInfo.LastPasswordChangedDate Then

That's it!

Filed under:
Session State Not Working? Check Your Web Garden!
23 July 09 08:59 AM | Scott Mitchell

I recently had a client with an urgent issue with a legacy classic ASP web application - users to his site were getting logged out immediately after logging into the site, or after visiting one or two pages. As with most classic ASP applications, user authentication information was being persisted in session state so this symptom implied that session state was not being maintained.

Session state problems usually arise from one of two causes:

  • The user is not properly maintaining the session token. By default, the web server is able to associate a user with a particular session store through the use of a SessionID value that is stored on the user's browser via a cookie. Each time a user makes a request to the server, this cookie is sent along and, lo, the web server knows the user's SessionID. If the user's browser is configured to reject cookies, or if there is a proxy or firewall on the user's side that is stripping out this cookie, then each time the user makes a request to the server it will appear as if it's the user's first request and the server will generate a new SessionID and a new cookie for the user.
  • A proxy server or firewall on the web server side is stripping out the cookie. It may be that the user is correctly storing and sending the session cookie, but that the hardware on the web server's side is stripping out the cookie before it can be read by the ASP or ASP.NET engine, causing the server to generate a new SessionID and a new cookie for the user on each request.

To narrow down the cause of the session problem, the first step is to fire up Fiddler and examine the HTTP traffic when visiting the site through a browser. In particular, look at the HTTP headers to see the Set-Cookie values sent from the server to the browser, and the Cookie value sent from the browser to the server. In running these initial tests, I saw that the browser was clearly sending the SessionID cookie to the server, but that the server was responding with a new SessionID cookie on most requests. However, every now and then the server would not send back a cookie at all, as if it had correctly identified the user by their SessioniD. Hrm....

My first inclination was to make sure that there was no hardware of software stripping out the cookie before it reached the server. To test this, create a page that dumps out the server variables to the screen, which include the cookies sent by the browser. (For code to do this in classic ASP, see: Using the Request.ServerVariables Object; for code in ASP.NET, see: Iterating Through ServerVariables in ASP.NET and C#.) If there was something stripping out the session cookie then when visiting this page through a browser I should see two things: in Fiddler I should see that the browser sends a Cookie HTTP Header, but in the output of the web page that lists the server variables I should not see that cookie information in the HTTP_COOKIE server variable.

However, when I ran the above test I did see the cookie values. So the browser was correctly storing and sending the SessionID cookie, the server was receiving it, but for some reason it was creating a new session store on (almost) every request. What was going on?

After nearly two hours of searching the Internet and poking through IIS settings, it finally became clear - the client had configured the IIS Application Pool to use a 20-thread web garden. When you use a web garden you instruct IIS to spin up multiple worker processes to handle incoming requests. The idea is that each worker process can be migrated to a different core on a multi-core CPU and thereby improve scalability, performance, and reliability. (Nicholas Piasecki has an excellent overview of web gardens, their benefits, and common pitfalls over at his blog: On Web Gardens, ASP.NET, and IIS 6.0.) One potential issue with web gardens is that they do not support InProc Session State, as with InProc Session State the session data is stored in the same memory space as each worker process.

What was happening with this client is that when a user visited the site the request would be randomly dispatched to one fo the worker processes in the garden and it would create (and store) session state for that user, sending back a valid SessionID value. When the user went to the next page the browser sent back the SessionID as expected, but there was a 19/20 chance (95%) that the user would be sent to a different worker process in the garden. This different worker process would not have access to the session store in the original worker process and would therefore create a new session store and create a new SessionID cookie.

In ASP.NET you can configure session state to use the OutOfProc setting, which stores session data using a state server rather than at the worker process level; alternatively, you could configure session state to store session in SQL Server. Either configuration could be used to have session state work in a web garden. With classic ASP applications, there are no configuration options for session state storage. Consequently, the only fix was to disable the web garden. And anyway, I think that setting was made accidentally, as I don't know why this client would want to suddently increase the number of worker processes from 1 to 20, as this web application is hosted on a dedicated server and gets at most a few dozen unique users per day.

Long story short, the vast majority of session state problems center around the cookie not being properly saved by the client or by the server not getting the cookie due to some hardware or software stripping it out of the incoming request. But if you've exhausted those avenues, be sure to check the web garden setting for the application pool. It may save you a couple of hours! :-)

Filed under:
Keeping ELMAH's Error Log Size In Check
10 July 09 08:52 AM | Scott Mitchell | 1 comment(s)

Error Logging Modules And Handlers (ELMAH) is a free, open source error logging library for ASP.NET applications that provides automated error logging and notification and, unlike health monitoring, offers a built-in log viewer web page along with a host of other nifty features. If you're not using ELMAH or have never heard of it I highly recommend that you check it out. It's the first thing I add to any new ASP.NET project I start.

As with any sort of logging service is is important that ELMAH's log be periodically pruned. If you let ELMAH's log grow unchecked it can reduce performance when querying the log and suck up disk space, which is especially important in hosted environments where there are typically hard disk quote limits for each user on the database server. The good news is that there are a number of techniques you can employ to help ensure that your ELMAH error log stays a reasonable size.

  • Use error filtering. ELMAH offers a rich set of error filtering rules that you can use to instruct ELMAH not to log certain types of errors, which can help keep ELMAH's log size down. I use error filtering to filter out 404 errors. While logging 404 errors can alert you to broken URLs on your site or others, if you've ever managed an Internet-facing website you know that it's not uncommon to receive a deluge of requests from bots searching for security holes.
  • Setup a weekly job in SQL Server. If your website is hosted in a dedicated environment you can (likely) setup a job on SQL Server. In past projects I've used a job that ran once a week on Sunday at 2:00 AM that would delete all ELMAH log entries older than three months.
  • Update the ELMAH_LogError stored procedure to delete old log entries. This approach works well for applications in a shared environment as you do not need permissions to create SQL jobs, but rather just permission to create/alter a stored procedure. The ELMAH_LogError stored procedure is the stored procedure used by the SQL Server provider for logging errors; it inserts a record into the ELMAH_Error table. Update this table by adding a DELETE statement that deletes all log records older than a certain threshold. For example, the following DELETE statement removes all log entries more than 90 days old.

    WHERE TimeUtc < DATEADD(d, -90, getdate())

    Add the above statement after the INSERT statement in the stored procedure. Doing so will cause the error log to be pruned of entries older than 90 days anytime a new error is logged. (I've never used an ELMAH log provider other than the SQL Server provider, so I'm not certain what stored procedures or queries or other things would need to be changed to implement such log trimming using an alternate provider.)

Keep in mind that trimming the error log brings with it a tradeoff: you are removing error log entries that might be important for analysis later in time. If this is the case, if you think you might need to review that error log from more than 90 days in the past, then before deleting records from the error log you should archive them somewhere.

Happy Programming!

Filed under:
More Posts


My Books

  • Teach Yourself ASP.NET 4 in 24 Hours
  • Teach Yourself ASP.NET 3.5 in 24 Hours
  • Teach Yourself ASP.NET 2.0 in 24 Hours
  • ASP.NET Data Web Controls Kick Start
  • ASP.NET: Tips, Tutorials, and Code
  • Designing Active Server Pages
  • Teach Yourself Active Server Pages 3.0 in 21 Days

I am a Microsoft MVP for ASP.NET.

I am an ASPInsider.