Channels ▼
RSS

.NET

AutoLogout for Application Security

Source Code Accompanies This Article. Download It Now.


Oct03: AutoLogout for Application Security

Minimizing the chance of security breaches

Jonathan has worked in numerous vertical application development companies, from accounting to law enforcement. He can be contacted at jonathanlurie@ hotmail.com.


A while back, I worked for a software company that developed law- enforcement software used by almost every state in the country. To build the software, the government allowed the company to have a live workstation on the National Crime Investigation Center (NCIC) backbone. This was significant because it was possible for us to get official police-type information on anyone.

The live terminal was typically locked with a key so that unauthorized personnel couldn't access it. A printer in a locked room kept a full activity log. One day, a manager noticed the terminal was not locked, and upon checking the log discovered that classified data had been queried. You have to understand that we were only supposed to query fictitious data that had been entered into the system for us, for example "Mickey Mouse" or "Donald Duck." As it turns out, an employee actually requested information about her ex-boyfriend. She was fired on the spot and the incident was reported. Luckily, the State Department didn't revoke our clearance, but they came close. The reason for mentioning this is that, had we built a feature into the application that would close the application after a certain period of inactivity, this entire fiasco could have been prevented.

While almost everyone knows what computer security means, few understand application security. For developers who work on highly sensitive projects, application security is critical. Many software contracts have software security requirements that call for the ability of an application to log itself out after a certain period of inactivity. This minimizes the likelihood of a security breach as the result of an application being left running on a computer. Users who go to lunch without logging out or locking the workstation pose a potential security breach. An application, thus, must be able to detect inactivity and close itself down, a capability referred to as "AutoLogout."

One approach is to use low-level operating system calls to accomplish autologout. This might work well on applications that can be tied to a specific platforms, such as the Windows API. However, it is not platform independent. Suppose, for instance, the design must not preclude the application from running on Microsoft's FreeBSD port of the Common Language Runtime (codenamed "Rotor"). This restriction alone prevents you from using Windows APIs to accomplish the AutoLogout.

Consequently, the approach I present in this article uses threading, recursion, reflection, private constructors, static constructors, Windows Forms, Events, Delegates, synchronization, and more to accomplish the task. Although this design is implemented using C#, it could just as well be used with VB.NET or Java.

So how do you determine if an application has been idle? The Application class exposes an Idle event, which is raised whenever there are no messages waiting in the queue, so you could use the firing of that event to start a timer running; but then, there would be no way to know when a nonIdle event fired. (This would be needed to properly restart the timer.)

Since there is no means to track inactivity through an exposed .NET Framework class, you have to do it yourself. To do this, you must define what it means for an application to be inactive—for our purposes, inactivity/idleness is simply defined as the lack of mouse movement and key presses.

To track inactivity, you create a low-priority background thread that checks a variable that contains the date/time of the last activity. The thread performs a date difference between the date/time of the last activity and the current date/time. If the time difference exceeds the maximum allowed idle time, then the application shuts down. Each time activity occurs, the date/time of the last activity will be reset—this is the most difficult part.

A further requirement involves designing the AutoLogout in such a manner that it is unobtrusive to existing applications. This means that any application wishing to take advantage of AutoLogout should need minimal modification. To accomplish this, you encapsulate all the code in a class called AutoLogout.

Listing One presents the AutoLogout class. The private constructor on the AutoLogout class prevents the class from being instantiated. This enforces the Singleton pattern. A private constructor should only be placed on classes that have no instance variables; in other words, all members should be static.

A static constructor is sometimes called a "class constructor" (or, in Java, a "static block"). Static constructors are executed only once—the first time the class is referenced from within the code. Static constructors can be used to initialize static variables, or in this case, to kick off the background thread.

The application needing AutoLogout is likely to contain multiple forms. Each form must be watched for activity; however, only one background thread is needed for the entire application. Any activity on any form resets the date/time of the last activity. The single background determines if more than the allowable time of inactivity has elapsed since the last activity.

A delegate is a typesafe function pointer. In other words, a delegate serves as the intermediary between the calling procedure and the procedure being called. To start a thread in C#, you need to declare and instantiate the ThreadStart delegate. The thread constructor receives an instance of ThreadStart as a parameter. Delegates are also used to implement events in .NET.

The design registers two methods as event handlers for any mouse move and keypress events that are fired from the controls on a form being watched. The registering of event handlers is performed in the WatchControl method. It is necessary to determine whether a control contains other controls; this is done by checking the Count property of the Controls collection of the current control. This is necessary because a parent control does not receive the events of its children; subsequently, recursion is used to register the event handlers with all constituent controls.

To use the AutoLogout feature, a form simply makes a call to the AutoLogout.WatchControl() method. The method has a parameter, which is the control to watch. The Form class is derived from the Control class; thus, the code AutoLogout.WatchControl(this); executed from a Form's load method supports AutoLogout for the form.

Obviously, every form in the application would have to make such a call. One line of code fulfills our requirement that the design be unobtrusive. By default, the application performs an AutoLogout after one minute of inactivity.

Conclusion

Unfortunately, there is no solution for AutoLogout that does not adversely affect performance. The degree of performance degradation depends on how much sleep time is allowed between checks to AutoLogout. A limitation of the design is that it cannot capture activity on a MessageBox because a MessageBox.Show() does not expose the form created. Another drawback of the current design is that the application is not shut down in an elegant manner. Improvements can be made so that the AutoLogout displays a message box prompting users to cancel the AutoLogout. For example, users may not have left the workstation—they may simply be working on another program, an AutoLogout in this scenario may be undesirable.

DDJ

Listing One

using System;
using System.Windows.Forms;
using System.Threading;

namespace NSAutoLogout
{
    public class AutoLogout
    {
        // This is the maximum number of minutes the application can remain 
        // without activity before the AutoLogout routine will close the 
        // application. The field is public so that it can be changed.
        public static int maxNumberMinutesIdle = 1;

        // Keeps the timestamp for the last time activity was detected.
        private static System.DateTime dtLastActivity = DateTime.Now;
            
        // This is the method that will serve as the background thread.
        private static void CheckForExceededIdleTime()
        {
            // Sets up an infinite loop
            while (true)
            {
                // If the last time activity occured + the Maximum Allowable 
                // Idle time is less than the current time, then the system 
                // should be shut down
                if (dtLastActivity.AddMinutes 
                              (maxNumberMinutesIdle) < DateTime.Now)
                {
                    Console.WriteLine ("Inactivity Exceeded");

                    // Exits the program, not elegant you should modify this
                    // so it disposes all open windows, etc.
                    System.Windows.Forms.Application.Exit ();
                }
                else
                {
                    Console.WriteLine ("Not AutoLogged Out");
                }
                // Probably don't need this running every second
                // Perhaps every minute would be better in a production system
                Thread.Sleep (1000);
            }
        }
        // Static Constructor. Used to launch the background thread
        static AutoLogout()
        {
            ThreadStart ts = new ThreadStart (CheckForExceededIdleTime);
            Thread t = new Thread (ts);

            // Ensures background thread is killed when 
            // last foreground terminates
            t.IsBackground = true;

            // Don't want this thread taking up too much CPU            
            t.Priority = ThreadPriority.BelowNormal;
            t.Start ();
        }
        // Private Constructor Prevents Instantiation
        private AutoLogout()
        {
        }
        // This is the method called to watch a form
       public static void WatchControl(Control c)
        {
            //  If the control is a textbox then we want to watch 
            // for KeyStrokes since these signify activity.
            if (c is TextBox)
            {
                TextBox t = (TextBox) c;
                // Register an event listener for the keypress event
                t.KeyPress += new System.Windows.Forms.
                 KeyPressEventHandler(MethodWhichResetsTheDateofLastActivity);
                
                // Register an event listener for the MouseMove event
                c.MouseMove  += new System.Windows.Forms.MouseEventHandler 
                                  (MethodWhichResetsTheDateofLastActivity);
            }
            // If the control is not a TextBox then the we want to watch 
            // for MouseMovement since this signifies activity
            else 
            {
                c.MouseMove  += new System.Windows.Forms.MouseEventHandler
                                  (MethodWhichResetsTheDateofLastActivity );
            }
            // Checks to see if the control is a container for other controls
            // if so then we need to watch of all the constituent controls/
            if (c.Controls.Count > 0) 
            {
                foreach(Control cx in c.Controls)
                {
                    // Recursive call
                    WatchControl(cx);
                }
            }
        }  // End of Watch Control Method
        // This method resets the datetime stamp which indicates when the last 
        // activity occured. Overloaded to support KeyPressEventArgs parameter
        private static void MethodWhichResetsTheDateofLastActivity(object
                          sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            Console.WriteLine ("Keyboard Activity Detected");
            dtLastActivity = DateTime.Now;
        }
        // This method resets the datetime stamp which indicates when the last
        // activity occured. Overloaded to support MouseEventArgs parameter
        private static void MethodWhichResetsTheDateofLastActivity(object 
                             sender, System.Windows.Forms.MouseEventArgs  e)
        {
            Console.WriteLine ("Mouse Activity Detected");
            dtLastActivity = DateTime.Now;
        }
    }
}

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video