Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

CGI and saving passwords

by JoeJaz (Monk)
on May 03, 2004 at 20:36 UTC ( [id://350128]=perlquestion: print w/replies, xml ) Need Help??

JoeJaz has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I am developing a CGI script and am looking for a way to allow users to log into it and have certain privlidges once they do. However, every time I look into a method for authenication, I keep finding holes. First, I tried using a password file to keep username/password pairs. Then, when a user logs in, I would check to see if the username exists and the password is correct in the password file and then I would set a cookie (with the username and password). For every subsequent page, I would read the cookie and compare it to the password file to see if the user exists and the password is correct. Perhaps leter, I will hash the passwords with MD5. However, then I found out about CGI::Session. After learning about this and crudely implementing it, I found out that everyone who has a login to the server can read the session data in the /tmp directory. So basically, I have a choice of having a world readable password file (since the apache user needs to be able to read it) or a world readable session file in the /tmp directory. Does anyone know the preferred method for storing username/password paris to allow a user access to only what they should have access to and still be secure on the server? Note: I am using a public server (one for which I do not have root), but have found a way to add needed modules within my program directory. I would be very greatful for any advice. Thank you for reading this. Joe

2004-05-05 Edit by jdporter: Changed title from 'Module relocation'

Replies are listed 'Best First'.
Re: CGI and saving passwords
by BUU (Prior) on May 03, 2004 at 22:26 UTC
    Theres a simple, two part answer to this question.

    The first part: Don't store username/password in cookies, instead store a simple session identifier. This session identifier should be a randomly created string of probably at least 10 characters so it's impossible for a person with a session to guess the identifier to some one elses session. Digest::MD5::md5_hex plus rand,$$ and time should probably suffice.

    What the session idenfitier allows you is to store all of the "sensitive" data someplace on the server so the user accessing your website can't edit it or even see it.

    The second part is to use crypt (or md5, or any other one way hashing function) to store hashed version of your passwords on the server. Then you take the plain text password submitted by the user, hash it, and compare it to the hashed version you have on disk. If it matches, the password is correct. The advantage to one way hashing functions, such as crypt is that theres no (known) way to get the plain text back from the hash, so even if other users can read the password file it won't do them any good. (This is how the /etc/passwd file basically works on linux installations (ignoring shadow passwords))

    These two suggestions, combined, will probably give you just about the most security you can reasonably expect from using a "public" server you don't have full control over.
      This looks like a good scheme and will be what I will work toward. Thanks for sharing your idea.
      Great thread, and a great comment. ++ to parent.

      I would like to make one quibble. The intention of one way hashes is to have there be no known way to get the plain text back from the hash, but in the real world, evil people can be very clever, especially when there is a monetary reason to be so clever, or if someone claims "there is no way...".

      IANAH (I am not a hacker) but I know that many one-way hashing cracking programs are available. They can be surprisingly successful on realworld hashes (passwords). Consequently, please remember the following limitations to one way hashes.

      * Input strings should be 8 or more characters and should include numbers, symbols and capitol letters. (if not, it can more than likely be cracked).
      * Using words and names as part of your passwd weakens them considerably. Using only a word is like having no passwd.
      * Using "3" for e or "@" for a in your passwd won't help at all -- crackers know these tricks.

      Cheers

      -------------------------------------
      Nothing is too wonderful to be true
      -- Michael Faraday

        it's true that there is nothing to stop someone from running a brute force attack on a one way hash. However, the reason that people are encouraged to occansionally use non-alphanumeric characters in passwords is simply to slow the cracker down. Using upper and lower case letters increases the number of possibilities from 26 to 52, using numbers increases it 62, using non-alphanumerics increases it again. All highly worthwhile practices. This is also the reason for having as many characters as possible in the input string and stay away from real words, both of these techniques slow down a cracker.
        I found it quite amazing that one can find over 50MB of dictionary files on sites related to the "crack" program. I guess if you can think of a word, someone else has thought of it as well and included them in those lists. (not that I have used them in any other way than audits on my own system, but still impressive that someone has gone to the time to make those lists so large). Joe
Re: CGI and saving passwords
by matija (Priest) on May 03, 2004 at 21:38 UTC
    Without CGI::Session you have
    • a readable password file (apache user must read it)
    • a readable password in the cookies - anybody on the packet path can read it with a sniffer

    With CGI::Session you at least avoid the second part. And if you have readable passwords in one place, one more place won't make that much of a difference.

    No matter how you slice it, your apache process must be able to read the passwords to verify them. And it must be able to read your script. So even if you encrypt your passwords to protect them, the bad guys can just read your script, and use that to decypt the passwords.

    Face it: you can't secure passwords on a server where other people have root. Root, if no-one else, can read everything.

    You have to make a weighted decision: are your passwords valuable? If they are, get your own server, and be the only one with root. If they aren't worth the cost of a separate server, perhaps no-one will bother getting an account on that exact shared server just to steal your passwords.

    Just one caveat (I know it's not what you're asking, but it's worth mentioning): Do not ever accept credit cards (or Ghu forbid store credit card info) on a shared server. Because that is just asking for trouble.

    Get your own server, or have the CC transactions handled by a merchant service, but if you're on a shared server, don't do it yourself.

      No matter how you slice it, your apache process must be able to read the passwords to verify them. And it must be able to read your script. So even if you encrypt your passwords to protect them, the bad guys can just read your script, and use that to decypt the passwords.
      No, passwords can be "encrypted" with a one-way hash function, like crypt(3) or MD5. That's how the Unix /etc/passwd file works. The plaintext password are hashed and compared with the stored encrypted password.
      That's very helpful. I guess I'll take the time to refine the CGI::Session implementation considering that it will afford me a bit more security. I guess there is no foolproof way to be totally secure in this situation... but my passwords in this case are not top secret. Good to know about the CC transactions. Thank you very much for your help.
Re: CGI and saving passwords
by Ryszard (Priest) on May 04, 2004 at 05:31 UTC
    There are three methods to maintain state on the web:
    1. Cookies
    2. Hidden tags
    3. Mangled url

    Essentially 1 and 2 are the same, pretty much sending some kind of token that you later get back and verify that everything is ok.

    Point 3 can be split into two sub categories:

    1. Creating a token as part of the URI
    2. Adding the token to a parameter
    If you're doing the 1st option its a little more work getting the url back, parsing it and extracting the token. the 2nd point is pretty much the same as the top lot of methods. (you can reference it by $q->param('token') with CGI.

    In terms of building the token, the most accepted and secure way is to generate a unique string that has no direct relevance to the user in question. The token will be stored server side along with the user associated with it (you can also store other stuff like expiry).

    Mechanically, acutally building the token is pretty damn easy. I've rolled my own using MD5 that pretty much will give a unique token every time: md5_hex('s3cr37 s7r1n6'.$userid.$$.localtime().rand());

    How you store the association is pretty much up to you, i personally use a postgres database, but you can go with a flatfile, encrypted file, storable, a tied hash, some kind of caching module (gives you expiry by the length of the cache timeout) or whatever floats your boat.

    For added security, you can rotate the token each page view. So you get the cookie, (read the token) look it up in your db, if it matches, generate a new token, update the cookie, then update your database.

    Maintaining state with HTTP is not hard, however may be a little bit of work depending on whatever implementation path you choose. There are plenty of resouces out there, and its not hard to get it right and (relatively) secure the 1st time.

    Update: I forgot authorisation. Its all server side, your user will log in with a username and password, this should be hashed using something like crypt, md5, sha1 or "Your fav hashing algo (tm)". Its then a simple matter of doing an encrypt and compare server side. Each time an existing user logs in, you get the password supplied, hash it, and compare it to the password you already have server side. If there is a match, you issue the token, if not, you kick them out.. (or whatever your procedure is).

      I like your md5 function. Hashing against the username, time, and a random number... seems like it would yield some pretty unique results :-) Thanks for pointing out some other methods of storing the token. I thought I only had a flat file or DB as an option. That Storable module looks fairly interesting... compiled in C; very fast! I also like your idea of rotating the token for each page call. Seems like a good method to keep crackers on their toes. I have got to try some of this stuff out. Thanks for your help. Joe
Re: CGI and saving passwords
by flyingmoose (Priest) on May 04, 2004 at 14:58 UTC
    All good comments (except for the part about saving passwords in cookies! Bad coder! No donut!), but I'll add in some comments that some folks have not yet made.

    Given the choice of a hashing algorithm, please advice new folks to use the strongest available... this means SHA1 over MD5, and MD5 over crypt. Better SHA is also an option ... look at CPAN for modules available. Might as well be paranoid. "YourFavoriteHashingAlgorithm" certaintly doesn't mean you can roll your own, at least not if you have any ethics -- it's far too easy to make a weak function.

    You say 'most likely will give a unique ID', but you (in the case of dealing with Credit Cards, especially) might have an obligation to make sure they ARE unique. Good security is based on good math, not chance. One of the failings of random number generators is that they are not unique, so again, it never hurts to be paranoid.

    Perhaps obvious, but when possible, use SSL. This not only encrypts the session from basic packet sniffing, but (when using certs), can tip someone off to a man-in-the-middle type attack, where you could be tricked into authenticating to a server that wasn't really the server you were connecting to.

      I had no idea that SHA1 is better than MD5. I'll have to use that instead. It appears I don't get a donut as well :-) My original implementation was to store password cookies, but after learning about sessions and all of the other options that were much more secure than cookies... I don't know what I was thinking. Good to know about the Credit Cards and the SSL. Luckily I don't need that for this project, but I have always been curious about how to design transaction-secure applications. Thanks for your advice. Joe
        At this point in time, AFAIK, the difference between sha1 and md5 and their respective security is prety well academic (I'll stand corrected, i'm not a math/crypto geek).

        Another point to note is sha1 can be a little slower to compute than md5, however on modern hardware, the difference is probably not material.

        All that said, the effort between your code using sha1 and md5 is actually zero, so you may as well use the stronger algorithm right off the bat.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://350128]
Approved by Corion
Front-paged by dragonchild
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-03-29 05:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found