ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


O'Reilly Book Excerpts: Practical Unix & Internet Security, 3rd Edition

Secure Programming Techniques, Part 4

Related Reading

Practical UNIX and Internet Security
By Simson Garfinkel, Gene Spafford, Alan Schwartz

Editor's note: In the previous installment in this multipart series of excerpts from Chapter 16 of Practical Unix & Internet Security, 3rd Edition, we offered tips on writing SUID/SGID programs, and on using chroot(). In this week's final excerpt of the series, we have tips on using passwords more securely, and on generating random numbers, both of which play important roles in maintaining computer security.

Tips on Using Passwords

Lots of computer programs use passwords for user authentication. Beyond the standard Unix password, users soon find that they have passwords for special electronic mail accounts, special accounting programs, and even fantasy role-playing games.

Few users are good at memorizing passwords, and there is a great temptation to use a single password for all uses. This is a bad idea. Users should be encouraged not to type their login password into some MUD that's running over at the local university, for example.

As a programmer, there are several steps that you can take in programs that ask for passwords to make the process more secure:

  1. Don't echo the password as the user types it. Normally, Unix turns off echo when people type passwords. You can do this yourself by using the getpass( ) function. In recent years, however, a trend has evolved to echo asterisks (*) for each character of the password typed. This provides some help for the person typing the password to see if they have made a mistake in their typing, but it also enables somebody looking over the user's shoulders to see how many characters are in the password.

  2. When you store the user's password on the computer, concatenate a key and a salt, and encrypt the password with a cryptographically secure one-way function. Never have programs store passwords in plaintext form in files or databases. If this file is compromised, all of the passwords need to be changed!

    Traditionally, the easy way to store a password on a Unix system was to use the Unix crypt() library function with a randomly generated salt. For example, the following bit of simple Perl code takes a password in the $password variable, generates a random salt, and places an encrypted password in the variable $encrypted_password:[20]

    my $salts=
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
    my $s1 = rand(64);
    my $s2 = rand(64);
    my $salt = substr($salts,$s1,1) . substr($salts,$s2,1);
    my $encrypted_password = crypt($password,$salt);
  3. You can then check to see if a newly provided password is in fact the encrypted password with this simple Perl fragment:

    if($encrypted_password eq crypt($entered_password, $encrypted_password) {
      print "password matched.\n";
    }
  4. This code fragment can be significantly improved by rewriting it to use the MD5 message digest algorithm:

    use Digest::MD5 qw(md5_base64);
    
    my $salts=
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
    my $key  ="justakey";
    
    my $s1 = rand(64);
    my $s2 = rand(64);
    my $salt = substr($salts,$s1,1) . substr($salts,$s2,1);
    my $encrypted_password = $salt . md5_base64("$salt/$password/$key");
  5. To verify this password, we would use:

    use Digest::MD5 qw(md5_base64);
    
    my $salts=
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
    my $key  ="justakey";
    
    my $salt = substr($encrypted_password,0,2);
    my $pw2 = $salt . md5_base64("$salt/$entered_password/$key");
    
    if($encrypted_password eq $pw2) {
        print "passwords match.\n";
    }

The primary benefit of using a cryptographic hash value is that it takes whatever input the user types as the password, no matter how long that value might be. This may encourage users to type longer passwords or passphrases that will be more resistant to dictionary attacks. You might also want to remind them of this practice when you prompt them for new passwords.

Tips on Generating Random Numbers

Random numbers play an important role in modern computer security. Many programs that use encryption need a good source of random numbers for producing session keys. For example, the PGP program uses random numbers for generating a random key that is used to encrypt the contents of electronic mail messages; the random key is then itself encrypted using the recipient's public key.

Random numbers have other uses in computer security as well. A variety of authentication protocols require that the computer create a random number, encrypt it, and send it to the user. The user must then decrypt the number, perform a mathematical operation on it, re-encrypt the number, and send it back to the computer.

A great deal is known about random numbers. Here are some general rules of thumb:

  1. If a number is random, then each bit of that number's binary representation should have an equal probability of being a 0 or a 1.

  2. If a number is random, then after each 0 bit in that number's binary representation there should be an equal probability that the following bit is a 0 or a 1. Likewise, after each 1 there should be an equal probability that the following bit is a 0 or a 1.

  3. When examining a large set of random values, each with a large number of bits, then roughly half of the bits should be 0s, and half of the bits should be 1s.

For security-related purposes, a further requirement for random numbers is unpredictability:

  1. It should not be possible to predict the output of the random number generator given previous outputs or other knowledge about the computer generating the random numbers.

  2. It should not be possible to determine the internal state of the random number generator.

  3. It should not be possible to replicate the initial state of the random number generator, or to reseed the generator with the same initial value.

One of the best ways of generating a stream of random numbers is to make use of a random process, such as radioactive decay. Unfortunately, most Unix computers are not equipped with Geiger counters. Thus, they need to use something else. Often, they use pseudorandom functions as random number generators.

A pseudorandom function is a function that yields a series of outputs that appears to be unpredictable. In practice, these functions maintain a large internal state from which the output is calculated. Each time a new number is generated, the internal state is changed. The function's initial state is referred to as its seed.

If you need a series of random numbers that is repeatable, you need a pseudorandom generator that takes a seed and keeps an internal state. If you need a nonreproducible series of random numbers, you should avoid pseudorandom generators. Thus, successfully picking random numbers in the Unix environment depends on two things: picking the right random number generator, and then picking a different seed each time the program is run.

Pages: 1, 2, 3

Next Pagearrow





Sponsored by: