Τρέχουσα κατάσταση: PHP2Shell. Δύο κρίσιμες ευπάθειες της PHP (CVSS 9.2) επιτρέπουν κακόβουλο κώδικα χωρίς σύνδεση. Patches από τις 21/10/2026.Τι πρέπει να γίνει τώρα →

Ασφαλής αποθήκευση κωδικών στην PHP: σωστή χρήση του password_hash

Πώς αποθηκεύουν οι προγραμματιστές κωδικούς στην PHP με τα password_hash και password_verify, πώς επιλέγουν την κατάλληλη μέθοδο και πώς αντικαθιστούν παλιά hashes MD5 ή SHA-1 χωρίς επαναφορά κωδικού για όλους τους χρήστες.

Ημερομηνία 6 λεπτά ανάγνωσηςΑσφαλής ανάπτυξη

Εν ολίγοις: Τους κωδικούς τους αποθηκεύετε στην PHP με το password_hash() και τους ελέγχετε με το password_verify(). Η προεπιλογή PASSWORD_DEFAULT χρησιμοποιεί προς το παρόν bcrypt, από την PHP 8.4 με συντελεστή κόστους 12· όπου η PHP έχει μεταγλωττιστεί με Argon2, το PASSWORD_ARGON2ID είναι μια καλή εναλλακτική. Τα παλιά hashes MD5 ή SHA-1 τα μετατρέπετε ενσωματώνοντάς τα αμέσως μέσα στο password_hash και αντικαθιστώντας τα στην επόμενη σύνδεση με ένα άμεσο hash.

Τι σχέση έχει μια διαρροή της βάσης δεδομένων με τα hashes σας

Μετά από εισβολή σε διακομιστή ιστού, η βάση δεδομένων είναι ένας από τους πρώτους στόχους. Όποιος μπορεί, όπως στο PHP2Shell, να εκτελέσει κώδικα στον διακομιστή, διαβάζει και τα στοιχεία πρόσβασης της εφαρμογής και μαζί τον πίνακα με τους λογαριασμούς χρηστών. Το αν οι κωδικοί εκεί θα γίνουν αναγνώσιμοι μετά από ώρες ή μετά από χρόνια, δεν κρίνεται την ημέρα της επίθεσης, αλλά κατά τον προγραμματισμό.

Εκπληκτικά πολλές εφαρμογές αποθηκεύουν ακόμη κωδικούς με μεθόδους που δεν σχεδιάστηκαν ποτέ γι' αυτόν τον σκοπό: MD5, SHA-1, ένα απλό SHA-256 ή ακόμη και αντιστρέψιμα κρυπτογραφημένους. Αυτές οι μέθοδοι είναι γρήγορες, και αυτό ακριβώς είναι το πρόβλημα. Οι επιτιθέμενοι μπορούν να δοκιμάσουν με αυτές πάρα πολλούς υποψήφιους κωδικούς ανά δευτερόλεπτο.

Η PHP διαθέτει εδώ και καιρό μια λύση που υπολογίζει αργά, δημιουργεί αυτόματα salt για κάθε κωδικό και αποθηκεύει τη μέθοδο και τις παραμέτρους μέσα στο ίδιο το hash. Αρκεί να τη χρησιμοποιείτε με συνέπεια.

password_hash στην PHP: bcrypt ή Argon2id;

Το password_hash() γνωρίζει περισσότερες μεθόδους. Η επιλογή σύμφωνα με το εγχειρίδιο της PHP:

Σταθερά Μέθοδος Υπόδειξη
PASSWORD_DEFAULT προς το παρόν bcrypt Προεπιλογή, μπορεί να αλλάξει σε μελλοντικές εκδόσεις
PASSWORD_BCRYPT bcrypt Συντελεστής κόστους από την PHP 8.4 εξ ορισμού 12, πριν 10· μετρούν μόνο τα πρώτα 72 byte
PASSWORD_ARGON2I Argon2i από την PHP 7.2, μόνο αν η PHP έχει χτιστεί με υποστήριξη Argon2
PASSWORD_ARGON2ID Argon2id από την PHP 7.3, ίδια προϋπόθεση

