Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

Βάλτε λίγο "αλάτι" στη ζωή σας και στο hashing...

Γενικά

Hashing είναι η διαδικασία στην οποία από ένα αλφαριθμητικό (γενικά ένα byte stream) παράγουμε ένα άλλα αλφαριθμητικό που ονομάζεται hash value του αρχικού μυνήματος. Η δημιουργία του hash value γίνεται με ειδικούς μαθηματικούς αλγορίθμους που έχουν (εκτός των άλλων) τις ακόλουθες δύο βασικές ιδιότητες:

  • Είναι "πρακτικά αδύνατο" να αναπαραχθεί το αρχικό αλφαριθμητικό από το hash value
  • Δύο διαφορετικά αλφαριθμητικά είναι "πρακτικά αδύνατο" να δώσουν το ίδιο hash value

Με τον όρο "πρακτικά αδύνατο" εννοούμε ότι απαιτείται πολύ χρόνος και μεγάλη υπολογιστική ισχύ για να πετύχουμε τα παραπάνω. Ετσι για παράδειγμα αν έχουμε μια hash value και θέλουμε να μάθουμε το αλφαριθμητικό από το οποίο προήλθε, το μόνο που μπορούμε να κάνουμε είναι να παράγουμε hash values από όλα τα πιθανά αλφαριθμητικά (brute force attack) και να ελέγχουμε αν το παραγόμενο hash value ταιριάζει με αυτό που ψάχνουμε.

Αλγόριθμοι για Hashing

Υπάρχουν αρκετοί τέτοιοι αλγόριθμοι, οι πιο γνωστοί είναι οι:

  • SHA1 (Secure Hash Algorithm) και οι παραλλαγές του SHA256, SHA384, SHA512 κτλ.
  • MD5 (Message Digest)

Καθένας από αυτούς παράγει hash values συγκεκριμένου μεγέθους, για παράδειγμα ο MD5 παράγει 128 bit hashes, o SHA1 160 bit hashes, o SHA1 256 bit hashes κτλ.

Που είναι χρήσιμο το Hashing?

Σε πολλές περιπτώσεις, εδώ θα σχολιάσουμε αυτές που έχουν να κάνουν με αποθήκευση ευαίσθητων δεδομένων. Ισως το πιο αντιπροσωπευτικό παράδειγμα είναι η αποθήκευση passwords. Πράγματι φανταστείται ένα σύστημα στο οποίο θέλουμε να αποθηκεύουμε τα password των χρηστών σε μια βάση δεδομένων και ΔΕΝ θέλουμε να έχουμε την υπηρεσία "I forgot my password". Δηλαδή ΔΕΝ χρειάζετε να έχουμε διαθέσιμο το password κανενός χρήστη σαν clear text. Σε αυτή την περίπτωση δεν πρέπει να σώζουμε τα password με τρόπο που να μπορούν να ΑΝΑΚΤΗΘΟΥΝ. Μια καλή ιδέα λοιπόν είναι να αώζουμε hash values των passwords.

Ετσι αφενώς μπορούμε να κάνουμε έλεγχο αν ένας χρήστης έδωσε σωστό password (ελέγχοντας αν το hash του password που έδωσε είναι ίδιο με το hash που έχουμε αποθηκεύσει) και αφετέρου κανένας (ούτε ο DBA) δεν έχει πρόσβαση στο password κανενός χρήστη (πράγματι ο DBA μπορεί να δεί το hash ενός password αλλά ΔΕΝ μπορεί να ανακτήσει το clear text password).

Το μόνο που μπορεί να κάνεις κάποιος για να ανακτήσει το clear text password ξέροντας ένα hash, είναι το brute force attack που αναφέραμε παραπάνω. Αυτό όμως εν'γένει είναι μια πάρα πολύ χρονοβόρα (και resource consuming) διαδικασία. Πρέπει για κάθε πιθανό αλφαριθμητικό (πάααααρα πολλά σε πλήθος) να υπολογίζουμε το hash και να το συγκρίνουμε με το hash που θέλουμε να κάνουμε attack.

Salted Hash. Τί είναι? Γιατί είναι απαραίτητο σε κάποιες περιπτώσεις?

