PHP-salausmenetelmät salasanoille ja muille arkaluonteisille tiedoille

osallistuin äskettäin Laracon EU 2018: aan, jossa Marcus Bointon piti suuren puheen salauksesta PHP 7.2: ssa. Jätin puheen, jossa on paljon suurempi arvostus siitä, miten valtavasti monimutkainen salaus on, mutta myös siitä, miten PHP tekee salauksen helpommin natriumin käyttöönoton ansiosta. Tietojen salaus PHP on jotain Olen työskennellyt osana työni SpinupWP joten ajattelin, että oli aika jakaa muutamia oivalluksia. Turvavyöt kiinni, tästä voi tulla kuoppainen kyyti!

Salaustyypit

nykyään on käytössä useita erilaisia salausmenetelmiä, joista yleisimpiä ovat tiivistys, salaisen avaimen salaus ja julkisen avaimen salaus. Lisäksi jokaisessa salausmenetelmässä on useita algoritmeja tai salakirjoituksia, joista valita (jokaisella on omat vahvuutensa ja heikkoutensa). Tässä artikkelissa aiomme keskittyä toteuttamaan hajautus ja salaisen avaimen salaus.

tiivistys

tiivistysalgoritmi ottaa syöttöarvon ja muuttaa sen sanoman digestiksi. Pähkinänkuoressa, selkotekstin arvot muunnetaan kiinteäpituisiksi hash, ja voidaan validoida vain siirtämällä alkuperäinen arvo tiivistysalgoritmille. Tämä tekee hashing täydellinen tallentamiseen käyttäjän salasanoja.

on syytä huomata, että tiivistys ei ole luodinkestävä ratkaisu eivätkä kaikki tiivistysalgoritmit ole tasa-arvoisia. Harkitse MD5 ja SHA1, jotka ovat nopeita ja tehokkaita, joten ne ovat ihanteellisia tarkistussummaukseen ja tiedostojen todentamiseen. Niiden nopeus tekee niistä kuitenkin sopimattomia käyttäjän salasanan tiivistämiseen. Nykypäivän laskentateholla modernin GPU, salasana voidaan murtaa raa ’ alla voimalla muutamassa minuutissa, paljastaen alkuperäisen plaintext salasana. Sen sijaan tulisi käyttää tarkoituksellisesti hitaampia tiivistysalgoritmeja, kuten bcrypt tai Argon2.

vaikka minkä tahansa algoritmin luoma hajautettu salasana varmasti hämärtää alkuperäisen tiedon ja hidastaa mahdollisia hyökkääjiä, meidän kehittäjinä tulisi pyrkiä käyttämään vahvinta käytettävissä olevaa algoritmia. Onneksi php tekee tästä helppoa password_hash()ansiosta.

$hash = password_hash($password, PASSWORD_DEFAULT);

password_hash() funktio ei ainoastaan käytä turvallista yksisuuntaista tiivistysalgoritmia, vaan se käsittelee automaattisesti suolaa ja estää aikaan perustuvia sivukanavahyökkäyksiä. PHP 5.5: stä lähtien bcryptiä käytetään hajautuksen luomiseen, mutta tämä muuttuu tulevaisuudessa, kun PHP: hen lisätään uudempia ja turvallisempia hajautusalgoritmeja. Argon2: sta tulee todennäköisesti seuraava oletusarvoinen tiivistysalgoritmi ja sitä voidaan käyttää nykyään (PHP 7.2: ssa) ohittamalla PASSWORD_ARGON2I – lippu PASSWORD_DEFAULT: n sijaan.

käyttäjän salasanan tarkistaminen on myös triviaali prosessi password_verify() – funktion ansiosta. Syötä vain käyttäjän toimittama teksti-salasana ja vertaa sitä tallennettuun hasikseen, kuten näin:

if (password_verify($password, $hash)) { echo "Let me in, I'm genuine!";}

huomaa, miten salasanan vahvistus suoritetaan PHP: ssä. Jos tallennat käyttäjän tunnistetiedot tietokantaan, saatat olla taipuvainen hävittämään sisäänkirjautumisen yhteydessä syötetyn salasanan ja sitten suorittamaan tietokantakyselyn, kuten näin:

SELECT * FROM usersWHERE username = 'Ashley'AND password = 'password_hash'LIMIT 1;

tämä lähestymistapa on altis sivukanavahyökkäyksille, ja sitä tulisi välttää. Sen sijaan, palauta käyttäjä ja tarkista salasana hash PHP.

SELECT username, password FROM usersWHERE username = 'Ashley'LIMIT 1;

vaikka tiivistys on erinomainen käyttäjän salasanan tallentamiseen, se ei toimi mielivaltaisille tiedoille, joita sovelluksemme tarvitsee käyttää ilman käyttäjän väliintuloa. Mietitäänpä laskutussovellusta, joka salaa käyttäjän luottokorttitiedot. Joka kuukausi sovelluksemme tulee laskuttaa käyttäjää edellisen kuukauden käytöstä. Luottokorttitietojen tiivistäminen ei toimi, koska se edellyttää, että sovelluksemme tietää alkuperäiset tiedot sille hakea sen selkotekstinä.

salaisen avaimen salaus pelastukseen!

Secret Key Encryption

Secret key encryption (tai symmetric encryption as it ’ s also known) käyttää yhtä avainta sekä datan salaamiseen että salauksen purkamiseen. Katsotaanpa, miten toteutamme tällaisen mekanismin käyttämällä natriumia, joka otettiin käyttöön PHP 7.2: ssa. Jos käytät PHP: n vanhempaa versiota, voit asentaa natriumin PECL: n kautta.

ensin tarvitaan salausavain, joka voidaan luoda random_bytes() – funktion avulla. Yleensä teet tämän vain kerran ja tallennat sen ympäristömuuttujana. Muista, että tämä avain on pidettävä salassa hinnalla millä hyvänsä. Kun avain on murrettu, samoin salatut tiedot.

$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);

salataksemme arvon annamme sen sodium_crypto_secretbox() meidän $key ja A $nonce. Nonce luodaan käyttämällä random_bytes(), koska samaa nonssia ei saa koskaan käyttää uudelleen.

$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);$ciphertext = sodium_crypto_secretbox('This is a secret!', $nonce, $key);

tämä aiheuttaa ongelman, koska tarvitsemme noncen purkamaan arvon myöhemmin. Onneksi, nonces ei tarvitse pitää salassa, jotta voimme ennalta sen meidän $ciphertext sitten base64_encode() arvo ennen tallentamista tietokantaan.

$encoded = base64_encode($nonce . $ciphertext);var_dump($encoded);// string 'v6KhzRACVfUCyJKCGQF4VNoPXYfeFY+/pyRZcixz4x/0jLJOo+RbeGBTiZudMLEO7aRvg44HRecC' (length=76)

arvon purkamisessa toimitaan päinvastoin.

$decoded = base64_decode($encoded);

koska tiedämme Nonin pituuden, voimme purkaa sen käyttämällä mb_substr() ennen arvon purkamista.

$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);var_dump($plaintext);// string 'This is a secret!' (length=17)

that ’ s all there is to secret key salaus in PHP, thanks to Sodium!

kirjekuoren salaus

vaikka edellä kuvattu lähestymistapa on varmasti askel oikeaan suuntaan, se jättää silti tietomme haavoittuvaisiksi, jos salainen avain vaarantuu. Tarkastellaan haitallista käyttäjää, joka saa pääsyn palvelimelle, joka isännöi sovellustamme. Tässä skenaariossa hyökkääjä voi löytää salaisen avaimemme, jota käytimme tietojen salaamiseen. Tämä jättää tietomme täysin alttiiksi.

yksinkertainen ratkaisu on olla tallentamatta salaista avainta samaan paikkaan kuin salattua dataa, mutta tämä aiheuttaa ongelman. Miten salaamme ja puramme salauksen tarvittaessa? Syötä Google Cloud Key Management Service (Cloud KMS).

Cloud KMS on Googlen tarjoama palvelu salausavainten turvalliseen ylläpitoon. Se tarjoaa erilaisia hyödyllisiä ominaisuuksia noin avain varastointi, kuten automaattinen avain kierto ja viivästynyt avain tuhoaminen. Tässä esimerkissä keskitymme kuitenkin ensisijaisesti salaisen avaimen tallentamiseen pois tiedoistamme.

turvataksemme asiat käytämme tekniikkaa, joka tunnetaan kirjekuoren salauksena. Pohjimmiltaan kirjekuoren salaukseen kuuluu salausavainten salaaminen toisella avaimella. Teemme tämän kahdesta syystä:

  1. Cloud KMS: n salattavien ja purettavien tietojen kokoraja on 64 KiB. Siksi ei välttämättä ole mahdollista lähettää kaikkia tietoja kerralla.
  2. vielä tärkeämpää on, että emme halua lähettää arkaluonteisia tekstitietojamme kolmannelle osapuolelle riippumatta siitä, kuinka luotettavilta ne vaikuttavat.

sen sijaan, että lähettäisimme tekstitietomme Cloud KMS: ään, luomme ainutlaatuisen salausavaimen joka kerta, kun kirjoitamme arkaluonteisia tietoja tietokantaan. Tätä avainta kutsutaan tietojen salausavaimeksi (dek), jota käytetään tietojemme salaamiseen. Tämän jälkeen dek lähetetään Cloud KMS-palveluun salattavaksi, joka palauttaa avaimen salausavaimen (tunnetaan nimellä KEK). Lopuksi KEK tallennetaan rinnakkain tietokantaan salatun datan viereen ja DEK tuhotaan. Prosessi näyttää siltä:

  1. Luo ainutlaatuinen salausavain (DEK)
  2. salaa tiedot salaisen avaimen salauksella
  3. lähetä ainutlaatuinen salausavain (DEK) Cloud KMS: ään salausta varten, joka palauttaa KEK
  4. säilytä salattu tieto ja salattu avain (KEK) side-by-side
  5. tuhoa luotu avain (DEK)

kun salausta puretaan, prosessi peruutetaan:

  1. Hae salattu tieto ja salattu avain (KEK) tietokannasta
  2. lähetä KEK Cloud KMS: ään salauksen purkamista varten, joka palauttaa dek
  3. käytä DEK: ia salatun tiedon purkamiseen
  4. tuhoa DEK

tätä silmällä pitäen olen luonut hyvin yksinkertainen helper-luokka kirjekuoren salauksen suorittamiseen. En aio käydä läpi Google Cloud-konsolin edellyttämiä vaiheita, sillä Quickstart-ja Todennusoppaat hahmottelevat kaiken, mitä tarvitset aloittaaksesi. Lyhyyden vuoksi ei ole virheenkäsittelyä jne. tässä esimerkissä.

<?phpuse Google_Service_CloudKMS as Kms;use Google_Service_CloudKMS_DecryptRequest as DecryptRequest;use Google_Service_CloudKMS_EncryptRequest as EncryptRequest;class KeyManager{ private $kms; private $encryptRequest; private $decryptRequest; private $projectId; private $locationId; private $keyRingId; private $cryptoKeyId; public function __construct(Kms $kms, EncryptRequest $encryptRequest, DecryptRequest $decryptRequest, $projectId, $locationId, $keyRingId, $cryptoKeyId) { $this->kms = $kms; $this->encryptRequest = $encryptRequest; $this->decryptRequest = $decryptRequest; $this->projectId = $projectId; $this->locationId = $locationId; $this->keyRingId = $keyRingId; $this->cryptoKeyId = $cryptoKeyId; } public function encrypt($data) { $key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $ciphertext = sodium_crypto_secretbox($data, $nonce, $key); return ; } public function decrypt($secret, $data) { $decoded = base64_decode($data); $key = $this->decryptSecret($secret); $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); return sodium_crypto_secretbox_open($ciphertext, $nonce, $key); } private function encryptKey($key) { $this->encryptRequest->setPlaintext(base64_encode($key)); $response = $this->kms->projects_locations_keyRings_cryptoKeys->encrypt( $this->getResourceName(), $this->encryptRequest ); return $response; } private function decryptSecret($secret) { $this->decryptRequest->setCiphertext($secret); $response = $this->kms->projects_locations_keyRings_cryptoKeys->decrypt( $this->getResourceName(), $this->decryptRequest ); return base64_decode($response); } private function getResourceName() { return sprintf( 'projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s', $this->projectId, $this->locationId, $this->keyRingId, $this->cryptoKeyId ); }}

huomaa, että varsinaiset salaus-ja salauksen purkumenetelmät ovat lähes identtiset yllä esitetyn salaisen avaimen toteutuksen kanssa. Erona on kuitenkin se, että käytämme nyt useita salausavaimia. Auttajaluokka tositoimiin. Sinun täytyy antaa $projectId, $locationId, $keyRingId ja $cryptoKeyId , jotka ovat saatavilla Google Cloud Consolesta.

<?phpuse Google_Service_CloudKMS as Kms;use Google_Service_CloudKMS_DecryptRequest as DecryptRequest;use Google_Service_CloudKMS_EncryptRequest as EncryptRequest;$client = new Google_Client();$client->setAuthConfig(getenv('GOOGLE_CREDENTIALS_FILE'));$client->addScope('https://www.googleapis.com/auth/cloud-platform');$keyManager = new KeyManager( new Kms($client), new EncryptRequest(), new DecryptRequest(), $projectId, $locationId, $keyRingId, $cryptoKeyId);$encrypted = $keyManager->encrypt('This is a secret!');var_dump($encrypted);// array (size=2)// 'data' => string 'uKjmEU7e1JEU+2vL3hBK2wBk6afCSgb+Y4GQtu/mmLuffgHlnqxnqOMPOI6WGkM18vAGGvFVDTvd' (length=76)// 'secret' => string 'CiQAdA0emUW2nhlU3RijX/5GnUsTnPPrQdLZNxdHWXWYugx49a4SSQBHyYr0T/PEbKwyFhIkaZl28oKkJRkXqNcqOL4Z+OTQFLpGvS6zCDt2mFn/nUQ/bi4znD4DORk9ZDTqiIBK3UNFUZcrXvoExds=' (length=152)$decrypted = $keyManager->decrypt($encrypted, $encrypted);var_dump($decrypted);// string 'This is a secret!' (length=17)

jos hyökkääjä vaarantaisi järjestelmämme, pystyisivätkö he saamaan myös API-tunnuksemme Cloud KMS: lle? Riippuen käytetystä todennusmenetelmästä, niin kyllä se voi olla mahdollista. Jos näin on, saatat ihmetellä, miten kirjekuoren salaus on yhtään turvallisempaa kuin tavallinen salaisen avaimen salaus? Keskeinen ero (pun tarkoitettu) on, että API pääsy voidaan peruuttaa, mikä estää hyökkääjä, joka on tehnyt pois arkaluonteisia tietoja. Se vastaa lukkojen vaihtamista, jos joku varastaa kotiavaimesi. Tavallisella salaisella salauksella, jossa yksi paikallinen avain vaarantuu, sinulla ei ole sitä ylellisyyttä. Hyökkääjällä on aikaa purkaa arkaluontoiset tietosi.

paketointi

tietoturva ja salaus ovat laajoja aiheita ja olen käynyt läpi vain kourallisen tapoja suojata arkaluontoisia tietoja PHP: n avulla. (Kirjoitimme aiemmin tällaisen tiedon suojelemisesta paikallisessa ympäristössä)

Vastaa

Sähköpostiosoitettasi ei julkaista.

Previous post Päivitys: poliisi pidättää epäilty ampuja Shady Oaks-Albert Lea Tribune / Albert Lea Tribune
Next post miten poistaa öljy-ja rasvatahrat vaatteista