Encrypting user passwords, save secret key to file - encryption

I'm trying to make secure keeping users passwords in database. After quick research I decided use ed25519_dalek library.
Important notice:
I plan use this encryption in actix website.
For now steps are simple:
save password in variable,
generate keys,
save secret and public passwords in file(for now),
encrypt password,
save password to database(skipping for now),
I'm stack in third step: saving keys to file.
To be honest I can write passwords to file, but can't read it.
My code below:
use std::io;
use std::io::{BufRead, Write};
use rand::rngs::OsRng;
use ed25519_dalek::{Keypair, PublicKey, Signature, Signer, Verifier};
use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
let mut csprng = OsRng{};
let keypair: Keypair = Keypair::generate(&mut csprng);
let password: &[u8] = b"testing_password123!##$%";
let signature: Signature = keypair.sign(password);
assert!(keypair.verify(&password, &signature).is_ok());
For this moment my code works well. I'm getting signature and looks like values password and signature are the same.
I'm trying to deserialize keys:
let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = keypair.public.to_bytes();
let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = keypair.secret.to_bytes();
let keypair_bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes();
let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes();
After this, I want to save secret key and public key to different files. But for simplify we want to save only secret key.
My steps below:
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
let filename = "keys.txt";
let path = Path::new(filename);
let display = path.display();
let mut file = match File::create(&path) {
Err(why) => panic!("Couldn't create {}: {}", display, why),
Ok(file) => file,
};
match file.write_all(&secret_key_bytes) {
Err(why) => panic!("couldn't write to {}: {}", display, why),
Ok(_) => println!("successfully wrote to {}", display),
};
In this step I can write bytes to a file. But... The key has type [u8;32]. Using cat command showing strange things(I understand why).
But I can't read this file. Bytes can't be read like this.
After this entry is time for my question:
How can I change type of secret key [u8;32] to String or str?
My idea is:
Change datatype to String or str and then save to file.
Or maybe I do something wrong or simply is it better way to do this?
I need little push in right way :)

It is unsafe to encrypt and store passwords in a database. Moreover you are planning to store both public and private keys in files. The program requires it to be kept in reach for authenticating, so it won't be hard for someone to find the files with public and private keys. Anyone who have access to those files can access users' passwords including you.
The workaround is to generate a hash from the password and store it in the database. Hashing is a one way process of generating a string from an input string such that the inverse operation is impossible. So,the hash of the user input and the hash in the database is matched to authorize the user. There are a bunch of hashing functions available in RUST including the SHA-2 family hashing functions.
It is still insecure if you are just hashing the passwords and saving them in the database since the hashes of most common passwords and almost all possible words are already available in datasets named hash dictionaries and by using that, anyone who have access to your database can find the password. This technique is called dictionary attack. To prevent it, you can make the input string larger by concatenating with some random characters (salt) with the user input and
store the hash and the salt in the database.

Related

I need an alternative to initialization vector encryption