Το OWASP συνιστά το Argon2id ως πρώτη επιλογή, με τουλάχιστον 19 MiB μνήμης, δύο περάσματα και ένα νήμα, και το bcrypt κυρίως για υπάρχοντα συστήματα. Για τις περισσότερες εφαρμογές PHP η διαφορά στην πράξη είναι μικρότερη από τη διαφορά ανάμεσα σε οποιαδήποτε από αυτές τις μεθόδους και το MD5. Σημασία έχει να χρησιμοποιείτε έστω μία από αυτές και να προσαρμόζετε τακτικά τις παραμέτρους.

Αν το Argon2id είναι διαθέσιμο, το δείχνει ένας σύντομος έλεγχος:

<?php
var_dump(defined('PASSWORD_ARGON2ID'));
var_dump(password_algos());

Αποθήκευση και έλεγχος: ένα πλήρες πρότυπο

Το παρακάτω παράδειγμα δείχνει εγγραφή και σύνδεση με αυτόματη ενημέρωση παλιών hashes. Ο έλεγχος μήκους προστατεύει από το όριο των 72 byte του bcrypt:

<?php
declare(strict_types=1);

const PASSWORT_ALGO = PASSWORD_DEFAULT;

function passwortSpeichern(string $passwort): string
{
    if (strlen($passwort) > 72) {
        throw new InvalidArgumentException('Passwort zu lang für das gewählte Verfahren.');
    }
    return password_hash($passwort, PASSWORT_ALGO);
}

function passwortPruefen(string $eingabe, string $gespeicherterHash, callable $hashAktualisieren): bool
{
    if (!password_verify($eingabe, $gespeicherterHash)) {
        return false;
    }
    if (password_needs_rehash($gespeicherterHash, PASSWORT_ALGO)) {
        $hashAktualisieren(password_hash($eingabe, PASSWORT_ALGO));
    }
    return true;
}

Το strlen() μετρά byte και όχι χαρακτήρες. Αυτό εδώ είναι επιθυμητό, γιατί το όριο του bcrypt αναφέρεται σε byte. Η στήλη στη βάση δεδομένων πρέπει σύμφωνα με το εγχειρίδιο να χωράει περισσότερους από 60 χαρακτήρες, το 255 είναι καλή επιλογή. Μην ορίζετε ποτέ δικό σας salt: η επιλογή είναι παρωχημένη και αγνοείται από την PHP 8.0.

Με τη μετάβαση στην PHP 8.4 ο προεπιλεγμένος συντελεστής κόστους για το bcrypt ανεβαίνει από 10 σε 12. Τα υπάρχοντα hashes ανανεώνονται αυτόματα στην επόμενη σύνδεση χάρη στο password_needs_rehash(). Σε συστήματα με μεγάλο φορτίο αξίζει πριν από τη μετάβαση ένα σύντομο τεστ φορτίου της σύνδεσης.

Συμβουλή

Μετρήστε τον συντελεστή κόστους στον παραγωγικό σας διακομιστή. Ένας μεμονωμένος έλεγχος πρέπει να κοστίζει αισθητό χρόνο υπολογισμού, αλλά να μένει σαφώς κάτω από ένα δευτερόλεπτο, ώστε οι συνδέσεις υπό φορτίο να μην γίνουν σημείο συμφόρησης.

Αντικατάσταση παλιών hashes MD5 και SHA-1

Η πραγματική πρόκληση είναι συνήθως το υπάρχον απόθεμα. Τους ίδιους τους κωδικούς δεν τους γνωρίζετε, άρα δεν μπορείτε απλώς να τους κάνετε ξανά hash. Αν περιμένετε να συνδεθεί ξανά κάθε χρήστης, οι ανενεργοί λογαριασμοί μένουν απροστάτευτοι. Το OWASP περιγράφει γι' αυτό μια διαδικασία δύο σταδίων:

  1. Άμεση ενσωμάτωση: Τυλίξτε κάθε υπάρχουσα τιμή MD5 ή SHA-1 με σενάριο μέσα στο password_hash() και αποθηκεύστε την με σήμανση, για παράδειγμα σε στήλη hash_typ.
  2. Έλεγχος κατά τη σύνδεση: Για σημασμένους λογαριασμούς υπολογίστε πρώτα την εισαγωγή με την παλιά μέθοδο και ελέγξτε την έπειτα με το password_verify() απέναντι στην τυλιγμένη τιμή.
  3. Άμεση αντικατάσταση: Μετά από επιτυχή σύνδεση δημιουργήστε ένα κανονικό hash από το απλό κείμενο και αφαιρέστε τη σήμανση.
  4. Καθαρισμός του υπολοίπου: Λογαριασμοί που μετά από προκαθορισμένη προθεσμία έχουν ακόμη τυλιγμένα hashes, τίθενται σε επαναφορά κωδικού.
