Dropping /etc/shadow
Ca. 451 ord.
On UNIX-systems a normal user nolonger have access to the encrypted
password in /etc/passwd. The encrypted password is stored in
/etc/shadow and is only readable by root. This is done so that an
attacker cannot check every word in a dictionary and combinations
thereof (do a dictionary attack) against the encrypted password.
However, it is possible to make this hard and thereby removing
the need for protecting /etc/shadow.
Per user seed
One of the nice features of /etc/shadow is that if you copy the
password field from one user to another then the other user will
have the same password.
Password crackers use this to encrypt one word (which is
expensive) and test the encrypted word against every user's
encrypted password. E.g. if enc(foo)=bar and user Quux has the
encrypted password bar, then Quux's password is foo. If user Baz'
encrypted password is bar then Baz' password is also foo.
This could be avoided if the encryption had to take a seed with
it. E.g. Let Quux get a random number r; encrypt Quux's password as
enc(foo+r)=fubar. If Baz has another random number q and Baz'
encrypted password is also fubar, then it is highly unlikely that
Baz' password is foo, as it is highly unlikely that
enc(foo+q)==fubar.
The per user seed should be larger than the number of users to
avoid two having the same seed and can be stored in the encrypted
password field along with the encrypted password. The format could
be:
:$$seed$encryptedpassword$:
The feature of copying passwords by copying password fields is
preserved.
Expensive testing
It is fairly cheap to check if a password is the correct password.
Password crackers can try more than 100000 passwords per second.
However it does not have to be so. It is possible to make the check
much harder. By making it hard to check 1 password then a login
will take a bit longer, but checking 100000 passwords will take
100000 times longer.
A simple way to do this is to encrypt the ecrypted password
again and again for a period of time. A simple algorithm could be
like this:
decrypted_password = "foo";
encrypted_password = encrypt(decrypted_password);
encrypted_times=1;
time=now();
while(time-now() < 1 sec) {
encrypted_password = encrypt(encrypted_password);
encrypted_times++;
}
Encryption would take 1 sec. You would need to save both the
encrypted_password and the number of times it was encrypted,
e.g.
:$$seed$encryptedpassword$number_of_encryptions$:
Checking the password would be:
decrypted_password_to_check = "foo";
encrypted_password = encrypt(decrypted_password_to_check);
encrypted_times=number_of_encryptions;
while(--encrypted_times) {
encrypted_password = encrypt(encrypted_password);
}
if(encrypted_password == encrypted_password_from_password_file)
On the same machine this would take 1 sec. On a faster machine this
will be faster, but not nearly as fast as a simple
hash-function. |