Click here to Skip to main content
15,885,757 members
Articles / Web Development / HTML

JavaScript Password Protection and Session Management

Rate me:
Please Sign up or sign in to vote.
4.07/5 (11 votes)
17 Jul 200213 min read 154.2K   2.7K   46   8
This client-side code implements secure password protection and user/session management using cookies based on rc4 encryption.

Introduction

This article would be useful for those of you who want to secure some data on their web page but don't have access to advanced and expensive serves supporting CGI.

The code provided with this article is a client-side JavaScript that allows you to password protect the content of your web page, define users with their own personal password, and define groups of users.

The code uses RC4 encryption to protect secured content, and the MD5 algorithm to store passwords and usernames, which makes your content fully secure even if the user has access to the JavaScript source (which is always the case for client-side JavaScript).

This encryption is what makes this code entirely different from most other JavaScript password protection approaches available on the net, most of which store usernames and passwords as plaintext in the JavaScript source, thus rendering those methods completely unsecured.

As a bonus, the code supports sessions with cookies which allows users to come back to the web page without having to log in again. (Cookies store the username in plaintext, and the users password encrypted with the username, which is in no way secure, but slightly better that plaintext)

A Bit of Vocabulary and Basic Stuff

A resource is any protected (encrypted) HTML / DHTML / Script / XML / etc. code that the browser can interpret. A user is, well... someone with a password who has access to certain resources provided he knows the correct username and associated password A group is a collection of users. Each resource belongs to a group, and only the members of this group have access to the resource. Each user can be a member of any number of groups.

As you can see above, this system allows for different levels of users having access to different resources.

How the Basics Work

Groups are the core of the protection system. Each group has a different master password. This password is known only by the administrator, and no users will ever need to know it. Resources belonging to a groups are encrypted with that group's master password using strong and fast rc4 encryption.

Now we want each user to have their own password independently of which groups he's a member of, so for each group, a specific user is member of the user is assigned a version of the master password for that group encrypted with the user's personal password. The user won't need to remember these encrypted passwords as they're stored in the user "database" in a JavaScript file.

When the user needs to access a resource, his password is used to decrypt the master password of the groups the resource belongs to, and once the master password is retrieved, it's used to decrypt the resource itself.

How the Demo Works

The features of the JavaScript source provided can be used in different ways, but the demo shows the most common usage, which I'll explain here.

The files included in the download are the following:

dvxcrypt.js the core code (You shouldn't need to modify this file)
demo1.html - demo3.html demonstration pages each with a different encrypted content
demo.js used by all three demo pages and is where the users/password and groups are defined, as well as a few routines used by all demo pages.

Let's start with demo.js.

The first lines contain the group declaration:

JavaScript
// both 'user' and 'bob' have access to Group0
var Group0 = new Group('SaCN+kWgjfp93w0s/d8NLI');
// only 'bob' has access to Group1
var Group1 = new Group('VBS2A1wUtgNIdiPC/HYjwu');

Group0 and Group1 are the two group names. They are both variables pointing to an object called Group. The argument passed to the constructor is the master password of the group hashed with the MD5 algorithm. This is used for error-checking when a user is logged in.

In the next lines, we declare the two users of the demo, called user and bob:

JavaScript
// username: user	password: pass	groups: Group0
DeclareUser('uscsR77HLEecL5FKWC+RSk','aHMkdGBzJHR/GJXOZxiVzk',
            [Group0,'QbzkVaxCwC']);
// username: bob	password: code	groups: Group0 + Group1
DeclareUser('fvFGdn7xRnZ/KIe94yiHvc','BlGczw5RnM8+RTF1/kUxdX',
            [Group0,'MzHCJgXUaA'],[Group1,'MzHCJgXUbA']);

(Obviously in real-life usage, you wouldn't want to put the username and password in a comment as I did above!) As you might have noticed, user belongs only to Group0, whereas bob belongs to both Group0 and Group1, but we'll return to that later.

Users are declared using the DeclareUser function. The first argument this function takes is the hashed version of the username, and the second one the hashed user password. These two are used to validate the user when he logs in.

The next argument(s) are the groups the user belongs to. Each group is enclosed in square brackets, and consists of the group name followed a comma, and, enclosed in quotes, the master password of the group encrypted with the users password. The DeclareUser function takes any number of group declarations separated by commas.

The next step is the instantiation of a session object:

JavaScript
var S = new Session('my_session');
S.Callback = s_cb; // function s_cb defined below
// if the user doesn't come back after 48 hours, he'll need to login again:
S.nCookieExpirationDelay = 48;
S.bLocationLogin = true;

The only parameter of the constructor for the Session object is the name of the session, which is internally used in cookie names to distinguish different sessions, if you for some reason use more than one in your webpage.

The line below (S.Callback = s_cb), sets a callback function for the session. This callback is called when a users logs in or out, but we'll return to that one in just a second.

The S.nCookieExpirationDelay = 48; statement sets the time the session cookies will live. In this case, they'll last for 48 hours. After this delay is passed, the user will need to login again. You may set this to something like an hour for greater security, or to a year if you don't want to bug your users. Note that the session cookies get refreshed (and the expiration delay reset) every time the user gets logged in.

The last line sets the bLocationLogin parameter to true which allows you to specify user/password information in the address bar of the browser or in a link. This exposes the username and password to everyone, but might be useful for some applications. It's used like this: http://www.myserver.com/demo1.html?user=bob&pass=code, where bob and code is the username and password of the user that will automatically get logged in when the page loads (provided bLocationLogin is set to true). Note that a location login will override a users login cookies (which will be deleted on successful login).

Next part of demo.js is what I call a resource callback. It's a function called before and after a resource gets decrypted and can be used for whatever you like.

The resource callback has this declaration:

JavaScript
function r_cb(context,lparam)

When called, the context parameter can take the value of either this.ccBeforeDecrypt, this.ccAfterDecrypt or this.ccInvallidMaster. In the first case, lparam is the plaintext master password, in the second case, lparam is the plaintext content of the resource being decrypted, and in the last case, the callback is called when the user hasn't got the privilege to access the resource, lparam should be ignored.

The resource callback used in the demo looks like this:

JavaScript
function r_cb(context,lparam)
{
  if(context == this.ccAfterDecrypt)
  {
    if(!lparam)
      document.all['cnt'].innerHTML = "Error!";
    else
      document.all['cnt'].innerHTML = lparam;
  } else if(context == this.ccInvallidMaster)
  {
    document.all['cnt'].innerHTML = "You do not have the right to view this page!";
  }
  return true;
}

When the function is called with context == this.ccAfterDecrypt, it checks lparam for an invalid value, and displays an error message. Otherwise, the content of lparam (the decrypted resource) is displayed to the user. If the function is called with this.ccInvallidMaster, a message telling the user he cannot access the resource is displayed.

The session callback mentioned above, looks like this in the demo:

JavaScript
function s_cb(context,lparam)
{
  switch(context)
  {
    case this.ccLogin:
      document.all['login'].style.display = 'none';
      document.all['logout'].style.display = '';
      document.all['username'].value = '';
      document.all['password'].value = '';
      document.all['cnt'].innerHTML = "Loading...";
      document.all['curuser'].innerHTML = lparam[0];
// Trick: use a setTimeout to decrypt the page content in order to let the browser
// update the page with the changes we made above before beginning decryption
      setTimeout("Content.DecryptResourceS(S)",0);
      break;
    case this.ccLogout:
      document.all['login'].style.display = '';
      document.all['logout'].style.display = 'none';
      default_text();
  }
  return true;
}

When context is this.ccLogin, the user has just logged on, and lparam is an array containing the plaintext username and password of the user that just logged in. In the demo, this is used to display the username of the user that logged in, and after that, the setTimeout("Content.DecryptResourceS(S)",0); statement makes sure the content of the page is decrypted and displayed immediately after the user has logged on.

When the function is called with context == this.ccLogout, the user has logged off (lparam is not used in this case), and demo clears the page to default.

Each html page of the demo contains some encrypted content. In demo2.html, it looks like this:

JavaScript
<script>

// encrypted content of the page
var Content = new Resource(Group0,'/f7Ff8JQaefs+HomAFPfA9yJLMB+BWPzyG9g5Byd/Etz0L1q4Y0
ZaDbyTkZEglrkdb0KydY2ePZ/tnaUiDHcllQsdrgz/aAxsj5o1VDyDsYp6WjAQCUXHerc2NtPIyX9DALJH6D');
// (above line wrapped for display)

Content.Callback = r_cb;

</script>

Content is the variable that holds the resource. The constructor takes two parameters:

  • The first one is the group the resource belongs to (in this case it's Group0).
  • The second parameter is the encrypted resource itself (plaintext content, encrypted with the master password of the group).

That's it for the description of the demo. I'm tired (and lazy), so you'll have to figure out the rest of how it works by yourself (should be more or less straight forward if you look at the source).

Wrapper Objects

This section describes the different objects (aka classes) used as wrappers around the rc4 and md5 algorithm. If you don't care / don't have the time, you can always modify the demo to fit your needs and come back here for reference if you need it.

The usage of these objects is (hopefully) described above. This is a reference of the member function and variables.

User

The User object describes a single user with a username and a password inside a single group. This means that a User object is created for each group a user belongs to. This is automatically done in the DeclareUser function.

Member Variables

  • name
    This variable contains the hashed username, and is filled by the constructor.
  • emp
    The master password of the group the User object belongs to encrypted with the users password.

Member Functions

  • User([string] hashed_user_name, [string] encrypted_master_password)
    The constructor takes the hashed username as first parameter, and the master password of the group encrypted with the users password as second parameter.
  • [string] DecryptMaster([string] password)
    This function takes the plaintext user password as argument and returns the decrypted master password stored in emp.

Password

The Password object acts pretty much like the User object except that it cannot be used with the Session object and its member functions.

You'll want to use the Password object when you want to give access to certain resources based on just a password instead of a username/password pair. The Password object is fully compatible with Group and Resource objects. For the functions taking a username and password as argument, simply specify a blank string as username.

Member Variables

  • emp
    The master password of the group the Password object belongs to encrypted with the password.

Member Functions

  • Password([string] encrypted_master_password)
    The only argument of the constructor is the master password of the group encrypted with the password.
  • [string] DecryptMaster([string] password)
    This function takes the plaintext password as argument and returns the decrypted master password stored in emp.

Group

The Group object is a collection of Users and/or Passwords. These can be accessed through the standard [] array operator using a zero based index.

Member Variables

  • length
    Integer describing the total number of Users and Password in the collection.
  • hmp
    The master password of the group hashed with the MD5 algorithm (used for error checking).

Member Functions

  • Group([string] hashed_master_password)
    The constructor takes the hashed master password of the group as argument.
  • [bool] CheckMaster([string] master)
    This function takes a plaintext string as argument and returns true or false depending on whether it's the correct master password for the group.
  • [string] GetMaster([string] user, [string] password)
    Takes a plaintext username and password as argument and returns the plaintext master password of the group. If the user is not a member of the group, or if an invalid password is specified, null is returned.
  • [bool] Verify([string] user, [string] password)
    Takes the same arguments as GetMaster but returns true or false depending on whether the username/password check failed or succeeded.
  • [int] Add([object] obj)
    This function adds a User or Password object to the group. The return value is the index of the newly added object in the collection.

Resource

The Resource object holds some encrypted content. It is always associated with a Group object, and the content is encrypted with that groups master password.

Member Variables

  • group
    A reference to the Group associated with the resource.
  • content
    The encrypted content of the resource.
  • Callback
    OK, this is actually a function, but it is treated as a variable. You can set this to your own resource callback (described somewhere above)

Member Functions

  • Group([string] hashed_master_password)
    The constructor takes the hashed master password of the group as argument.
  • [string] DecryptResourceM([string] master)
    The first argument is the plaintext master password of the group associated with the resource. The return value is the decrypted resource or false on failure.
  • [string] DecryptResourceUP([string] user, [string] password)
    Takes a plaintext username and password as arguments and returns the decrypted resource, or false on error (the user must be a member of the group associated with the resource).
  • [string] DecryptResourceS([object] session)
    Takes a Session object as parameter and tried to decrypt the resource using the currently logged in user's name and password. Returns the decrypted resource or false on failure.
  • [string] DecryptFromLocation()
    Tries to decrypt the resource using the username/password specified in the querystring. Return value is the same as above.

Session

The Session object handles user login and logout, cookies, and holds information about the user currently logged in (if any).

Member Variables

  • bIsLoggedIn
    A boolean telling if a user is currently logged in.
  • bUseCookies
    Boolean telling is cookies should be used with this session.
  • sUserNameCookie
    If cookies are used, this variable holds the name of the cookie used for storing the username. Otherwise it is set to null.
  • sPasswordCookie
    Same as above, but for the password cookie.
  • nCookieExpirationDelay
    Time in hours after which the session cookies should expire.
  • bLocationLogin
    Whether or not the querystring should be checked for login information.
  • sUserName
    Username of the user currently logged in, or null if not logged in.
  • sPassword
    Plaintext password of the user currently logged in, or null if not logged in.
  • Callback
    Set this to your own session callback function (again, described somewhere above).

Member Functions

  • Session([string] cookie_base)
    The only argument of the constructor is a name for the session, used for generating cookie names. This parameter can be set to null if you want session cookies disabled.
  • [bool] Login([string] user, [string] password)
    Attempts to log the specified user in. Returns true on success or false on failure.
  • [void] Logout()
    Logs out the currently logged in user, and deletes the session cookies
  • [bool] UserLogin([string] user, [string] password, [bool] use_cookies)
    Same as Login but takes a third argument describing whether or not to use session cookies for this specific login. Also, a message box is displayed on failure.
  • [bool] DeleteLoginInfo()
    Deletes session cookies. Always returns true.
  • [bool] SaveLoginInfo()
    Creates session cookies based on the user currently login in.
  • [bool] RestoreLoginInfo()
    Checks if the user has session cookies stored, and attempts a login using the information retrieved from the cookies.
  • [bool] LoginFromLocation()
    Tries to log in using the username/password in the querystring (i.e. http://www.myserver.com/demo1.html?user=bob&pass=code)
  • [void] Init()
    Attempts to log in using the querystring and/or session cookies, depending on the options set.
    You should always call this function in the onload handler of the document body! (i.e. <body onload="S.Init()">).

Admin.html & Test.html

The download contains two more files, admin.html and test.html.

admin.html contains pseudo-wizards for easily generating users, groups and resources, without you having to do the dirty encryption / hashing work. You shouldn't have to much trouble figuring out how it works.

test.html is for those of you who want to get your hands dirty.
It allows you to generate MD5 digests, and to do RC4 encryption / decryption.

Credits

The JavaScript implementation of the MD5 algorithm is Copyright (C) Paul Johnston 1999 - 2002.

The RC4 function wasn't written by me either, but I don't remember where I got it from.

History

  • 18th July, 2002: Initial version

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Engineer Nokia
Denmark Denmark
My programming experience started a long time ago in
QBasic (on a 25MHz 486).
I'm now mainly using Java, C++, C, MFC, Perl and PHP, but have used quite a number of other languages as well for various projects.

Comments and Discussions

 
GeneralCan't get it to work! Pin
jennmoore12-Jun-08 4:29
jennmoore12-Jun-08 4:29 
GeneralReading Session Variable Pin
sureshkumar_a21-Mar-06 1:19
sureshkumar_a21-Mar-06 1:19 
GeneralRe: Reading Session Variable Pin
thund3rstruck31-Dec-08 5:02
thund3rstruck31-Dec-08 5:02 
GeneralA good javascript implementation for ... Pin
__PPS__20-Feb-06 6:50
__PPS__20-Feb-06 6:50 
GeneralFirst article to use the Paul tag Pin
Nish Nishant21-Jul-02 15:40
sitebuilderNish Nishant21-Jul-02 15:40 
GeneralRe: First article to use the Paul tag Pin
Paul Watson12-Sep-07 2:21
sitebuilderPaul Watson12-Sep-07 2:21 
GeneralRe: First article to use the Paul tag Pin
Nish Nishant12-Sep-07 2:28
sitebuilderNish Nishant12-Sep-07 2:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.