<?php
// Στάδιο 1 (εφάπαξ με σενάριο): $neu = password_hash($alterMd5Wert, PASSWORD_DEFAULT);
// Στάδια 2 και 3 κατά τη σύνδεση:
if ($konto['hash_typ'] === 'md5-umhuellt') {
    $ok = password_verify(md5($eingabe), $konto['passwort_hash']);
    if ($ok) {
        $speichere(password_hash($eingabe, PASSWORD_DEFAULT), 'direkt');
    }
}

Τα τυλιγμένα hashes είναι καλύτερα από γυμνές τιμές MD5, αλλά ασθενέστερα από τα άμεσα hashes. Είναι μεταβατική λύση και όχι στόχος. Μετά από ένα περιστατικό στο οποίο μπορεί να επηρεάστηκε η βάση δεδομένων, αυτή η μετάβαση δεν αντικαθιστά το αίτημα προς τους χρήστες να αλλάξουν τους κωδικούς τους. Ποια ίχνη υποδεικνύουν ένα τέτοιο περιστατικό, περιγράφει το Είμαι ήδη παραβιασμένος;.

Pepper, κρυπτογράφηση και άλλοι λανθασμένοι δρόμοι

Ορισμένες ιδέες ακούγονται σαν περισσότερη ασφάλεια, προσφέρουν όμως λίγα ή βλάπτουν:

  • Δικές σας κατασκευές hash, όπως πολλαπλό SHA-256 με salt που δημιουργείτε μόνοι σας, είναι ταχύτερες από το bcrypt και γι' αυτό ασθενέστερες.
  • Οι κρυπτογραφημένοι κωδικοί είναι αντιστρέψιμοι. Αν το κλειδί βρίσκεται στον ίδιο διακομιστή, η κρυπτογράφηση προστατεύει ελάχιστα μετά από εισβολή.
  • Ένα pepper, δηλαδή μια μυστική πρόσθετη τιμή για όλους τους κωδικούς, μπορεί να αποτελέσει ένα επιπλέον επίπεδο αν φυλάσσεται χωριστά από τη βάση δεδομένων. Δεν αντικαθιστά όμως μια καλή μέθοδο hash και δυσκολεύει μελλοντικές αλλαγές.
  • Προ-hashing πριν από το bcrypt, για να παρακαμφθεί το όριο των 72 byte, ενέχει κατά το OWASP δικούς του κινδύνους. Ένα σαφές όριο μήκους ή το Argon2id είναι απλούστερο.

Και στους κανόνες για νέους κωδικούς αξίζει μια ματιά στην τρέχουσα τεχνολογική στάθμη: το μήκος προστατεύει περισσότερο από περίπλοκες απαιτήσεις για ειδικούς χαρακτήρες και οι πολύ συνηθισμένοι κωδικοί ή κωδικοί που έχουν ήδη εμφανιστεί σε διαρροές δεδομένων πρέπει να απορρίπτονται. Οι αναγκαστικές αλλαγές σε σταθερά διαστήματα χωρίς αφορμή οδηγούν κατά την εμπειρία μάλλον σε προβλέψιμα μοτίβα παρά σε ισχυρότερους κωδικούς.

Για συστήματα σύνδεσης οι κωδικοί δεν αρκούν ποτέ μόνοι τους: μέθοδοι δύο παραγόντων για διαχειριστές και ένα φρένο απέναντι σε μαζικές απόπειρες σύνδεσης συμπληρώνουν την αποθήκευση. Και τα δύο πραγματεύεται ο οδηγός Προστασία των συνδέσεων διαχειριστή από brute force. Για συνδέσμους επαναφοράς και κωδικούς μίας χρήσης ισχύουν άλλοι κανόνες, δείτε Ασφαλείς τυχαίοι αριθμοί και tokens στην PHP. Μια επισκόπηση περαιτέρω λειτουργιών παρέχουν οι Ενσωματωμένες λειτουργίες ασφαλείας της PHP.

