PHP Krypteringsmetoder For Passord Og Andre Sensitive Data

jeg deltok nylig På Laracon EU 2018 hvor Marcus Bointon ga en god snakk om Krypto i PHP 7.2. Jeg forlot samtalen med en mye større forståelse for hvor mye komplisert kryptografi er, men også for hvordan PHP gjør kryptering mer tilgjengelig takket være Innføringen av Natrium. Datakryptering I PHP er noe jeg har jobbet med som en del av Arbeidet mitt På SpinupWP, så jeg trodde det var på tide jeg delte noen innsikt. Spenne opp, fordi dette kan være en humpete tur!

Typer Kryptering

det finnes en rekke forskjellige krypteringsmetoder i bruk i dag, de vanligste er hashing, hemmelig nøkkelkryptering og offentlig nøkkelkryptering. I tillegg har hver krypteringsmetode flere algoritmer eller koder å velge mellom (hver med sine egne styrker og svakheter). I denne artikkelen skal vi fokusere på å implementere hashing og hemmelig nøkkelkryptering.

Hashing

en hashingalgoritme tar en inngangsverdi og forvandler den til et meldingssammendrag. I et nøtteskall transformeres verdier i ren tekst til en hash med fast lengde, og kan bare valideres ved å sende den opprinnelige verdien til hashingalgoritmen. Dette gjør hashing perfekt for lagring av brukerpassord.

det er verdt å merke seg at hashing ikke er en skuddsikker løsning, og ikke alle hashingalgoritmer er like. VURDER MD5 OG SHA1 som er raske og effektive, noe som gjør dem ideelle for sjekksumming og filverifisering. Men deres hastighet gjør dem uegnet for hashing en brukers passord. Med dagens beregningskraft av moderne Gpuer, kan et passord bli sprakk av brute force i løpet av minutter, og avslører det opprinnelige klartekstpassordet. I stedet bør forsettlig langsommere hashingalgoritmer som bcrypt eller Argon2 brukes.

mens et hashed passord generert av en hvilken som helst algoritme vil sikkert skjule de opprinnelige dataene og redusere eventuelle angripere, bør vi som utviklere strebe etter å bruke den sterkeste algoritmen som er tilgjengelig. HELDIGVIS GJØR PHP dette enkelt takket være password_hash().

$hash = password_hash($password, PASSWORD_DEFAULT);

funksjonen password_hash() bruker ikke bare en sikker enveis hashingalgoritme, men den håndterer automatisk salt og forhindrer tidsbaserte sidekanalangrep. PER PHP 5.5, bcrypt vil bli brukt til å generere hash, men dette vil endre seg i fremtiden som nyere og sikrere hashing algoritmer legges TIL PHP. Argon2 vil sannsynligvis bli den neste standard hashingalgoritmen og kan brukes i DAG (PÅ PHP 7.2) ved å sende PASSWORD_ARGON2I flagget i stedet for PASSWORD_DEFAULT.

Å Verifisere en brukers passord er også en triviell prosess takket være password_verify() – funksjonen. Bare pass klartekst passord levert av brukeren og sammenligne den med den lagrede hash, som så:

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

Legg Merke til hvordan passordbekreftelsen utføres I PHP. Hvis du lagrer en brukers legitimasjon i en database, kan du være tilbøyelig til å hash passordet som er oppgitt ved innlogging og deretter utføre en databasespørring, slik som:

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

denne tilnærmingen er utsatt for sidekanalangrep og bør unngås. I stedet returnere brukeren og deretter sjekke passordet hash I PHP.

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

mens hashing er flott for lagring av brukerens passord, virker det ikke for vilkårlig data som vår søknad trenger tilgang uten brukerintervensjon. La oss vurdere et faktureringsprogram, som krypterer brukerens kredittkortinformasjon. Hver måned vår søknad må fakturere brukeren for sin forrige måneds bruk. Hashing kredittkortdata vil ikke fungere fordi det krever at vår søknad kjenner de opprinnelige dataene for det hente den i klartekst.

Hemmelig nøkkel kryptering til unnsetning!

Hemmelig Nøkkelkryptering

hemmelig nøkkelkryptering (eller symmetrisk kryptering som den også kalles) bruker en enkelt nøkkel til både å kryptere og dekryptere data. La oss se hvordan vi ville implementere En slik mekanisme ved Hjelp Av Natrium, som ble introdusert I PHP 7.2. Hvis du kjører en eldre versjon AV PHP, kan du installere sodium via PECL.

først trenger vi en krypteringsnøkkel, som kan genereres ved hjelp av random_bytes() – funksjonen. Vanligvis gjør du dette bare en gang og lagrer det som en miljøvariabel. Husk at denne nøkkelen må holdes hemmelig for enhver pris. Når nøkkelen er kompromittert, så er alle krypterte data.

$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);

for å kryptere verdien sender vi den til sodium_crypto_secretbox() med vår $key og en $nonce. Nonce genereres ved hjelp av random_bytes(), fordi den samme nonce aldri skal gjenbrukes.

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

dette presenterer et problem fordi vi trenger nonce å dekryptere verdien senere. Heldigvis må ikke nonces holdes hemmelige, slik at vi kan prepend det til vår $ciphertext og base64_encode() verdien før du lagrer den i databasen.

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

når det gjelder dekryptering av verdien, gjør vi det motsatte.

