Are there known issues with the C function crypt()? - c

I use crypt() to encrypt passwords for a project of mine. When the password is chosen by the user, it's encrypted like this:
password = crypt(<password chosen>, <user's account name>)
The problem is when the user logs in using their password. If what they type doesn't match their password, it should enter this if check:
if (strcmp(crypt(<what user types in as password>, <user's account name>), <user's encrypted password>)) {
//...
}
It doesn't in one certain scenario. Let's say their password is 'asdf'. If they enter 'asdf' with any random trailing characters, such as 'asdffffff' or 'asdf339sfd', it still accepts the password. It seems to ignore everything after 'asdf'.
Is this a known issue with crypt? Is there another way to encrypt passwords?

The second argument to crypt is not supposed to be the user's account name. It's supposed to be a setting string. Setting strings look like this:
$2b$07$fQuDK3TaQP4sw6IX6iVcTw
The $2b$07$ part tells crypt which password-hashing algorithm to use, and the string of random characters that follows is the salt. The salt must be different for each user, but it is not supposed to have any correlation with the user's account name. It doesn't technically have to be random, but it's critical that it be different for each user, and it needs to change every time the user changes their password, so best practice is to use a long string drawn from a cryptographic PRNG.
When you authenticate a user who has logged in before, you use the stored hashed password as the setting string:
char *new_hash = crypt("password typed in", "stored hash");
if (new_hash && !strcmp(new_hash, "stored hash")) {
// user has successfully logged in
}
This works because the stored hashed password always begins with the setting string that was used to create it in the first place, and crypt is coded to look only at the setting-string part.
(Also note the null check; some implementations of crypt can fail and report failure by returning a null pointer.)
When you create a new account or change a password, you have to generate a new setting string. If you have the function crypt_gensalt, use that:
char *new_setting = crypt_gensalt("$2b$", 0, 0, 0);
if (new_setting) {
char *new_hash = crypt("user's new password", new_setting);
// ...
} else {
// halt and catch fire
}
If you don't have crypt_gensalt you have to implement it yourself, unfortunately. (To make matters worse, some Unixes have the crypt_gensalt whose documentation I linked to above, and others have a different version, with the same name, doing the same job, but taking different arguments. Time to dust off your Autoconf skillz!)
Now you know all of that, I can explain why
password = crypt("password chosen", "user's account name");
appeared to work but truncated the password. Your user account names probably begin with at least two alphanumeric characters, right? Like, "Ma[ya]", or "zw[ol]"? Unfortunately, any two alphanumeric characters make a valid setting string ... that selects one of the oldest and least secure password-hashing algorithms known to science, descrypt. (It was pretty good when it was invented ... in the mid-1970s. Nowadays, it can be cracked by brute force no matter what the password is.) One of the many problems with this algorithm is that it truncates all passwords to eight characters. asdf and asdfhjkl hash to different things, but asdfhjkl and asdfhjkl1234 hash to the same thing.
The cure for this is to use crypt_gensalt or equivalent to select a modern algorithm. All the modern algorithms accept arbitrarily long passphrases.

Related

Two Dimension Array of size 10

starting with programming and would like tips on writing pseudocode for a 2 Dimension array in a main class that stores usernames and passwords for each account.
Program should take a username input, store it in the array - preferbly using a function(Function that adds usernames and passwords or different functions adding each seperately), and then a password store it in a correspoding dimension to the user name enter - for an array sizeof 10: ArraySize(10)(10) all
Should be able to login(Account Search Function - could search both password and function using the same function or two function searching each part separetly) to the account with check of password combination(Function that checks password limit = 3)
Should be able to delete. So login first, and then delete an invidual account.

What's the safest encryption I can use for a user to login to my program?

I'm writing a program and before it loads I want the user to enter the correct password without storing the password anywhere in my code. I've implemented MD5 hashes before but from what I've read they're outdated and can be broken. There are a few sites out there that attempt to reverse engineer and MD5 hash. What's the strongest encryption I can use to keep prying eyes out of my program (e.g., The NSA)?
"Encryption" is not the right thing to do for storing user passwords - as by design an encrypted password can be decrypted. As you said - hashing is the way to go.
MD5 is outdated, and I believe the current recommendation is sha1.
Note that there are ways to reverse any hashing algorithm to acceptable input. The commonly accepted standard to make this much more dificult is to add a unique "salt" to all passwords before putting them through the hashing function. A common mistake made when adding salts to passwords is to use the same salt value on every password in the database.
When salting passwords, use a unique value, for example the user ID, or the created date/time string for the user record. This will prevent attacks based on rainbow tables because there will be no existing ready to use rainbow table for your stored password hashes.
I personally like the approach of using the created date / time string of the user as it's a value that should never change and will be available and will likely be different for each user the the database.
Eexamples below assume you are familiar with PHP - however the concepts can be applied to any language.
Example:
Before saving a new user into the database:
$date = date('Y-m-d H:i:s');
// save this same value into the user record somewhere
$passwordHash = sha1($user['created_date'].$_POST['password']);
// and save the $passwordHash value into the password field for that user
To authenticate a login attempt, use something like the following:
function authenticateUserLogin($email, $password) {
$user = $db->fetchRow('SELECT * FROM users WHERE email=?', array($email));
if (!$user) return false;
$passwordHash = sha1($user['created_date'].$password);
return $user['password_hash'] !== $passwordHash;
}
To update an existing users password, use something like...
$passwordHash = sha1($user['date_created'].$newPassword);
$db->query('UPDATE users set password_hash=? WHERE id = ?', array($passwordHash, $user['id']));

User ID encoding on the URL?

I would like to get some information from the user profiles of a certain domain.
www.domain.com/profiles/[userID]
The main problem is that the user ID is like:
4fc34f1ad3d85a000300b5a4
4fc34f1ad3d85a000300b5a4
4f4bdd96848740000300026a
4f09884f9cc1590001019c98
4f1bcd5f33aa850001011811
4faa2801c619ad0003011c7e
4e4f506ac9d69600010010ca
so if I wnat the user profile of a certain user I have to put
www.domain.com/profiles/4fc34f1ad3d85a000300b5a4
www.domain.com/profiles/4f4bdd96848740000300026a
www.domain.com/profiles/4f09884f9cc1590001019c98
www.domain.com/profiles/4f1bcd5f33aa850001011811
www.domain.com/profiles/4faa2801c619ad0003011c7e
www.domain.com/profiles/4e4f506ac9d69600010010ca
What kind of ID is that? How I know the subjacent canonical order?
Other times was a lot easier because the user ID only was integers.
www.domain.com/profiles/1
www.domain.com/profiles/2
www.domain.com/profiles/3
www.domain.com/profiles/4
www.domain.com/profiles/5
I would like to know if this is some kind of encoding or what.
What kind of ID on the URL is that?
Thank you in advance.
Looks like an MD5 hash. So either it's a random hash, or it might a hash of the previously used ID, or something completely different. Either way apart from brute force guessing, there's no simple way to decode them.
http://en.wikipedia.org/wiki/MD5
These ID's use hexadecimal - notice there are no letters above 'f'.
The similarities between ID's are interesting - could you provide the ID's of some consecutively created pages, to check if there is a pattern to how the ID changes.
Or the website may just generate a random number between certain limits and convert to hex.

Store user name as separate first/last name, or as a single full name String?

I have a web app, I'd like the user to supply their real name, for friend searches. I'm not sure whether to store this as two separate fields in my user class, or as a single field:
class User {
#Persistent
private String mFirstName;
#Persistent
private String mLastName;
}
.. or ..
class User {
#Persistent
private String mFullName;
}
I'm only going to use it to let users search for people. For example, they might search for "John", or "John Doe", or "Doe". I'm not sure what the app engine query engine allows us to do here, in terms of partial matches and such - has anyone gone through this and can recommend a good solution? I'm leaning towards just storing the full name to make searches easier,
Thanks
It's not just a question of how you end up storing names, it also matters how you ask for names on your web form.
If you prompt with a first and last field, the vast majority of inputs will probably conform, but you'll still have many exceptions (prefixes, middle names, suffixes, punctuation, etc).
If you clearly prompt with separate prefix, first name, middle name, last name, suffix fields, there'll be even fewer exceptions, but users might get peeved or confused.
You might even offer both: an easy one-field input or preparsed multifield input. Explore what other web sites do and see if you find them appealing/confusing/whatever.
Also keep in mind that if you input separate fields you can always easily join them later, but if you input only a single field you won't have the typist's help if you need to parse it later.
Short answer: store a full name.
Long answer here (Falsehoods programmers believe about names, by Patrick McKenzie).
I’m going to list assumptions your systems probably make about names. All of these assumptions are wrong.
(#1) People have exactly one canonical full name.
(#20) People have last names, family names, or anything else which is shared by folks recognized as their relatives.

get the default email from the user on a Linux box

Is there any way to programmatically get the current user's email address?
I know the email is usually user#hostname but is there any I can get the email?
I know how to get the username and the hostname so I can build it myself, but I want to be sure that I get the email address even when the email is not user#hostname.
Code in C is appreciated.
Thanks
There is no such standard mapping of user account to email address - at least not for ordinary /etc/passwd derived accounts. Consider that a user might not even have an email address.
Nobody's mentioned the GECOS fields in the /etc/passwd file.
You'll notice that the fifth field in your entry in /etc/passwd is either blank, or a comma-separated list the first element of which is your full name. Originally in Bell Labs (before the days of email) the GECOS fields were:
User's full name (or application name, if the account is for a
program)
Building and room number or contact person
Office telephone
number
Any other contact information (pager number, fax, etc.)
Some Linux distributions store the user's default email address in the 4th GECOS field, and if your system doesn't do this by default, you can set it up yourself. Ordinary users without superuser privilege can edit their GECOS fields using the command line command chfn. To access this field, you can then do
grep ${USER}: /etc/passwd | awk -F\: '{print $5}' | awk -F\, '{print $4}'
or whatever floats your boat in your language of choice (No, I am NOT going to write C. This is the twenty-first century!).
There is no standard mapping of user accounts to RFC822 (i.e. user#domain) email addresses. Generally, a default setup of typical mail transfer agents will accept local mail to addresses without a domain and deliver it to the user account of the same name. But even that can't be relied on, as you may not even have an MTA.
The UNIX way of doing this is to send email through the local mail-transfer-agent - simply invoking /usr/bin/mail is enough. The system administrator is responsible for configuring the local MTA to make sure email works properly.
If you want to send email to the local user, just send it to their username - if they read their email somewhere other than locally, the MTA should be configured to forward it to them.
If you just want to use the right "from" email address when sending email on behalf of a local user, so they get replies in the right place - again, just use their username. The MTA should be configured to do the right translation.
This way of doing things is good, because it means that this configuration only has to be done in one place (the MTA), rather than having to manually configure every single application on the box that sends or recieves email.
Just to complement Simon's answer and given I don't have enough reputation to make a comment on it, GECOS stands for General Comprehensive Operating System aka General Electric Comprehensive Operating Supervisor and the most portable way I found to get the user GECOS field (As it might not be defined in your /etc/passwd file directly depending on your system's configuration) is the following:
getent passwd <USERNAME> | awk -F ':' '{print $5}'
It depends how the user is stored. In a simple passwd file there's no email address, only a username. But you can have additional information with other authentication method like LDAP or SQL.
Prompt the user for their email. If you have no guarantee that the email is user#hostname, then how else do you expect to determine what their email is other than asking them?
You can't get the actual email address in any standard way. I would try to send the mail to just username. Chanses that it will end up on the correct domain are actually not that bad ...
Check in the terminal you're using, that is :
root#peter-laptop#
for root users it is shown before the # sign, that is
root#peter-laptop or peter#peter-laptop# for user peter
Try to get to /var/mail/ and there you should have a file for each user that has (not all users have to have it) an email address. And you can indeed read the mail from those files.
Then you can redirect the mail to anywhere else with the sendmail tool.

Resources