Πώς βοηθά συγκεκριμένα η PHP SOS;

  • Ελέγχουμε πώς αποθηκεύουν τους κωδικούς η εφαρμογή και τα πρόσθετά σας και εντοπίζουμε παρωχημένες μεθόδους και σε δευτερεύουσες λειτουργίες.
  • Σχεδιάζουμε τη μετάβαση του υπάρχοντος αποθέματος με ενσωμάτωση, μετάβαση κατά τη σύνδεση και προθεσμία για τους υπόλοιπους λογαριασμούς.
  • Προσαρμόζουμε τον συντελεστή κόστους ή τις παραμέτρους Argon2 στους διακομιστές σας.
  • Μετά από ένα περιστατικό σας υποστηρίζουμε στην απόφαση για επαναφορές κωδικών και στην τεχνική υλοποίησή τους.

Έναν έλεγχο του δικού σας λογισμικού PHP προσφέρουμε ως έλεγχο ασφαλείας και δοκιμή διείσδυσης.

Συχνές ερωτήσεις

Ποιον αλγόριθμο χρησιμοποιεί το password_hash με το PASSWORD_DEFAULT;

Προς το παρόν bcrypt. Το εγχειρίδιο της PHP επισημαίνει ότι η προεπιλογή μπορεί να αλλάξει όταν προστεθούν ισχυρότερες μέθοδοι. Γι' αυτό η στήλη της βάσης δεδομένων πρέπει να χωράει περισσότερους από 60 χαρακτήρες· το εγχειρίδιο αναφέρει το 255 ως καλή επιλογή.

Πρέπει να κρυπτογραφώ τους κωδικούς αντί να τους κάνω hash;

Όχι. Οι κρυπτογραφημένοι κωδικοί μπορούν να διαβαστούν ξανά με το κλειδί, και αυτό το κλειδί βρίσκεται συνήθως στον ίδιο διακομιστή. Το hash κωδικού είναι μονόδρομος: η εφαρμογή μπορεί μόνο να ελέγξει αν μια εισαγωγή ταιριάζει.

Πόσο υψηλός πρέπει να είναι ο συντελεστής κόστους στο bcrypt;

Από την PHP 8.4 η προεπιλογή είναι 12, πριν ήταν 10. Το OWASP αναφέρει το 10 ως κατώτατο όριο και συνιστά να ανεβείτε όσο αντέχουν οι διακομιστές σας. Μετρήστε στο δικό σας υλικό πόσο διαρκεί ένας έλεγχος.

Τι σημαίνει το όριο των 72 byte στο bcrypt;

Το bcrypt λαμβάνει υπόψη μόνο τα πρώτα 72 byte ενός κωδικού, ό,τι ακολουθεί κόβεται. Σε χαρακτήρες με τόνους ή μη λατινικούς χαρακτήρες ένας χαρακτήρας μετρά συχνά δύο byte ή περισσότερα. Γι' αυτό περιορίστε ρητά το μήκος ή χρησιμοποιήστε Argon2id, αν η PHP σας έχει μεταγλωττιστεί με αυτό.

Πρέπει οι χρήστες να αλλάξουν τους κωδικούς τους μετά από μια εισβολή;

Αν δεν μπορεί να αποκλειστεί ότι η βάση δεδομένων διέρρευσε, πρέπει να το ζητήσετε. Τα καλά hashes επιβραδύνουν σημαντικά το σπάσιμο, οι αδύναμοι κωδικοί όμως παραμένουν ευάλωτοι. Αν υπάρχει επιπλέον υποχρέωση αναφοράς, το διευκρινίζετε με τον υπεύθυνο προστασίας δεδομένων σας.

Πηγές

Άμεση επικοινωνία

Κάθε ώρα μετράει. Γράψτε μας τώρα.

Περιγράψτε σύντομα το σύστημα, την έκδοση PHP και ό,τι παρατηρείτε. Θα επικοινωνήσουμε μαζί σας, θα διευκρινίσουμε την κατάσταση και θα εργαστούμε εξ αποστάσεως μέχρι το περιστατικό να κλείσει και να τεκμηριωθεί.

Άμεση βοήθεια