Τώρα που ανέφερα τη φράση "ευαίσθητα δεδομένα" ήρθαν στο μυαλό μου οι πιστωτικές μου κάρτες (ναι έχω και εγώ πολλές...) και οι cash κάρτες -πολλές πάλι αλλά με λίγο υπόλοιπο :(((.

Κάθε μια από αυτές έχει ένα τετραψήφιο PIN το οποίο, λογικά, φυλάσεται σε κάποια βάση κάποιου τραπεζικού συστήματτος. Προφανώς δεν αποθηκεύεται σαν clear text αλλά σαν hash value. Ωραία, μπορώ να κοιμάμαι ήσυχος, ο DBA ΔΕΝ μπορεί να ξέρει το PIN κάποια κάρτας μου. Χχχχχχχχχχχμμμμμμμμμμ, για μισό λεπτό, το PIN είναι ένα 4ψήφιο νούμερο, που σημαίνει ότι αν θέλω να κάνω brute force attack για να βρώ πιο νούμερο έχει το ίδιο hashed value ενός 4ψήφιου, αρκεί να δοκιμάσω 10000 νούμερα (από το 0000 μέχρι το 9999). Ο source πιθανόχωρος του brute force attack είναι πολύ περιορισμένος (ακόμα και ο Spectrum +2 που είχα κάποτε, θα μπορούσε σε μερικά δευτερόλεπτα να κάνει το attack!!!).

Σε αυτές τις περιπτώσεις (όπου ο πιθανόχωρος των ευαίσθητων δεδομένων είναι από τη φύση του περιορισμένος), χρησιμοποιούμε το λεγόμενο salted hash. Χρησιμοποιούμε ένα επιπλέον αλφαριθμητικό (το salt) και το προσθέτουμε είτε αριστερά είτε δεξιά του "ευαίσθητου δεδομένου" πριν δημιουργήσουμε το hash. Ετσι για παράδειγμα αν το PIN μιας κάρτας είναι το 1234 και το salt είναι το CARDNo τότε θα γινόταν hashed to CARDNo1234 (ή το 1234CARDNo). Ετσι ο πιθανόχωρος για το brute force attack θα ήταν πολύ μεγαλύτερος από τα 10000 νούμερα που αναφέραμε στην προηγούμενη παράγραφο.

Πως παράγω το salt? Πώς το αποθηκεύω?

Το salt παράγεται κατά κανόνα με ευριστικό τρόπο. Συνήθως είναι εξάρτηση άλλων πεδίων του record που περιέχει το "ευαίσθητο δεδομένο" που θέλουμε να αποθηκεύσουμε σαν hash. Για παράδειγμα μπορούμε να χρησιμοποιήσουμε το όνομα του χρήστη (username) σαν salt στο password. Αλλες φορές είναι πάντοτε το ίδιο για όλες τις εγγραφες και είναι αποθηκευμένο κάπου στον κώδικα σαν hardcoded τιμή, ενώ αλλές φορές παράγεται τυχαία από κάποιον random generator.

Οπως και να το έχουμε δημιουργήσει πρέπει να το αποθηκεύουμε (ή να το παράγουμε) με ασφαλή τρόπο ώστε να μην μπορεί να το βρεί κανείς εύκολα. Αν το salt γίνει compromised τότε μπορεί να έχουμε τα προβλήματα που αναφέραμε στην παραπάνω παράγραφο.

Στην ακόλουθη παράγραφο παραθέτω τον κώδικά για hashing και salted hashing. Το πού θα αποθηκεύσεται το salt.... δικό σας πρόβλημα :)))

Κώδικας για hashing και salted hashing (έτσι για να μην ξεχνιώμαστε...)


using
System;
using System.Security.Cryptography;
using System.Text;

namespace Hashing
{

    public enum HashAlgorithms
    {
        SHA1,
        SHA256,
        SHA384,
        SHA512,
        MD5
    }

    public class HashingUtils
    {      
        public static string ComputeHash(string plainText, HashAlgorithms hashAlgorithm,
            string salt)
        {
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
            byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + saltBytes.Length];
           
            for (int j = 0; j < plainTextBytes.Length; j++)
            {
                plainTextWithSaltBytes[j] = plainTextBytes[j];
            }

            for ( int j = 0; j < saltBytes.Length; j++)
            {
                plainTextWithSaltBytes[plainTextBytes.Length + j] = saltBytes[j];
            }

            HashAlgorithm hash = null;
            switch ( hashAlgorithm )
            {
                case HashAlgorithms.SHA1 :
                    hash = new SHA1Managed();
                    break;
                case HashAlgorithms.SHA256  :
                    hash = new SHA256Managed();
                    break;
                case HashAlgorithms.SHA384 :
                    hash = new SHA384Managed();
                    break;
                case HashAlgorithms.SHA512 :
                    hash = new SHA512Managed();
                    break;
                case HashAlgorithms.MD5 :
                    hash = new MD5CryptoServiceProvider();
                    break;
            }
         
            byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);

            return Convert.ToBase64String(hashBytes);
        }

      
        public static bool VerifyHash(string plainText, HashAlgorithms hashAlgorithm,
            string hashValue, string salt)
        {
            byte[] hashBytes = Convert.FromBase64String(hashValue);
            byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
           
         
            string expectedHashString = ComputeHash(plainText, hashAlgorithm, salt);
            return (hashValue == expectedHashString);
        }
    }
}

Χρησιμοποιούμε την κλάση κάπως έτσι:

static void Main(string[] args)
        {
            string plainText = "myPIN" ;
            string salt = "mySalt";

            string hashValueInBase64Encoding = Hashing.HashingUtils.ComputeHash(plainText,
                Hashing.HashAlgorithms.SHA512, salt);

            Console.Write("This is the hashed value: " + hashValueInBase64Encoding);
          
            bool verified = Hashing.HashingUtils.VerifyHash(plainText,
                Hashing.HashAlgorithms.SHA512, hashValueInBase64Encoding, salt);

            Console.WriteLine(verified);
            Console.ReadLine(); 
        }
Έχουν δημοσιευτεί Κυριακή, 17 Ιουνίου 2007 1:30 μμ από το μέλος Nick Makris
Δημοσίευση στην κατηγορία: ,

Ενημέρωση για Σχόλια

Αν θα θέλατε να λαμβάνετε ένα e-mail όταν γίνονται ανανεώσεις στο περιεχόμενο αυτής της δημοσίευσης, παρακαλούμε γίνετε συνδρομητής εδώ

Παραμείνετε ενήμεροι στα τελευταία σχόλια με την χρήση του αγαπημένου σας RSS Aggregator και συνδρομή στη Τροφοδοσία RSS με σχόλια

Σχόλια:

Χωρίς Σχόλια

Ποιά είναι η άποψή σας για την παραπάνω δημοσίευση;

(απαιτούμενο)
απαιτούμενο
(απαιτούμενο)
ÅéóÜãåôå ôïí êùäéêü:
CAPTCHA Image