I’m going to open with just one line, just one line from Megan D, before we dig into the story:
public static boolean comparePasswords(char[] password1, char[] password2)
A long time ago, someone wrote a Java 1.4 application. It’s all about getting data out of data files, like CSVs and Excel and XML, and getting it into a database, where it can then be turned into plots and reports. Currently, it has two customers, but boy, there’s a lot of technology invested in it, so the pointy-hairs decided that it needed to be updated so they could sell it to new customers.
The developers played a game of “Not It!” and Megan lost. It wasn’t hard to see why no one wanted to touch this code. The UI section was implemented in code generated by an Eclipse plugin that no longer exists. There was UI code which wasn’t implemented that way, but there were no code paths that actually showed it. The project didn’t have one “do everything” class of utilities- it had many of them.
The real magic was in Database.java
. All the data got converted into strings before going into the database, and data got pulled back out as lists of strings- one string per row, prepended with the number of columns in that row. The string would get split up and converted back into the actual real datatypes.
Getting back to our sample line above, Megan adds:
No restrictions on any data in the database, or even input cleaning - little Bobby Tables would have a field day. There are so many issues that the fact that passwords are plaintext barely even registers as a problem.
A common convention used in the database layer is “loop and compare”. Want to check if a username exists in the database? SELECT username FROM users WHERE username = 'someuser'
, loop across the results, and if the username in the result set matches 'someuser'
, set a flag to true (set it to false otherwise). Return the flag. And if you're wondering why they need to look at each row instead of just seeing a non-zero number of matches, so am I.
Usernames are not unique, but the username/group combination should be.
Similarly, if you’re logging in, it uses a “loop and compare”. Find all the rows for users with that username. Then, find all the groups for that username. Loop across all the groups and check if any of them match the user trying to log in. Then loop across all the stored- plaintext stored passwords and see if they match.
But that raises the question: how do you tell if two strings match? Just use an equality comparison? Or a .equals
? Of course not.
We use “loop and compare” on sequences of rows, so we should also use “loop and compare” on sequences of characters. What could be wrong with that?
/**
* Compares two given char arrays for equality.
*
* @param password1
* The first password to compare.
* @param password2
* The second password to compare.
* @return True if the passwords are equal false otherwise.
*/
public static boolean comparePasswords(char[] password1, char[] password2)
{
// assume false until prove otherwise
boolean aSameFlag = false;
if (password1 != null && password2 != null)
{
if (password1.length == password2.length)
{
for (int aIndex = 0; aIndex < password1.length; aIndex++)
{
aSameFlag = password1[aIndex] == password2[aIndex];
}
}
}
return aSameFlag;
}
If the passwords are both non-null, if they’re both the same length, compare them one character at a time. For each character, set the aSameFlag
to true
if they match, false
if they don’t.
Return the aSameFlag
.
The end result of this is that only the last letter matters, so from the perspective of this code, there’s no difference between the word “ship” and a more accurate way to describe this code.