$decoded = base64_decode($encoded);

Fordi vi vet lengden på nonce, kan vi trekke den ut med mb_substr() før dekryptering av verdien.

$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)

det er alt som er å hemmelig nøkkelkryptering I PHP, Takket Være Sodium!

Konvoluttkryptering

mens tilnærmingen som er skissert ovenfor, er absolutt et skritt i riktig retning, forlater det fortsatt dataene våre sårbare hvis den hemmelige nøkkelen er kompromittert. La oss vurdere en ondsinnet bruker som får tilgang til serveren som er vert for vår søknad. I dette scenariet er sjansen stor for at angriperen vil kunne oppdage vår hemmelige nøkkel som vi pleide å kryptere dataene. Dette etterlater våre data helt eksponert.

den enkle løsningen er å ikke lagre vår hemmelige nøkkel på samme sted som de krypterte dataene, men dette gir et problem. Hvordan krypterer og dekrypterer vi på forespørsel? Skriv Inn Google Cloud Key Management Service (Cloud KMS).

Cloud KMS er En tjeneste levert Av Google for sikker hosting kryptografiske nøkler. Det gir en rekke nyttige funksjoner rundt nøkkel lagring, inkludert automatisk nøkkel rotasjon og forsinket nøkkel ødeleggelse. Men i dette eksemplet er vi først og fremst opptatt av å lagre vår hemmelige nøkkel bort fra dataene våre.

For å gjøre ting sikrere, skal vi bruke en teknikk kjent som konvoluttkryptering. I hovedsak innebærer konvoluttkryptering kryptering av nøkler med en annen nøkkel. Vi gjør dette av to grunner:

  1. Cloud KMS har en størrelsesgrense på 64 KiB på data som kan krypteres og dekrypteres. Derfor kan det ikke være mulig å sende alle dataene i ett fall.
  2. Enda viktigere, vi ønsker ikke å sende våre sensitive klartekstdata til en tredjepart, uansett hvor pålitelige de kan virke.

I Stedet for å sende våre klartekstdata til Cloud KMS, skal Vi generere en unik krypteringsnøkkel hver gang vi skriver sensitive data til databasen. DENNE nøkkelen er KJENT SOM EN datakrypteringsnøkkel (DEK), som vil bli brukt til å kryptere dataene våre. DEK sendes deretter Til Cloud KMS for å bli kryptert, som returnerer en nøkkelkrypteringsnøkkel (kjent som EN KEK). Til slutt lagres KEK side ved side i databasen ved siden av de krypterte dataene, OG DEK blir ødelagt. Prosessen ser slik ut:

  1. Generer en unik krypteringsnøkkel (DEK)
  2. Krypter dataene ved hjelp av hemmelig nøkkelkryptering
  3. Send den unike krypteringsnøkkelen (DEK) Til CLOUD KMS for kryptering, som returnerer KEK
  4. Lagre krypterte data og kryptert nøkkel (KEK) side ved side
  5. Ødelegge den genererte nøkkelen (DEK)

når dekryptering av data prosessen er reversert:

  1. Hent krypterte data og kryptert nøkkel (KEK) fra databasen
  2. Send KEK Til Cloud KMS for dekryptering, som returnerer DEK
  3. Bruk DEK til å dekryptere våre krypterte data
  4. Destroy THE DEK

Med dette i tankene har jeg laget en veldig enkel helper klasse for å utføre konvolutt kryptering. Jeg kommer ikke til Å gå over trinnene som kreves I Google Cloud console, Da Hurtigstart-og Autentiseringsguidene skisserer alt du trenger for å komme i gang. For korthetens skyld er det ingen feilhåndtering etc. i dette eksemplet.

<?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 ); }}

du vil legge merke til at de faktiske krypterings-og dekrypteringsmetodene er nesten identiske med den hemmelige nøkkelimplementeringen som ble introdusert ovenfor. Forskjellen er imidlertid at vi nå bruker flere krypteringsnøkler. La oss se hjelpeklassen i aksjon. Du må gi din $projectId, $locationId, $keyRingId og $cryptoKeyId som er tilgjengelige Fra Google Cloud console.

<?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)

hvis en angriper kompromitterte systemet vårt, ville de også kunne få VÅR API-legitimasjon for Cloud KMS? Avhengig av autentiseringsmetoden som brukes, så ja det kan være en mulighet. Hvis det er tilfelle, lurer du kanskje på hvordan konvoluttkryptering er noe sikrere enn vanlig hemmelig nøkkelkryptering? Hovedforskjellen (ordspill ment) er AT API-tilgang kan tilbakekalles, og dermed hindre en angriper som er laget av med dine sensitive data. Det er det samme som å bytte låser hvis noen stjeler husnøkkelen din. Med vanlig hemmelig nøkkelkryptering der en enkelt lokal nøkkel er kompromittert, har du ikke den luksusen. Angriperen har all tid i verden til å dekryptere dine sensitive data.

Innpakning

datasikkerhet og kryptering er store fag, og jeg har dekket bare en håndfull måter å beskytte sensitive data ved HJELP AV PHP. (Vi skrev tidligere om å beskytte denne typen data i ditt lokale miljø)

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.

Previous post Politiet arresterer angivelig skytter På Shady Oaks-Albert Lea Tribune / Albert Lea Tribune
Next post hvordan fjerne olje og fettflekker på klær