I helped a small non-profit create a cool "anonymous" help line where their clientele can send in SMS questions to a phone number and organization can respond without knowing who the client is. It's proven to be a very helpful tool for the organization. I am in the process of re-writing it from scratch and am looking for advice on how to handle the anonymity aspect of it.
The way that v1 works is by simply encrypting / decrypting the client phone number and using that as the client id in the data model using 'aes-256-ctr'. Since the staff / leadership of the nonprofit do not have access to the encryption key, they can legitimately claim to not be able to have access to any p.i.i. not disclosed voluntary by their clients. When they issue a message response to the client, I simply need to decrypt the client id to get the phone that I need in order to dispatch the message.
v1 encrpytion strategy:
export const encrypt = (text) => {
const cipher = crypto.createCipher(algorithm, password);
let crypted = cipher.update(text, 'utf8', 'hex');
crypted += cipher.final('hex');
return crypted;
};
export const decrypt = (text) => {
const decipher = crypto.createDecipher(algorithm, password);
let dec = decipher.update(text, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
};
Lately though, I've been getting console warnings because new node versions will output a warning when using createCipher instead of createCipheriv to utilize initialization vector. On my v2 rewrite I switched it over to createCipheriv, but that has the effect of "randomizing" the encryption output and breaking my ability to rely upon the encrypted phone number as a reliable, consistent client id. Being able to reliably group client messages is an important requirement to provide a conversational view in the UX.
How might I properly encrypt the identifying phone number such that only I can decrypt it at message send time and still have some kind of consistent internal ID by which to group messages? I don't HAVE to rely upon the encrypted phone number as the client id, but I can't think of any reliable alternative (via a lookup table of some kind). If possible I don't want to fall back on createCipher without the I.V. if its not a best practice.
I should note that this application is not dealing with any legally sensitive information or anything high stakes like that. I probably could get away with storing the unencrypted phone number in the database and just not exposing it to the front end and maintain anonymity, I just really don't like that idea if there is a sensible workaround.
Any ideas?
You should be generating a new random (or UUID-style) client id for each new client. The benefit of this is that you can keep using a secure encryption method and still identify and lookup users quickly.
If you want the phone number field to act as a unique lookup, simply take an HMAC of the phone number and use that for lookups (as well as storing encrypted) - it can't ever be decrypted and is guaranteed to be consistent across invocations for the same phone number under a given key.
EDIT: To migrate existing data, you should, for each record, take the client_id (currently the encrypted phone number), decrypt it, HMAC it, and store the result in a new column. Then, generate a new ID and set that to be the client_id. This allows lookup by actual client_id and by phone number.

Theory of Retrieving Hashed Passwords

This is a theory question, as I already know the following:
Hashes are not encryptions
Hashes can be broken https://crackstation.net/
That my code is not serious enough to be used on an enterprise site
But my question is just on the theory of it all.
In theory, if I stored a hashed password into a database, how could I check it when a user comes to log in? Is it that simple or is there a standard way to actually store and retrieve passwords in SQLite3?
If you can offer some SQLite3 code or guidance on where to start, that would be appreciated.
import sqlite3
import hashlib
def encrypt_password(password):
encrypted_pass = hashlib.sha1(password.encode('utf-8')).hexdigest()
return encrypted_pass
cemail = input("E: ")
cpassword = input("P: ")
connection = sqlite3.connect('/Users/Air/Desktop/users.db')
connection.create_function('encrypt', 1, encrypt_password)
cursor_v = connection.cursor()
cursor_v.execute("insert into user (cemail, cpassword) values (?,encrypt(?))", (cemail, cpassword))
connection.commit()
cursor_v.close()
When dealing with passwords, there's an absolute ton of information you need to know as a minimum:
Firstly, hashing passwords must be slow, which is the exact opposite of what a hash function is usually intended for.
Use bcrypt or PBKDF2 for this purpose. MD5/SHA1/256/512 are not sufficient for a password and offer no real protection when faced with a GPU-based bruteforcer (such as hashcat).
Second, verification of passwords must be in constant-time, aka they must complete in the same time regardless of the length of a string.
String comparison functions are at risk of a timing attack, and this means that the hash can be deduced by repeatedly giving different inputs to your verification function until enough information about the hash is revealed.
To get around this, use a cryptography library that does this for you.
If you have no other choice, the following pseudocode should help you see how a constant-time algorithm generally works (though you should still use a library!)
def verify(hash, input):
key = secure_random(32)
hash = hmac_sha_512(key, hash)
input = hmac_sha_512(key, bcrypt(input))
results = []
for i, letter in input
results.add(hash[i] === letter)
return false not in results
Thirdly, passwords should have a cryptographically random salt used throughout the hashing. A secure random source of randomness is generally taken from /dev/urandom or a library dedicated to it.
To answer the question of Well how do I verify a password if I can't see the original value? — simple, just hash whatever a user gives you and see if the hash matches the one in the database. But see above for the huge pitfall there.
You cannot retrieve hashed password but compare new password whether it is the same. IF HASH(new_password) == hashed_password_in_db THEN ..ok.. ELSE ..wrong password....

Encrypt with a specific key and uncrypt with another specific key

There are some encrypt method that allow I encrypt a string data with a specific unique encode key (write key) and decode it to an another specific decode key (read key)?
For instance: supposing that we have the string Hello World. So we have two keys, one for encoding (like John) and another to decoding (like Doe). So we create an encoded version of string like:
Note: not limited to PHP, it just an easy example for me...
$string = "Hello World";
$encoded = encode($string, "John", "Doe");
//#function encode(string data, string write_key, string read_key);
//#return string "abcdef123456" -- supposing that it is the encoded string!
Now we have the new encoded string of Hello World data as abcdef123456. Now if we try to read this string with the write key we don't will get the original version, but a strange version or just an error like invalid read key.
$decoded = decode($encoded, "John");
//#function decode(string data, string read_key);
//#return false -- invalid read key! or just a strange result data
$decoded = decode($encoded, "Doe");
//#return string "Hello World"
Note: I just need to know if there are some encrypt project like that, but if you know a library, it's ok too. I need it to my project. I know that is possible hash with a key, but the original result never can back. And I know too read-write key encodings, but it doesn't is really what I need.
You are talking about Public key cryptography.
To suggest a library you should specify the language you will be using in your project.

A way to generate a signature or a hash of an image in ASP.NET for duplicate detection?

I run a rather large site where my members add thousands of images every day. Obviously there is a lot of duplication and i was just wondering if during an upload of an image i can somehow generate a signature or a hash of an image so i can store it. And every time someone uploads the picture i would simply run a check if this signature already exists and fire an error stating that this image already exists. Not sure if this kind of technology already exists for asp.net but i am aware of tineye.com which sort of does it already.
If you think you can help i would appreciate your input.
Kris
A keyword that might be of interest is perceptual hashing.
You use any derived HashAlgorithm to generate a hash from the byte array of the file. Usually MD5 is used, but you could subsitute this for any of those provided in the System.Security.Cryptography namespace. This works for any binary, not just images.
Lots of sites provide MD5 hashes when you download files to verify if you've downloaded the file properly. For instance, an ISO CD/DVD image may be missing bytes when you've received the whole thing. Once you've downloaded the file, you generate the hash for it and make sure it's the same as the site says it should be. If all compares, you've got an exact copy.
I would probably use something similar to this:
public static class Helpers
{
//If you're running .NET 2.0 or lower, remove the 'this' keyword from the
//method signature as 2.0 doesn't support extension methods.
static string GetHashString(this byte[] bytes, HashAlgorithm cryptoProvider)
{
byte[] hash = cryptoProvider.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
Requires:
using System.Security.Cryptography;
Call using:
byte[] bytes = File.ReadAllBytes("FilePath");
string filehash = bytes.GetHashString(new MD5CryptoServiceProvider());
or if you're running in .NET 2.0 or lower:
string filehash = Helpers.GetHashString(File.ReadAllBytes("FilePath"), new MD5CryptoServiceProvider());
If you were to decide to go with a different hashing method instead of MD5 for the miniscule probability of collisions:
string filehash = bytes.GetHashString(new SHA1CryptoServiceProvider());
This way your has method isn't crypto provider specific and if you were to decide you wanted to change which crypto provider you're using, you just inject a different one into the cryptoProvider parameter.
You can use any of the other hashing classes just by changing the service provider you pass in:
string md5Hash = bytes.GetHashString(new MD5CryptoServiceProvider());
string sha1Hash = bytes.GetHashString(new SHA1CryptoServiceProvider());
string sha256Hash = bytes.GetHashString(new SHA256CryptoServiceProvider());
string sha384Hash = bytes.GetHashString(new SHA384CryptoServiceProvider());
string sha512Hash = bytes.GetHashString(new SHA512CryptoServiceProvider());
Typically you'd just use MD5 or similar to create a hash. This isn't guaranteed to be unique though, so I'd recommend you use the hash as a starting point. Identify if the image matches any known hashes you stored, then individually load the ones that it does match and do a full byte comparison on the potential collisions to be sure.
Another, simpler technique though is to simply pick a smallish number of bits and read first part of the image... store that number of starting bits as if they were a hash. This still gives you a small number of potential collisions that you'd need to check, but has much less overhead.
Look in the System.Security.Cryptography namespace. You have your choice of several hashing algorithms/implementations. Here's an example using md5, but since you have a lot of these you might want something bigger like SHA1:
public byte[] HashImage(Stream imageData)
{
return new MD5CryptoServiceProvider().ComputeHash(imageData);
}
I don't know if it already exists or not, but I can't think of a reason you can't do this yourself. Something similar to this will get you a hash of the file.
var fileStream = Request.Files[0].InputStream;//the uploaded file
var hasher = System.Security.Cryptography.HMACMD5();
var theHash = hasher.ComputeHash(fileStream);
System.Security.Cryptography

Effective Password Encryption

I've taken a look at the StackOverflow question, "Password Encryption / Database Layer AES or App Layer AES," and I'd like to effectively and efficiently hash my passwords on registration (web app) and then be able to check they are correct on login. I'm using VB, but comfortable using C#.
I would love to use Jeff Atwood's Encryption class described in ".NET Encryption Simplified" as it's really easy to understand. It has a hashing class—but I have no idea how to "login" and compare hashes after they have been hashed. This is Jeff's demonstration of his hash methods using his Encryption class:
Sub DemoHash()
Dim d As New Encryption.Data( _
"{ts '2004-10-09 08:10:04'}The world is beautiful and needs caring by its children")
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA1)
Dim hash2 As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
Dim hash3 As New Encryption.Hash(Encryption.Hash.Provider.SHA384)
Dim hash4 As New Encryption.Hash(Encryption.Hash.Provider.SHA512)
Dim hash5 As New Encryption.Hash(Encryption.Hash.Provider.MD5)
Dim hash6 As New Encryption.Hash(Encryption.Hash.Provider.CRC32)
hash.Calculate(d)
hash2.Calculate(d)
hash3.Calculate(d)
hash4.Calculate(d)
hash5.Calculate(d)
Console.WriteLine("SHA1: " & hash.Value.Hex)
Console.WriteLine("SHA256: " & hash2.Value.Hex)
Console.WriteLine("SHA384: " & hash3.Value.Hex)
Console.WriteLine("SHA512: " & hash4.Value.Hex)
Console.WriteLine("MD5: " & hash5.Value.Hex)
Console.WriteLine("CRC32: " & hash6.Calculate(d).Hex)
Console.WriteLine()
Dim salt As New Encryption.Data("salty!")
Console.WriteLine("Salted CRC32: " & hash6.Calculate(d, salt).Hex)
Console.WriteLine("Press ENTER to continue...")
Console.ReadLine()
End Sub
So my questions are:
I can encrypt the password (though I have no intention of storing it) and hash a string. If I were to have a user called 'barry' with a password of 'fishlegs', what is the best way to store his password and retrieve it?
In SQL Server; is binary or nvarchar the best option for the storage of the hash?
Based on 'barry' and his password what effectively is the hash storing? Is it an encryption of 'fishlegs' appended to a salt?
Cryptography is hard!
Thanks to anyone who can assist...
Hmm, I think you're just missing some basic concepts related to how hashing works. Let me try to explain briefly. I'm going to start out simple and elaborate on my answer afterwards, so please read through the whole thing, the information at the beginning will not be secure.
What you want to use to store a password is a function known as a "one-way hash". What this means is that, for any input that you feed the function, the same input will always give the same result. However, there is no mathematical process that lets you take that result string and figure out what the original input was.
Let's take MD5 as an example of a hashing function. If I run MD5 on the string "password", I will always get the result "5f4dcc3b5aa765d61d8327deb882cf99". However, if you were to simply give someone that result string ("5f4d..."), it is impossible for them to apply some mathematical process to "reverse" the function and figure out that it came from "password".
What this means is that when a user first sets up their password, you apply a hashing function to it, and store the result. So instead of storing "password", you store "5f4dcc3b5aa765d61d8327deb882cf99". Then, when that user tries to log in, you take whatever they typed into the password box on the login form, and apply the same hashing function. If you get the same result as what's stored in the database, they must have entered the same password as they originally chose, even though you have no idea what that original password actually was.
Now, even though it's impossible to "reverse" a hash function, the fact that the same input always gives the same output means that someone can simply build up a big database of input/output pairs, and use that to effectively reverse hashes. This is called a "rainbow table". There are many of these available on the internet, so it's not safe to use simple hashing, just in case your database ever gets compromised. That is, even though it is mathematically impossible to take "5f4dcc3b5aa765d61d8327deb882cf99" and figure out that it came from running MD5 on "password", it's very easy to determine that in practice. All you have to do is run every word in a dictionary through MD5 and store the results, and you can easily reverse simple passwords.
This is where "salting" comes in. If you generate a random "salt" string for every user and attach that to their password, it effectively ruins rainbow tables. For example, let's say that the same user above registers with their password as "password". We generate a random 8-character salt to attach to their password before hashing it. Let's say that it's "A4BR82QX". Now, instead of hashing "password", we hash "A4BR82QXpassword". This gives the result "87a4ba071c8bcb5efe457e6c4e6c4490", so we store that in the database, along with the salt string. Then when this user tries to log in, instead of directly hashing and comparing the password they entered in the login form, we take what they entered, put "A4BR82QX" in front of it again, and hash that. Just as before, if it matches the stored hash, we know that they entered the right password.
Effectively what you've done here is make it so that pre-generated rainbow tables are useless for trying to crack the passwords in your database. Since the salt is random, and each user has a different one (generally), the attacker will have to re-generate their rainbow tables for every individual user. This is much more difficult.
However, there's one more problem, and that's that generating MD5 hashes is fast. Even though salting like this requires them to re-generate rainbow tables, because of how fast MD5 is, some decently-complete rainbow tables can be created very quickly. So if they just want to crack a high-value account on your site, it's not really a big deal for them to spend some time generating rainbow tables to try and reverse that password. If the high-value account's original password wasn't secure enough by itself, it'll still be found very quickly, even with salting.
So the next step is to find a slow hash function, and use this instead of a fast one like MD5. Having your site take an extra couple of seconds to check a login isn't a big deal at all. But when someone is trying to generate rainbow tables to crack a password, having each entry take several seconds is an absolute killer. I've written enough here, so I'll just finish by linking to this article, which goes into plenty of detail about picking a good, slow hashing function: Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes.
That was a pretty huge answer, if any of that's unclear, please let me know in a comment and I'll edit to elaborate.
OK so a couple of things about Jeff's class first of all. SHA1 and MD5 are deprecated now. CRC32 is not suitable at all for passwords. Secondly you should be salting every hash, preferably with a different salt value. Generally you choose a cryptographically random block of data for this, but at a push you could use the user name. To salt you prefix, or suffix the salt value somewhere in the process. I tend to hash the password, hash the salt, combine the two, then hash again. But you can swap things around it doesn't really matter that much, as long as you are consistent.
So rather than confuse things further with Jeff's class lets do this the classic way.
First off random salt generation.
public static byte[] GetRandomSalt()
{
int minSaltSize = 16;
int maxSaltSize = 32;
Random random = new Random();
int saltSize = random.Next(minSaltSize, maxSaltSize);
saltBytes = new byte[saltSize];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(saltBytes);
return saltBytes;
}
Then hashing
public static byte[] ComputeHash(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
HashAlgorithm hash = new SHA256Managed();
return hash.ComputeHash(plainTextWithSaltBytes);
}
So this will compute a SHA256 hash and return it as a byte array.
If you were salting you'd do something like the following
byte[] passwordHash = ComputeHash(password);
byte[] salt = GetRandomSalt();
byte[] saltHash = ComputeHash(salt);
byte[] hashWithSaltBytes = new byte[hashBytes.Length + saltBytes.Length];
for (int i=0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
for (int i=0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
And then, if you're bored hash it down again, or leave as is.
To turn a byte array into a string, if you don't fancy storing bytes you can use
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
String comparisons are easier than byte comparisons where you have to iterate over each array, up to you. Just remember if you are using random salts you need to store them beside the password.
Take the user password "Secret!".
Generate a random salt of a few bytes "s4L75a1T".
Concat them to "s4L75a1TSecret!".
Calculate the hash "b9797a5b683804eb195b6ba2a5e368ae74394cd3" (this is SHA-1 in hex)
Store the hash and the salt (both as hex string, Base64 string, or whatever you like) in the database together with the user name and the other user information (in the example the salt is just a plain string and the hash is Base64 encoded).
FirstName LastName Salt Hash
-----------------------------------------------------------------
John Doe s4L75a1T uXl6W2g4BOsZW2uipeNornQ5TNM=
If you want to verify the user password, just take the user name, look up the salt, do the above again and see if the calculated hash and the one in the database match. There is no (known) way to (easily) recover the password.
Don't confuse encrypting passwords with hashing them; with a good cryptographic hash function, you should not be able to reverse the hashing process to retrieve the original string.
Chad's answer above is an excellent point-by-point explanation of the concepts involved.
This subject's been done to death all over the Internet; not just on Stack Overflow; seriously - a simple web search ought to find you a pretty comprehensive guide.
Thanks, Jeff, for spreading yet another bunch of misinformation. I suppose we'll see a whole slew of misguided questions about hashing, encryption, etc. over the next week, not to mention the crap that'll come up about floating-point arithmetic.
I believe the process is like this: Generate a random salt, so 'fishlegs' + 'r4nd0mS4lt' to get 'fishlegsr4nd0mS4lt'. Then you hash that, say with MD5 (though you might want to use SHA256 to be more secure) to get: 593d5518d759b4860188158ef4c71f28. Store that and the randomly generated salt. When the user logs in, append the random salt and then check if his entered password with the salt matches the hash in the database.
What you are essentially wanting to do is:
A) On account creation, you get a username and password from the user, you hash those together with your own salt and store the resultant string in your database like:
Dim sUserName = "barry"
Dim sPassWord = "fishlegs"
Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£"
Dim d As New Encryption.Data(mySalt + sUserName + sPassWord)
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
hash.Calculate(d)
Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex;
SavenewPasswordHashToDatabase(sTheSaltedHashedUnPassCombination)
You never store sPassWord.
B) When the user logs in you perform exactly the same operation on the provided username and password then compare the resultant hash to the previously stored value in the database so you use:
Dim sThePreviouslyCreatedHashFromTheDatabase = GetHashForUserFromDatabase(usernameProvided)
Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£"
Dim d As New Encryption.Data(mySalt + usernameProvided+ passwordProvided)
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
hash.Calculate(d)
Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex;
if (sThePreviouslyCreatedHashFromTheDatabase.Equals(sTheSaltedHashedUnPassCombination))
'UN & Password Valid!
else
'UN & PW Invalid!
end
(Pardon any errors, VB aint my language)
To answer your given questions:
1) See above. Never store the password directly, store the hashed value
2) use a char(X), the number of characters returned from the hash is constant so you can give it a known storage size.
3) What you are effectively storing is the password for the user salted with their username and also salted with the constant on your server. This will be a fixed length string and you cannot change this string back into the separate username and password.
The whole point of hashing is that you store the hashes rather than the plaintext passwords and they can't be recovered (at least only with great difficulty). Hash functions are designed to be one-way, so that retrieval is impossible - this is their very essence.
nchar[n] is probably the best option, since common hash algorithms produce results of constant length (SHA1 outputs a 160-bit hash). Common practice is to convert the hash to Base64 so that it can be stored as ASCII text.
A salt is just a method that adds random bits onto a hash in order to complicate the process of cracking. Each extra bit of salt means that a brute-force cracker must take twice as long (and use twice as much memory).
ASP.NET includes a SQL based membership provider that allows you to create, store and verify encryped or hashed passwords, without you having to do anything - as Eric Lippert says:
let me give you all my standard caution about rolling your own cryptographic algorithms and security systems: don't.
You Want Salt With That? Part One: Security vs Obscurity
You Want Salt With That? Part Two: Security vs Obscurity
You Want Salt With That? Part Three: We Need A Hash
You Want Salt With That? Part Four: Challenge-Response

Resources