Ludicrous But Intriguing: A JavaScript Login System That Hides Content And Prevents Login Flooding

Asked recently on Yahoo! Answers:

Password control in javascript?
i have to design a program to allow access to a website to people who have the correct pin/ The pin is a 4-digit number (1000-4000).The program should have 2 options. 1. Enter the Pinand if is correct(between 1000 and 4000) the website will be accessed. 2. set the maximum no of attempts to enter correct pin. (by default i chose 3).
This is what i have so far.
{snip: code}

Finally, someone with a homework problem who has actually put in legitimate effort of his own. To celebrate this event — the first time anyone in P & D asking for homework help has ever actually tried to do his own homework, I suspect — I’ll take the questioner’s original code, apply some tweaks, and we’ll have his program ready in a jiffy. And I’m willing to do that because the question is 80 percent of the way there; the remaining 20 percent is just a lack of experience, and since gaining experience is the point behind the assignment, I’m happy to lend mine.

An aside on JavaScript login systems: A JavaScript login system is as stupid as a screen door on a submarine. This is clearly a learning exercise. Don’t use this code for actual security, because it doesn’t provide any.

Executive summary: We’re going to use a regular expression to test the PIN number. The user gets three cracks at entering a PIN that matches out pattern. If the user enters a good PIN, we show the page content; if the user doesn’t, he gets a message to that effect.

The Questioner’s Code

Let’s look at what the questioner has for code:

<script language="JavaScript">
	<!--hide

		var password;
		var numberOfAttempts = 3;

		password=prompt('Please enter the password to access this page page!',' ');

		if(password > 999 && password < 4001)
		{
			{
			alert('Password Correct! Click OK to enter!');
			window.location="http://www.celticfc.net...";
			}

		}
		do
		{
			password=prompt('Please enter the password to access this page page!',' ');
			numberOfAttempts++;
		}
		while(password <= 999 && password >= 4001)
		{
			alert('Password Incorrect! Click OK to try again!');
		}
</script>

(I am forgiving the questioner the truncated string at Line 13, but it would be nice if questioners in P & D actually previewed their questions during the preview stage of posting a question, noticed any string truncation, and posted code to the Web where appropriate.)

(I guess I also have to forgive that the question was deleted while I was writing this answer. There’s a better-than-average chance that’s because many users, who mistakenly think homework questions aren’t allowed on Answers, reported it. I’ve put in enough work so that this answer stays, even if the question is gone.)

There are a some syntax errors here but the algorithm is 100 percent sound; the questioner is definitely on the right track. It’s easy to tell what he is thinking, and how he’s implementing his thoughts:

  1. declare the password and attempt counter;
  2. prompt for the password;
  3. see if it is in range;
  4. increment the attempts counter;
  5. warn the user if the password is wrong;
  6. send the user to another page on the final failure

Unfortunately, the code isn’t properly structured to accomplish this clear and correct algorithm. (I won’t mention the few syntax errors, as they are ancillary.)

For example, while the attempt counter is being incremented, there’s no control structure testing for the third and final try. There’s also an assumption that the user will always input an integer at the prompt, but that’s not necessarily so. And there’s no handling of what happens if someone presses the Cancel button at the prompt (canceling a prompt box causes it to return null).

Again, this is an excellent effort, but we need to restructure it, primarily to to process the input properly.

Regular Expressions To The Rescue

Our solution is going to use CSS, along with JavaScript, to hide the page until a proper PIN is formatted. The parent div for our layout receives a display:none CSS property initially; once the correct PIN is entered, that property is changed to block.

We’re going to use a regular expression to test whether the PIN input is in range. Regular expressions are a way to test strings for a given pattern; that is, whether certain characters or character sequences occur in the string being tested.

In our case, our entry must be a four-digit number between 1,000 and 4,000. If we could safely ensure a user entered an integer, that would be easy to test, as the questioner did originally. But we can’t be sure the questioner will enter only an integer, and if he doesn’t, our script could well throw an error.

One option, therefore, is to test if what is entered at the prompt is a number, using isNaN(). That function, in theory, what was entered at the prompt is an integer; but it would also OK any mathematical expression entered into the prompt, as well as a decimal.

isNaN('foobar'); //true
isNaN(3); //false
isNaN(2 + 2); //false
isNaN('2' + '2'); //false; strings converted to integer for mathematical operation

Clearly, that’s not good enough.

Another option is to rephrase, in programming terms, our requirement.

We don’t really need to use the PIN as an integer; it’s just entered that way. We don’t need to do math on the PIN; from a practical standpoint, it’s just a string. The same way that this PIN should be between 1,000 and 4,000, it could be said to need to be between baaa and eaaa, if we were using letters instead of numbers.

Since that’s the case, we can further look at the requirement and say, so long as the first number of our PIN is a 1, 2 or 3, we don’t care what the remaining three numbers are, just so long as they are a number. But if the first number of the PIN is a 4, the three remaining characters must be zeros.

Thankfully, that kind of pattern detection is exactly what a regular expression can do for us. Here’s the PIN requirement as stated above, as a regex:

^([1-3]{1}[0-9]{3})|(4000)$

What the expression above literally says is, “From the start of the input (^) to the end of the input ($), make sure that either a) the first digit is a 1, 2 or 3 ([1-3]{1}), and there are three following digits ([0-9]{3}); or b) that the value is 4000.”

Identity And Weak Types In JavaScript

We’ll also test if the cancel button was clicked by using the identity operator (===) to test for null.

JavaScript is a weakly typed language. What that means is that the programming language generally doesn’t care if a variable is an integer, string, array, object, etc.. You don’t have to declare variables as a given data type; they take whatever type is needed and JavaScript does its best to convert them to a different type where needed.

This is generally a good thing, except on those rare occasions when we need to be not only certain of the value of a number, but the type of variable it is.

There are important differences between null, empty and zero-length variables, so being able to test for null — the “value” returned when a prompt box is canceled — requires us to ensure that we are actually getting a null back from the prompt box.

The way we do that is with the identity operator. The identity operator not only ensures two variables have the same value, it also ensures they are the same type:

"1" == 1; //true; string is automatically converted to integer by Javascript
"1" === 1; //false; strings aren't integers

The Changed Code

OK, with the discussion out of the way, we’re ready to create our JavaScript function.

Just two more steps to finish this project. First, we need to set the body tag's onload event to call the function:

<body class="twoColFixRtHdr" onload="checkPass()">

And the CSS for our page needs to set the initial display property for the main container div to none:

.twoColFixRtHdr #container {
	width: 780px;  /* using 20px less than a full 800px width allows for browser chrome and avoids a horizontal scroll bar */
	background: #FFFFFF;
	margin: 0 auto; /* the auto margins (in conjunction with a width) center the page */
	border: 1px solid #000000;
	text-align: left; /* this overrides the text-align: center on the body element. */
	display: none;
}

You can see a working demo of this solution here: http://www.dougv.com/demo/js_password/

No downloadable code; just save the demo page to your computer. I distribute all code under the GNU GPL.

All links in this post on delicious: http://delicious.com/dhvrm/ludicrous-but-intriguing-a-javascript-login-system-that-hides-content-and-prevents-login-flooding

2 Comments

  1. Scriptar says:

    It is not completely ludicrous to rely on JavaScript to hide content. If you use a JavaScript implementation of the MD5 (or another type of) one-way hashing algorithm, you can use it to keep your information secret when you don’t have a server-side solution:
    scriptar.com/JavaScript/login.html

  2. OK, I’ll compromise: A JavaScript login system isn’t ludicrous; it’s more pointlessly insecure. ;)