• Tei (unregistered)

    Two fatals errors combined result in The Real TRWTF.

  • (cs)

    So the web page looks something like this:

    Enter password, or 'free entry': ___________

    What's wrong with that?

  • Keith Hackney (unregistered)

    The Real WTF is that they have both IsActive AND IsDeleted.

    CAPTURE=And Again The Tumbleweed Blows Across Your Desk

  • James (unregistered) in reply to Keith Hackney

    Indeed what's a huge security hole compared to one BIT of redundancy.

  • (cs) in reply to Keith Hackney
    Keith Hackney:
    The Real WTF is that they have both IsActive AND IsDeleted.

    I dunno. It might be possible to have an account that is not active nor deleted. I have used something similar, but the IsDeleted was really a MarkedForDeletion flag. An account that was marked for deletion could not be re-activated.

    Personally, I prefer a single status field.

  • sweavo (unregistered) in reply to Keith Hackney
    Keith Hackney:
    The Real WTF is that they have both IsActive AND IsDeleted.

    Not if there were four possible states for the user.

    Captcha: I just used the generic one

  • Joon (unregistered)

    That's a very (ahem) novel solution

    Rather create the record initially with the invitation password.

    I wonder if this was introduced in the code, or whether the initial spec said something like "The user should be allowed to log in with their invitation password at any time, in case they have forgotten their password"

    Either way, the mind boggles...

    Also, unions degrade performance. They should rather have added the invitation password check to the top query instead of redoing the whole query. Then of course the WTF would have been harder to spot ;-)

  • (cs) in reply to Keith Hackney
    Keith Hackney:
    The Real WTF is that they have both IsActive AND IsDeleted.
    It may be that: IsActive = not (account inhibited e.g. by failing to enter right password X times) IsDeleted = account is flagged for deletion.

    The first would be updated automatically by the software, and the second manually by an administrator. Keeping the fields separate makes the logic of updates simpler, at the cost of an extra line in the WHERE clause.

    PS. The "preview pane" has a bit of a problem with Unicode. The by-line for this post now says:

    "sometime in the future • by Ľudovít Å túr"

    ... and it keeps getting longer.

    1. Can you guess the identity of my alter-ego from the above?
    2. How long before it blows up?
  • AdT (unregistered)

    Storing plain text passwords in the user database is not such a bright idea either, though of course this is a relatively minor issue (IOW relative to the sheer insanity of inviting everyone to use everyone else's account). Even if you want to support password reminders, it's better to store the passwords in a separate table that can only be queried by the password reminder service.

    And there are more secure solutions than password reminders, anyway. For example:

    1. "Forgot your password? Please enter your e-mail address"
    2. A new temporary password is created and sent to said e-mail address.
    3. If the temporary password is used to log in, the user is prompted to enter a new password immediately.
    4. If the temporary password is not used within a certain time span, it expires, and the permanent password is not changed to protect from certain DoS attacks.

    While this allows an attacker to hijack an account by intercepting e-mail, the same goes for ordinary password reminders, of course. But at least the attacker will not obtain your password, which happens to be the same combination you have on your luggage, and steal all your pants and canned oxygen. Furthermore, because the attacker has to change the password to log in (and changing it to the temporary password will be prevented), the user will notice that something is wrong the next time he tries to log in.

  • crxs (unregistered) in reply to jgayhart
    jgayhart:
    I dunno. It might be possible to have an account that is not active nor deleted.
    ... until someone opens the box?
  • Nicolas V. (unregistered)

    AND the password should be encoded in some way (although I personally do that business-side but what do I know).

  • Bob (unregistered) in reply to Keith Hackney
    Keith Hackney:
    The Real WTF is that they have both IsActive AND IsDeleted.

    CAPTURE=And Again The Tumbleweed Blows Across Your Desk

    IsActive could refer to: . email activation of an account . an unused account that has had data deleted but still exists (like yahoo mail if you don't log in for a while) . an account that is not longer accessible but has data attached to it that other users might see (eg forums) . etc, etc, etc

  • (cs) in reply to The General
    The General:
    PS. The "preview pane" has a bit of a problem with Unicode. The by-line for this post now says:

    "sometime in the future • by Ľudovít Å túr"

    ... and it keeps getting longer.

    1. Can you guess the identity of my alter-ego from the above?
    2. How long before it blows up?

    Interesting. It seems to be re-encoding each time. Unfortunately, the way you copied it already means it's blown up, so the closest I can get to your name from that (by decoding three times) is:

    "Ľudovít ▒\xC2\xC2\xC2 túr"

    I'm guessing that that's meant to be an Š in the middle there, but, yeah.

  • Michael (unregistered) in reply to crxs
    crxs:
    jgayhart:
    I dunno. It might be possible to have an account that is not active nor deleted.
    ... until someone opens the box?

    ... or perhaps when some hacker has tried to brute force your password, and the account gets locked out: isActive=false and isDeleted=false

    If you could delete an account by typing the wrong password a few times, that would be a wtf in itself.

  • (cs) in reply to AdT
    AdT:
    Storing plain text passwords in the user database is not such a bright idea either
    Ok, I'll bite. How can you tell from this that the password is stored in plain text?
  • BitTwiddler (unregistered) in reply to jgayhart
    jgayhart:
    Keith Hackney:
    The Real WTF is that they have both IsActive AND IsDeleted.

    I dunno. It might be possible to have an account that is not active nor deleted. I have used something similar, but the IsDeleted was really a MarkedForDeletion flag. An account that was marked for deletion could not be re-activated.

    Personally, I prefer a single status field.

    That's nothing - I once inherited a system that had a status bit called 'physically deleted'. Ponder that one for a second. (the authors distinguished between 'physically deleted' and 'literally deleted')

  • Anonymous (unregistered)

    Maybe I am missing something here, but...

    If once the user changes their password, the orignal records Is_Active flag is set to 0, then the query will not return that record anymore. Yes, the union was a bad decision, but depending on how the logic works, I don't see how both records would be returned, if the flags get changed on the 1st record once the 2nd record is created.

  • Paula Bean (unregistered)

    OMG...why would you waste all that space of an extra flag?

    If you want to store something to show if the account is currently active AND whether its deleted...you just need to use a single isDeleted String!!!

    if isDeleted = "Yes" then the user is not active. else if isDeleted = "Pink" then they're not.

    To indicate the deleted state, well heck you never heard of DELETE FROM...? we can just remove them from the database. If its there its not deleted...Come on thats obvious...

    What? Oh well yeah i remember that requirement that you need to be able to restore a deleted user if they were deleted by accident....Yes ofcourse my solution fullfills it...after all thats what database backups are for... and we definately do one of those....were very particular...each morning i check that the back up tape has been ejected...ie backup succesfull and then i push it back in to make sure its all ready for the next one.....

    ... Jeeze you lot like to make things complicated.

  • Al Gore (unregistered) in reply to Anonymous
    Anonymous:
    Maybe I am missing something here, but...

    If once the user changes their password, the orignal records Is_Active flag is set to 0, then the query will not return that record anymore. Yes, the union was a bad decision, but depending on how the logic works, I don't see how both records would be returned, if the flags get changed on the 1st record once the 2nd record is created.

    What the hell are you talking about? Yup you're missing something.

  • (cs) in reply to Ciaran
    Ciaran:
    The General:
    PS. The "preview pane" has a bit of a problem with Unicode. The by-line for this post now says:

    "sometime in the future • by Ľudovít Å túr"

    ... and it keeps getting longer.

    1. Can you guess the identity of my alter-ego from the above?
    2. How long before it blows up?

    Interesting. It seems to be re-encoding each time. Unfortunately, the way you copied it already means it's blown up, so the closest I can get to your name from that (by decoding three times) is:

    "Ľudovít ▒\xC2\xC2\xC2 túr"

    I'm guessing that that's meant to be an Š in the middle there, but, yeah.

    Vyhrate! Quite correct. Though that's not my real name either. Like Rance Muhammitz, I have many names...

    (Hope this comes out all right - I'm not going to preview it...)

  • (cs)

    Note how the recordset returns EMAIL and PSWD, even though those were explicitly included in the WHERE clause:

    SELECT  USER_ID, 
            FIRST_NAME + ' ' + LAST_NAME AS FULL_NAME,
            EMAIL,
            PSWD
    FROM    USER
    WHERE   (IS_ACTIVE = 1) 
            AND (IS_DELETED = 0)
            AND (EMAIL = @EMAIL) 
            AND (PSWD = @PSWD)

    I'm guilty of this... I mess around to get the query just right, and then forget to clean out unneeded return values. Not a biggie, but why send the same data both ways?

    Of course, it could be worse... I still find code all the time with "Select *", and have to dig through the code to find out what fields are actually being used.

    Or even worse, they could have a check like:

    If rs("EMAIL") <> sEMail Then
    MsgBox "OH NOES TEH EMAIL IZ BORKD!"

  • Chris Hayes (unregistered)

    Did nobody else notice the unnecessary union on the same table?!

    Ignoring all the other wtfs in this post, couldn't the query have just been:

    SELECT USER_ID, FIRST_NAME + ' ' + LAST_NAME AS FULL_NAME, EMAIL, PSWD FROM USER WHERE (IS_ACTIVE = 1) AND (IS_DELETED = 0) AND (EMAIL = @EMAIL) AND ((PSWD = @PSWD) OR (DBO.GET_INVITATION_PWD() = @PSWD))

    I know I'm not missing anything. ;)

  • (cs) in reply to crxs
    crxs:
    jgayhart:
    I dunno. It might be possible to have an account that is not active nor deleted.
    ... until someone opens the box?

    just like Schrödinger's cat before you don't know if the user is existing or not :)

  • Henry Miller (unregistered)

    Keeping deleted acounts is a good idea, that way spammers cannot re-sign up with the same old email address. Of course some spammers are slightly smarter than this programmer and will plenty of throw away email accounts to work with. (A good programer could corrolate information from the deleted acounts to trap spammers, but this is beyond the idiot who uses one initial password for all accounts)

  • (cs) in reply to Ciaran
    Ciaran:
    The General:
    PS. The "preview pane" has a bit of a problem with Unicode. The by-line for this post now says:

    "sometime in the future • by Ľudovít Å túr"

    ... and it keeps getting longer.

    1. Can you guess the identity of my alter-ego from the above?
    2. How long before it blows up?

    Interesting. It seems to be re-encoding each time. Unfortunately, the way you copied it already means it's blown up, so the closest I can get to your name from that (by decoding three times) is:

    "Ľudovít ▒\xC2\xC2\xC2 túr"

    I'm guessing that that's meant to be an Š in the middle there, but, yeah.

    so TRWTF is this site ???

  • (cs) in reply to Michael
    Michael:
    crxs:
    jgayhart:
    I dunno. It might be possible to have an account that is not active nor deleted.
    ... until someone opens the box?

    ... or perhaps when some hacker has tried to brute force your password, and the account gets locked out: isActive=false and isDeleted=false

    If you could delete an account by typing the wrong password a few times, that would be a wtf in itself.

    if they have already forseen hacker attacks the first thing to do is certainly not the introduction of an IsDeleted flag and then leave the door wide open for everbody to serve themselves. Hopefully this is not a financial institution

  • (cs)

    BTW, why not show a textbox on the screen with password in it and a text that says:

    if this is your password and username click on enter otherwise logon with any username and global password

  • B (unregistered) in reply to Al Gore
    Al Gore:
    Anonymous:
    Maybe I am missing something here, but...

    If once the user changes their password, the orignal records Is_Active flag is set to 0, then the query will not return that record anymore. Yes, the union was a bad decision, but depending on how the logic works, I don't see how both records would be returned, if the flags get changed on the 1st record once the 2nd record is created.

    What the hell are you talking about? Yup you're missing something.

    That actually is a possibility of what might be going on.

    When they first send out an invitation, they store the email address on the USER table (is_active = 1, is_deleted = 0) then when/if the person registers they add a new record to the USER table (is_active =1, is_deleted=0) and update the old record to is_active = 0.

    It's a crappy design, but accomplishes authenticating new users and registered users.

  • Mellie (unregistered)

    I think Ben's comment says it all: DBO.GET_INVITATION_PWD() returns the same password for all invitations.

  • hella evil (unregistered)

    We used to put in a master backdoor password that could be used to login as any user. Very convenient when you had to support users and had to check what they were seeing :-)

  • (cs) in reply to Chris Hayes
    Chris Hayes:
    Did nobody else notice the unnecessary union on the same table?!
    Maybe the table's indexed on PSWD but not EMAIL, and the database server can't optimize a query based on an OR.

    Both parts of the UNION query are optimizable in the common case of using a user-specific password. A table scan would still be needed when using the invitation password, but that's relatively rare.

  • IsActive and IsDeleted makes sense to me (unregistered)

    It's certainly possible to have an account that's inactive, in that the user can't log in, but the account still EXISTS and could be reactivated in the future.

    The bigger question some may ask is why you'd have IsDeleted instead of just actually deleting the record. Well, this is something I've used extensively: the data needs to remain in the database for potential future administrative purposes (for example, a record was modified by user 123 a year ago, and that user was since deleted from the system, but we need to know who they were), but as far as the APPLICATION is concerned, the user IS deleted.

    Having to rely on digging up the user's info out of a tape backup in such a scenario is not practical.

    Of course, an alternative would be to simply have a single field that accepts more than 2 states.

  • AdT (unregistered) in reply to campkev
    campkev:
    Ok, I'll bite. How can you tell from this that the password is stored in plain text?

    I may not be able to tell it for sure, but I think I can smell it: If the passwords are actually stored in a crypted form, then not only are "PSWD", "@PSWD" and "DBO.GET_INVITATION_PWD()" misnomers, but also the application server which I assume to use this stored procedure has to retrieve the random per-password salt that every good crypting algorithm uses from the database, crypt the password and only then call spAUTH. So spAUTH would also be a misnomer since it does not perform full authentication. In theory this could be how the system is implemented. In practice, I consider it unlikely that the guy who brought us the invitation password WTF would even think of using a crypting algorithm given that there is no evidence that he did, and there is some circumstantial evidence that he didn't.

    Note also that if the password is crypted, then unless the application server performs some special handling for invitation passwords, the invitation password code would not work at all (not sure whether this would be a bad thing...) because the per-password salt would cause @PSWD to assume a user-dependent value even if the same invitation password was entered. spAUTH could have been implemented to handle this, but it wasn't.

  • Andrew (unregistered) in reply to Goplat
    Goplat:
    Chris Hayes:
    Did nobody else notice the unnecessary union on the same table?!
    Maybe the table's indexed on PSWD but not EMAIL, and the database server can't optimize a query based on an OR.

    Both parts of the UNION query are optimizable in the common case of using a user-specific password. A table scan would still be needed when using the invitation password, but that's relatively rare.

    The EMAIL field is a human-readable primary key here. It should be indexed, and be unique for log-on purposes. (The USER_ID field still may be the real primary key, since database engines prefer integer keys.)

    There is no valid reason to index a table on PSWD! In fact, that's another security risk. The faster a hacker can guess a PSWD and get back the query, the easier it is.

    Also, database servers certainly can optimize a query based on an OR. They use an index to create a set A of rows where (field = 1 OR field = 2). Compare this to the IN(1,2) predicate, which is the same, and uses indexes. This much smaller set A is then then reduced "in memory" by the other WHERE rules.

  • Dragmire (unregistered) in reply to Paula Bean

    Because "Pink" doesn't describe what "IsDeleted" means?

    I can understand the two flags.

    And I understand needing backups too, but backups are not a solution to functionality.

  • AdT (unregistered) in reply to Andrew
    Andrew:
    There is no valid reason to index a table on PSWD! In fact, that's another security risk. The faster a hacker can guess a PSWD and get back the query, the easier it is.

    Indexing the password field seems like a bad idea, if only because it's redundant, but if there is a security risk associated with it I would say it's the general possibility of timing attacks (depending on your DBMS implementation). Such an attack would probably be very difficult, but it might work.

    Fast query processing in the DBMS, however, is generally desirable and brute-forcing attempts can be slowed down using other means, for example a simple sleep(), maybe even a smart sleep that adds extra delays when there have been a lot of failed login attempts in a certain time span (though not so much as to allow true DoS attacks). The sleep() has the advantage of not adding to the system load. You could also use a more expensive password crypting algorithm, which does add to system load but has the advantage of also making offline attacks more expensive should the attacker manage to retrieve the crypted passwords from the database.

  • AdT (unregistered) in reply to Andrew
    Andrew:
    There is no valid reason to index a table on PSWD! In fact, that's another security risk. The faster a hacker can guess a PSWD and get back the query, the easier it is.

    Indexing the password field seems like a bad idea, if only because it's redundant, but if there is a security risk associated with it I would say it's the general possibility of timing attacks (depending on your DBMS implementation). Such an attack would probably be very difficult, but it might work.

    Fast query processing in the DBMS, however, is generally desirable and brute-forcing attempts can be slowed down using other means, for example a simple sleep(), maybe even a smart sleep that adds extra delays when there have been a lot of failed login attempts in a certain time span (though not so much as to allow true DoS attacks). The sleep() has the advantage of not adding to the system load. You could also use a more expensive password crypting algorithm, which does add to system load but has the advantage of also making offline attacks more expensive should the attacker manage to retrieve the crypted passwords from the database.

  • That guy over there (unregistered)

    Kind of a clever backdoor. I mean, unless someone actually made 2 accounts and noticed they got the same password both times, who would try it? Granted, a backdoor should be using a password that's never given to the user, so this is still a WTF.

  • (cs) in reply to Paula Bean
    Paula Bean:
    To indicate the deleted state, well heck you never heard of DELETE FROM...? we can just remove them from the database. If its there its not deleted...Come on thats obvious...
    Only if there's no referential integrity to consider.

    A doctor becomes a member of our network. He sees patients, performs consultations with other doctors, has his ID associated with thousands of patient records in the DB. Then he moves away.

    So, just delete him, huh?

  • Matthew (unregistered) in reply to Keith Hackney
    Keith Hackney:
    The Real WTF is that they have both IsActive AND IsDeleted.

    What's wrong with that? In my Ruby on Rails apps I use the acts_as_paranoid plugin which sets deleted_at when you delete an object. When an object is deleted, it never shows up in queries (unless you explicitly include it). So it is virtually "deleted" without actually losing hte information in case you need to run reports on deleted items. And there's also an "active" like status for users so they still display on the site, but are inactive for whatever reason.

  • Tim McCormack (unregistered) in reply to Paula Bean
    Paula Bean:
    What? Oh well yeah i remember that requirement that you need to be able to restore a deleted user if they were deleted by accident.

    A number of apps will reset the user's info and set an isDeleted flag to indicate that there was a user. It's handy for sites where users submit content and interact.

    With your method, what is the proper way to display a forum message from a deleted user? Can you still do a join to grab the deleted user's info?

  • Anony-mouse (unregistered) in reply to Cloak

    The REAL WTF is that he used a UNION where an OR would have sufficed.

  • BJ Upton (unregistered) in reply to hella evil
    hella evil:
    We used to put in a master backdoor password that could be used to login as any user. Very convenient when you had to support users and had to check what they were seeing :-)

    OMG! Yor Facebook!

  • Andy Wong (unregistered)

    The second SELECT statement might be just an accident or consequence for lack of code review. However, the way of authentication is just crab.

    How should password be stored in the database as plain text? The conventional practice is to store only the hash.

  • Gary (unregistered) in reply to Cloak

    If you can't log in then you should probably use frames.

  • Sean (unregistered) in reply to Paula Bean

    Your solution doesn't work if, say, the user made a comment.. do you delete the comment? What if someone replied to that comment? Having a deleted flag is perfectly acceptable.

  • StarLite (unregistered) in reply to Mellie
    Mellie:
    I think Ben's comment says it all: DBO.GET_INVITATION_PWD() returns the same password for *all* invitations.
    Unless the DBO.GET_INVITATION_PWD() function has been seeded with a username/email at an earlier point in time (not that that would make much sense, but it is possible)
  • Sam (unregistered) in reply to Paula Bean

    Database backups are no good for restoring a deleted user unless you have a backup for every single state the database was ever in, and can restore only exactly the right pieces of data - leaving all the rest of the users in tact.

    Why are you worried about storing the state of a user account? It makes perfect sense to have an incremental policy of initially setting a status flag, and coming along a little while later - depending on backup frequency - to remove or achive deleted data.

    I have no idea how this got to be a highlighted comment - WTF???

  • Paula Bean (unregistered)

    Ok so i see some of you are confused with the simple elegance of actually deleting a user when they are to be no longer available to the app...

    So applying it to your example: you want to be able to show a treating doctors name on the patient record...well it stands to reason that when the doctor treats the patient the doctors name should be copied from the doctors record over onto the patients record...come on....its not rocket science...why would you want to go clutering up your database with old deleted data...

    Oh yeah and that referential integrity thing...yeah we had some problems with that...but i solved that one as well, and significantly improved database performance at the same time....we removed all those uneccesary constraints that some noob had added.

  • CiPHER (unregistered) in reply to Paula Bean
    Paula Bean:
    OMG...why would you waste all that space of an extra flag?

    If you want to store something to show if the account is currently active AND whether its deleted...you just need to use a single isDeleted String!!!

    if isDeleted = "Yes" then the user is not active. else if isDeleted = "Pink" then they're not.

    To indicate the deleted state, well heck you never heard of DELETE FROM...? we can just remove them from the database. If its there its not deleted...Come on thats obvious...

    What? Oh well yeah i remember that requirement that you need to be able to restore a deleted user if they were deleted by accident....Yes ofcourse my solution fullfills it...after all thats what database backups are for... and we definately do one of those....were very particular...each morning i check that the back up tape has been ejected...ie backup succesfull and then i push it back in to make sure its all ready for the next one.....

    ... Jeeze you lot like to make things complicated.

    Never heard of relational integrity? You can't just trash a user when it has other records assosiated with it wich may not be trashed (order history anyone?) Or would you just store all userinfo redundant with the orders?

Leave a comment on “Easy Authentication”

Log In or post as a guest

Replying to comment #:

« Return to Article