C# - Jelszó tárolása MS SQL adattáblába titkosítva

forráskód letöltése
Felhasználói adatokat tároló táblánk oszlopai között az esetek többségében találunk egy jelszavat tároló oszlopot. Ha ide csak egyszerűen szövegként beírjuk az adatokat, akkor aki hozzáfér a táblához, az hozzáfér az összes felhasználónk jelszavához is. A nagyobb adatbiztonság érdekében célszerű a jelszavakat titkosítva tárolni. Mellékelt példánkban az MD5 titkosítási algoritmust használjuk fel ehhez.
A mellékelt példa használatához szükséges a SecretPassword adatbázis. Ennek létrehozásához futtassa le a SecretPassword.sql állományt.
Az MD5 titkosítás 128 bites kulcsot használ és további előnye, hogy nem az adat kerül titkosításra, hanem egy abból képzett számhalmaz, mely mindig 16 bájt hosszú, ami viszont egyedileg azonosít bármilyen adatot. Ebből következik, hogy ha a jelszavakat így tároljuk, akkor visszafejteni nem lehet azokat. Ezért ha felhasználónk elfelejti jelszavát, akkor csak az a lehetőség áll fenn, hogy generálunk programból egy újat, majd ezt használva be tud lépni és tetszőlegesen új jelszavat megadni magának.
Amikor bejelentkezik a felhasználó, akkor az általa megadott jelszavat ismét kódolnunk kell és kapott 16 bájtot össze kell hasonlítanunk a már tárolt értékkel. Ha e két adatsor megegyezik, akkor a felhasználó helyesen adta meg jelszavát.
Nézzük mindezt a gyakorlatban. Készítünk egy függvényt StringToMD5 néven, mely egy tetszőleges sztringből előállítja a szükséges 16 bájtos kódot, melyet egy bájt tömbben ad visszatérési értékként.
Az MD5 kódoláshoz az MD5CryptoServiceProvider osztályt kell felhasználnunk.
    private byte[] StringToMD5(string s)
    {
      MD5CryptoServiceProvider csp = new MD5CryptoServiceProvider();
      UTF8Encoding e = new UTF8Encoding();
      byte[] b = csp.ComputeHash(e.GetBytes(s));
      return b;
    }
Új felhasználó regisztrációjakor a hagyományos módon járhatunk el az új adatsor felviteléhez, annyi különbséggel, hogy a jelszavat tároló oszlopnak a StringToMD5 függvényen keresztül adunk értéket.
    private void button1_Click(object sender, System.EventArgs e)
    {           
      SqlConnection connection = new SqlConnection(connStr);
      SqlCommand c = new SqlCommand("InsertNewUser", connection);
      c.CommandType = CommandType.StoredProcedure;
      c.Parameters.Add("@UserNameValue", SqlDbType.VarChar, 50).Value = textBox1.Text;
      c.Parameters.Add("@PasswordValue", SqlDbType.Binary, 16).Value = StringToMD5(textBox2.Text);
      connection.Open();
      c.ExecuteNonQuery();
      ...
    }
Amikor a felhasználónk bejelentkezik, akkor ismét a StringToMD5 függvényt hívjuk meg, hogy a bejelentkezéskor megadott jelszavából előállítsuk a szükséges kódot. A másik kód már tárolva van az adattáblába, így ezt egy lekérdezéssel tudhatjuk meg.
    private void button2_Click(object sender, System.EventArgs e)
    {
      SqlConnection connection = new SqlConnection(connStr);
      SqlCommand c = new SqlCommand("Login", connection);
      c.CommandType = CommandType.StoredProcedure;
      c.Parameters.Add("@UserNameValue", SqlDbType.VarChar, 50).Value = textBox3.Text;
      connection.Open();
      byte[] password = (byte[])c.ExecuteScalar();
      connection.Close();
A rendelkezésre álló két kódot most már csak össze kell hasonlítanunk. Ehhez készítettünk egy ComparePassword függvényt.
      if (ComparePassword(password, StringToMD5(textBox4.Text)))
      {
        label1.Text = "A jelszó megfelelő!";
      }
      else
      {
        label1.Text = "Nem megfelelő a jelszó!";
      }
    }
A ComparePassword függvény paraméterként két bájt tömböt vár. Ezeket bájtonként összehasonlítja. Ha egyezik, akkor igaz értékkel tér vissza. Ekkor biztosak lehetünk abban, hogy a bejelentkezni kívánó felhasználó helyes jelszót adott meg.
    private bool ComparePassword(byte[] b1, byte[] b2)
    {
      bool result = true;
      if (b1.Length != b2.Length)
      {
        result = false;
      }
      else
      {
        for (int i=0; i<b1.Length; i++)
        {
          if (b1[i] != b2[i])
          {
            result = false;
            break;
          }
        }
      }
      return result;
    }