password_hash

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

password_hash β€” Π‘ΠΎΠ·Π΄Π°Ρ‘Ρ‚ Ρ…Π΅Ρˆ пароля

ОписаниС

function password_hash(#[\SensitiveParameter] string $password, string|int|null $algo, array $options = []): string

Ѐункция password_hash() создаёт Ρ…Π΅Ρˆ пароля Ρ‡Π΅Ρ€Π΅Π· ΡΠΈΠ»ΡŒΠ½Ρ‹ΠΉ Π½Π΅ΠΎΠ±Ρ€Π°Ρ‚ΠΈΠΌΡ‹ΠΉ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ.

ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚ΡΡ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΡ‹:

  • PASSWORD_DEFAULT β€” Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ bcrypt. Π‘ PHP 5.5.0 bcrypt стал Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ. По ΠΌΠ΅Ρ€Π΅ добавлСния Π² PHP Π½ΠΎΠ²Ρ‹Ρ… усилСнных Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ² Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ константы измСняСтся ΠΈ Π·Π°Π΄Π°Ρ‘Ρ‚ Π½ΠΎΠ²Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ°. ΠŸΡ€ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΈ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° Π΄Π»ΠΈΠ½Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π° измСняСтся, часто увСличиваСтся. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π² Π±Π°Π·Π΅ Π΄Π°Π½Π½Ρ‹Ρ… Π»ΡƒΡ‡ΡˆΠ΅ Π² столбцС Π΄Π»ΠΈΠ½ΠΎΠΉ большС 60 Π±Π°ΠΉΡ‚ΠΎΠ². Π”Π»ΠΈΠ½Ρƒ поля ΡƒΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°ΡŽΡ‚ Ρ€Π°Π²Π½ΠΎΠΉ 255 Π±Π°ΠΉΡ‚Π°ΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠΊΡ€Π°Ρ‚ΠΈΡ‚ΡŒ риск ошибок.
  • PASSWORD_BCRYPT β€” Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ bcrypt. Π‘ этой константой функция Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ стандартный Ρ…Π΅Ρˆ с ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ $2y$, совмСстимый с Ρ…Π΅ΡˆΠ΅ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ создаёт функция crypt().
  • PASSWORD_ARGON2I β€” Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ Argon2i. Алгоритм доступСн, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ссли PHP скомпилировали с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ Argon2.
  • PASSWORD_ARGON2ID β€” Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ Argon2id. Алгоритм доступСн, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ссли PHP скомпилировали с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ Argon2.

Алгоритм PASSWORD_BCRYPT ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΎΠΏΡ†ΠΈΠΈ:

  • salt (string) β€” ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠ°Ρ соль для Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ пароля. Ручная установка соли ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ β€” ΠΏΡ€Π΅Π΄ΠΎΡ‚Π²Ρ€Π°Ρ‚ΠΈΡ‚ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π΅ΡΠΊΡƒΡŽ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΡŽ соли.

    ΠŸΡ€ΠΈ пропускС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° функция password_hash() сгСнСрируСт ΡΠ»ΡƒΡ‡Π°ΠΉΠ½ΡƒΡŽ соль для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Ρ…Π΅ΡˆΠΈΡ€ΡƒΠ΅ΠΌΠΎΠ³ΠΎ пароля. Π­Ρ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡Ρ‚ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π΅ΠΆΠΈΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹.

    Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅

    ΠžΠΏΡ†ΠΈΡ salt устарСла Π² ΠΏΠΎΠ»ΡŒΠ·Ρƒ соли ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ функция Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ автоматичСски. Начиная с PHP 8.0.0 функция ΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΡƒΡŽ соль.

  • cost (int) β€” алгоритмичСская ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ΠΎΠΏΡ†ΠΈΠ΅ΠΉ доступСн Π½Π° страницС с описаниСм Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ crypt().

    ΠŸΡ€ΠΈ пропускС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° функция Π²Ρ‹Π±Π΅Ρ€Π΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ: 12. Π‘ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ функция Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ стойкий Ρ…Π΅Ρˆ, Π½ΠΎ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ΡΡ‚Ρ€Π°ΠΈΠ²Π°ΡŽΡ‚ ΠΏΠΎΠ΄ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ΅ ΠΎΠ±ΠΎΡ€ΡƒΠ΄ΠΎΠ²Π°Π½ΠΈΠ΅.

Алгоритмы PASSWORD_ARGON2I ΠΈ PASSWORD_ARGON2ID ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΎΠΏΡ†ΠΈΠΈ:

  • memory_cost (int) β€” ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ памяти Π² ΠΊΠΈΠ»ΠΎΠ±Π°ΠΉΡ‚Π°Ρ… для вычислСния Ρ…Π΅ΡˆΠ° Argon2. По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ функция Π²Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ константы PASSWORD_ARGON2_DEFAULT_MEMORY_COST.

  • time_cost (int) β€” ΠΏΡ€Π΅Π΄Π΅Π»ΡŒΠ½ΠΎΠ΅ количСство Ρ€Π°ΡƒΠ½Π΄ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ выполняСт Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Argon2 для вычислСния Ρ…Π΅ΡˆΠ°. Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ: PASSWORD_ARGON2_DEFAULT_TIME_COST.

  • threads (int) β€” количСство ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² для вычислСния Ρ…Π΅ΡˆΠ° Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ Argon2. Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ: PASSWORD_ARGON2_DEFAULT_THREADS.

    Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅

    ΠžΠΏΡ†ΠΈΡ доступна, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ссли PHP собрали с Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΎΠΉ libargon2, Π° Π½Π΅ с libsodium.

Бписок ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ²

password

ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ.

ΠŸΡ€Π΅Π΄ΠΎΡΡ‚Π΅Ρ€Π΅ΠΆΠ΅Π½ΠΈΠ΅

ΠŸΡ€ΠΈ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ пароля Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ PASSWORD_BCRYPT Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π° password усСкаСтся Π΄ΠΎ ΠΏΡ€Π΅Π΄Π΅Π»ΡŒΠ½ΠΎΠΉ Π΄Π»ΠΈΠ½Ρ‹ β€” 72 Π±Π°ΠΉΡ‚ΠΎΠ².

algo

ΠšΠΎΠ½ΡΡ‚Π°Π½Ρ‚Π° Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ пароля, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ функция.

options

Ассоциативный массив с опциями. Π—Π° Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠ΅ΠΉ ΠΏΠΎ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹ΠΌ опциям для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ΡΡŒ ΠΊ Ρ€Π°Π·Π΄Π΅Π»Ρƒ ΠšΠΎΠ½ΡΡ‚Π°Π½Ρ‚Ρ‹ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ² Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ.

Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΡ‹Π΅ значСния

Ѐункция Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ.

Π˜Π΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ°, ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ слоТности ΠΈ соль Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‚ΡΡ Π² составС Ρ…Π΅ΡˆΠ°, поэтому Π½Π΅ трСбуСтся Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎΠ± Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ΅ ΠΈ соли ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎ, Π° для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ пароля достаточно ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ Ρ…Π΅Ρˆ Π² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ password_verify().

Бписок измСнСний

ВСрсия ОписаниС
8.4.0 Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ для ΠΎΠΏΡ†ΠΈΠΈ cost Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° PASSWORD_BCRYPT ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΠ»ΠΈ с 10 Π΄ΠΎ 12.
8.3.0 Ѐункция password_hash() Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ устанавливаСт Π±Π°Π·ΠΎΠ²ΠΎΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Random\RandomException Π² качСствС значСния свойства Exception::$previous, Ссли выбрасываСтся ошибка ValueError ΠΈΠ·-Π·Π° сбоя Π²ΠΎ врСмя Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ соли.
8.0.0 Ѐункция password_hash() большС Π½Π΅ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ false, Ссли Π²ΠΎΠ·Π½ΠΈΠΊΠ»Π° ошибка. ВмСсто этого функция выбросит ΠΎΡˆΠΈΠ±ΠΊΡƒ ValueError, Ссли Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ пароля нСдСйствитСлСн, ΠΈΠ»ΠΈ ΠΎΡˆΠΈΠ±ΠΊΡƒ Error, Ссли Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ Π·Π°Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ ΠΈΠ·-Π·Π° нСизвСстной ошибки.
8.0.0 ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ algo Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ null.
7.4.0 ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ algo Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΎΠΆΠΈΠ΄Π°Π΅Ρ‚ строку (string), Π½ΠΎ всё Π΅Ρ‰Ρ‘ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Ρ†Π΅Π»ΠΎΠ΅ число (int) для ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΠΉ совмСстимости.
7.4.0 ΠœΠΎΠ΄ΡƒΠ»ΡŒ sodium обСспСчиваСт Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½ΡƒΡŽ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ Argon2.
7.3.0 Π’Π²Π΅Π»ΠΈ константу PASSWORD_ARGON2ID, которая Π΄ΠΎΠ±Π°Π²ΠΈΠ»Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΡƒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ Argon2id.
7.2.0 Π’Π²Π΅Π»ΠΈ константу PASSWORD_ARGON2I, которая Π΄ΠΎΠ±Π°Π²ΠΈΠ»Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΡƒ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ Argon2i.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #1 ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ пароля Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ password_hash()

<?php

echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT);

?>

Π’Ρ‹Π²ΠΎΠ΄ ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΡ…ΠΎΠΆ Π½Π°:

$2y$12$4Umg0rCJwMswRw/l.SwHvuQV01coP0eWmGzd61QH2RvAOMANUBGC.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #2 ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ Ρ…Π΅ΡˆΠ° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ password_hash() установкой алгоритмичСской слоТности Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ

<?php

$options
= [
// Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°Π΅ΠΌ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΈΡ‡Π΅ΡΠΊΡƒΡŽ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° bcrypt с 12 Π΄ΠΎ 13
'cost' => 13,
];

echo
password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options);

?>

Π’Ρ‹Π²ΠΎΠ΄ ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΡ…ΠΎΠΆ Π½Π°:

$2y$13$xeDfQumlmdm0Sco.4qmH1OGfUUmOcuRmfae0dPJhjX1Bq0yYhqbNi

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #3 ΠŸΡ€ΠΈΠΌΠ΅Ρ€ поиска ΠΎΠΏΡ‚ΠΈΠΌΡƒΠΌΠ° показатСля алгоритмичСской слоТности для Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ password_hash()

Код протСстируСт ΠΌΠ°ΡˆΠΈΠ½Ρƒ ΠΈ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ слоТности, с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ функция сгСнСрируСт стойкий Ρ…Π΅Ρˆ, Π½ΠΎ Π½Π΅ испортит ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ ΠΎΠΏΡ‹Ρ‚ ΠΈ Π½Π΅ Π·Π°ΠΌΠ΅Π΄Π»ΠΈΡ‚ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ выполняСт машина. ВСстированиС Π½Π°Ρ‡ΠΈΠ½Π°ΡŽΡ‚ с Π±Π°Π·ΠΎΠ²ΠΎΠ³ΠΎ значСния 11 ΠΈ ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΠ²Π°ΡŽΡ‚ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ, Ссли машина Π½Π΅ замСдляСт Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΏΠΎΠ΄ Π½Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ. Код ΠΈΡ‰Π΅Ρ‚ максимум, ΠΏΡ€ΠΈ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ ΠΏΡ€Π΅Π²Ρ‹ΡˆΠ°Π΅Ρ‚ 350 миллисСкунд β€” допустимая Π·Π°Π΄Π΅Ρ€ΠΆΠΊΠ° для систСм Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ.

<?php

$timeTarget
= 0.350; // 350 миллисСкунд

$cost = 11;

do {
$cost++;
$start = microtime(true);
password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
$end = microtime(true);
} while ((
$end - $start) < $timeTarget);

echo
"ΠžΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½Π°Ρ ΡΡ‚ΠΎΠΈΠΌΠΎΡΡ‚ΡŒ: " . $cost - 1;

?>

Π’Ρ‹Π²ΠΎΠ΄ ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΡ…ΠΎΠΆ Π½Π°:

ΠžΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½Π°Ρ ΡΡ‚ΠΎΠΈΠΌΠΎΡΡ‚ΡŒ: 13

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #4 ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ Ρ…Π΅ΡˆΠ° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ password_hash() Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ Argon2i

<?php

echo 'Π₯Сш Argon2i: ' . password_hash('rasmuslerdorf', PASSWORD_ARGON2I);

?>

Π’Ρ‹Π²ΠΎΠ΄ ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΡ…ΠΎΠΆ Π½Π°:

Π₯Сш Argon2i: $argon2i$v=19$m=1024,t=2,p=2$YzJBSzV4TUhkMzc3d3laeg$zqU/1IN0/AogfP4cmSJI1vc8lpXRW9/S0sYY2i2jHT0

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΡ

ΠŸΡ€Π΅Π΄ΠΎΡΡ‚Π΅Ρ€Π΅ΠΆΠ΅Π½ΠΈΠ΅

Ѐункция автоматичСски создаст Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡƒΡŽ соль, поэтому Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ PHP Π½Π°ΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΡŽΡ‚ Π½Π΅ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΎΠΏΡ†ΠΈΡŽ salt Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ.

Π’ PHP 7.0.0 установка значСния ΠΎΠΏΡ†ΠΈΠΈ salt Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ сгСнСрируСт ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΎΠ± устарСвании. Π’ PHP 8.0.0 установку ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠΉ соли ΡƒΠ΄Π°Π»ΠΈΠ»ΠΈ.

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅:

Π Π°Π±ΠΎΡ‚Ρƒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Ρ‚Π΅ΡΡ‚ΠΈΡ€ΡƒΡŽΡ‚ Π½Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠΉ машинС ΠΈ ΠΏΠΎΠ΄ΡΡ‚Ρ€Π°ΠΈΠ²Π°ΡŽΡ‚ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ слоТности Π΄ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ, с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ ΠΏΡ€ΠΈ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ функция выполняСтся Π½Π΅ дольшС 350 миллисСкунд. Π‘ΠΊΡ€ΠΈΠΏΡ‚ Π² ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ‚ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΎΠΏΡ‚ΠΈΠΌΡƒΠΌ алгоритмичСской слоТности для Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° bcrypt.

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅: ОбновлСниС Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ функция, ΠΈΠ»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ подчиняСтся ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΏΡ€Π°Π²ΠΈΠ»Π°ΠΌ:

  • Π‘ ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Π½ΠΎΠ²ΠΎΠ³ΠΎ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° Π² ядро Π΄ΠΎ установки ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ ΠΏΡ€ΠΎΡˆΡ‘Π» 1 ΠΏΠΎΠ»Π½Ρ‹ΠΉ выпуск PHP. Алгоритм, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π²Π²Π΅Π»ΠΈ Π² вСрсии 7.5.5, ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ ΠΏΡ€Π°Π²ΠΎ ΡΡ‚Π°Ρ‚ΡŒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² вСрсии 7.7, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ вСрсия 7.6 станСт ΠΏΠ΅Ρ€Π²Ρ‹ΠΌ ΠΏΠΎΠ»Π½Ρ‹ΠΌ выпуском. Но ΠΈ Π΄Ρ€ΡƒΠ³ΠΎΠΉ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ Π±Ρ‹ Π² вСрсии 7.6.0, Ρ‚ΠΎΠΆΠ΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ» Π±Ρ‹ ΠΏΡ€Π°Π²ΠΎ ΡΡ‚Π°Ρ‚ΡŒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ Π² вСрсии 7.7.0.
  • Алгоритм ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ измСняСтся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² ΠΏΠΎΠ»Π½ΠΎΠΌ выпускС, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 7.3.0 ΠΈΠ»ΠΈ 8.0.0, Π½ΠΎ Π½Π΅ Π² ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½Ρ‹Ρ…. ЕдинствСнноС ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ β€” критичСская ΡƒΡΠ·Π²ΠΈΠΌΠΎΡΡ‚ΡŒ бСзопасности, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ»ΠΈ Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΌ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ.

Π‘ΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ Ρ‚Π°ΠΊΠΆΠ΅

  • password_verify() - ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅Ρ‚, соотвСтствуСт Π»ΠΈ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ Ρ…Π΅ΡˆΡƒ
  • password_needs_rehash() - ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΉ Ρ…Π΅Ρˆ соотвСтствуСт Π·Π°Π΄Π°Π½Π½Ρ‹ΠΌ опциям
  • crypt() - Π₯Π΅ΡˆΠΈΡ€ΡƒΠ΅Ρ‚ строку Π½Π΅ΠΎΠ±Ρ€Π°Ρ‚ΠΈΠΌΡ‹ΠΌ способом
  • sodium_crypto_pwhash_str() - ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ ASCII-ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ Ρ…Π΅Ρˆ
οΌ‹Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ 8 notes

up
164
phpnetcomment201908 at lucb1e dot com ΒΆ
6 years ago
Since 2017, NIST recommends using a secret input when hashing memorized secrets such as passwords. By mixing in a secret input (commonly called a "pepper"), one prevents an attacker from brute-forcing the password hashes altogether, even if they have the hash and salt. For example, an SQL injection typically affects only the database, not files on disk, so a pepper stored in a config file would still be out of reach for the attacker. A pepper must be randomly generated once and can be the same for all users. Many password leaks could have been made completely useless if site owners had done this.

Since there is no pepper parameter for password_hash (even though Argon2 has a "secret" parameter, PHP does not allow to set it), the correct way to mix in a pepper is to use hash_hmac(). The "add note" rules of php.net say I can't link external sites, so I can't back any of this up with a link to NIST, Wikipedia, posts from the security stackexchange site that explain the reasoning, or anything... You'll have to verify this manually. The code:

// config.conf
pepper=c1isvFdxMDdmjOlvxpecFw

<?php
// register.php
$pepper = getConfigVariable("pepper");
$pwd = $_POST['password'];
$pwd_peppered = hash_hmac("sha256", $pwd, $pepper);
$pwd_hashed = password_hash($pwd_peppered, PASSWORD_ARGON2ID);
add_user_to_database($username, $pwd_hashed);
?>

<?php
// login.php
$pepper = getConfigVariable("pepper");
$pwd = $_POST['password'];
$pwd_peppered = hash_hmac("sha256", $pwd, $pepper);
$pwd_hashed = get_pwd_from_db($username);
if (password_verify($pwd_peppered, $pwd_hashed)) {
    echo "Password matches.";
}
else {
    echo "Password incorrect.";
}
?>

Note that this code contains a timing attack that leaks whether the username exists. But my note was over the length limit so I had to cut this paragraph out.

Also note that the pepper is useless if leaked or if it can be cracked. Consider how it might be exposed, for example different methods of passing it to a docker container. Against cracking, use a long randomly generated value (like in the example above), and change the pepper when you do a new install with a clean user database. Changing the pepper for an existing database is the same as changing other hashing parameters: you can either wrap the old value in a new one and layer the hashing (more complex), you compute the new password hash whenever someone logs in (leaving old users at risk, so this might be okay depending on what the reason is that you're upgrading).

Why does this work? Because an attacker does the following after stealing the database:

password_verify("a", $stolen_hash)
password_verify("b", $stolen_hash)
...
password_verify("z", $stolen_hash)
password_verify("aa", $stolen_hash)
etc.

(More realistically, they use a cracking dictionary, but in principle, the way to crack a password hash is by guessing. That's why we use special algorithms: they are slower, so each verify() operation will be slower, so they can try much fewer passwords per hour of cracking.)

Now what if you used that pepper? Now they need to do this:

password_verify(hmac_sha256("a", $secret), $stolen_hash)

Without that $secret (the pepper), they can't do this computation. They would have to do:

password_verify(hmac_sha256("a", "a"), $stolen_hash)
password_verify(hmac_sha256("a", "b"), $stolen_hash)
...
etc., until they found the correct pepper.

If your pepper contains 128 bits of entropy, and so long as hmac-sha256 remains secure (even MD5 is technically secure for use in hmac: only its collision resistance is broken, but of course nobody would use MD5 because more and more flaws are found), this would take more energy than the sun outputs. In other words, it's currently impossible to crack a pepper that strong, even given a known password and salt.
up
9
fullstadev at gmail dot com ΒΆ
2 years ago
Similar to another post made here about the use of strings holding null-bytes within password_hash(), I wanted to be a little more precise, as we've had quite some issues now. 

I've had a project of an application generating random hashes (CSPRN). What they've done is that they've used random_bytes(32), and the applied password_hash() to that obtained string, with the bcrypt algorithm. 

This on one side led to the fact that sometimes, random_bytes() generated a string with null-bytes, actually resulting to an error in their call to password_hash() (PHP v 8.2.18). Thanks to that ("Bcrypt password must not contain a null character") I modified the the function generating random hashes to encoding the obtained binary random string with random_bytes() using bin2hex() (or base64 or whatever), to assure that the string to be hashed has no null-bytes. 

I then just wanted to add that, when you use the bcrypt algorithm, make sure to remember that bcrypt truncates your password at 72 characters. When encoding your random string (e.g. generated using random_bytes()), this will convert your string from binary to hex representation, e.g. doubling its length. What you generally want is that your entire password is still contained within the 72 characters limit, to be sure that your entire "random information" gets hashes, and not only part of it.
up
48
nicoSWD ΒΆ
12 years ago
I agree with martinstoeckli,

don't create your own salts unless you really know what you're doing.

By default, it'll use /dev/urandom to create the salt, which is based on noise from device drivers.

And on Windows, it uses CryptGenRandom().

Both have been around for many years, and are considered secure for cryptography (the former probably more than the latter, though).

Don't try to outsmart these defaults by creating something less secure. Anything that is based on rand(), mt_rand(), uniqid(), or variations of these is *not* good.
up
8
bhare at duck dot com ΒΆ
2 years ago
If you are you going to use bcrypt then you should pepper the passwords with random large string, as commodity hardware can break bcrypt 8 character passwords within an hour; https://www.tomshardware.com/news/eight-rtx-4090s-can-break-passwords-in-under-an-hour
up
35
Lyo Mi ΒΆ
10 years ago
Please note that password_hash will ***truncate*** the password at the first NULL-byte.

http://blog.ircmaxell.com/2015/03/security-issue-combining-bcrypt-with.html

If you use anything as an input that can generate NULL bytes (sha1 with raw as true, or if NULL bytes can naturally end up in people's passwords), you may make your application much less secure than what you might be expecting.

The password 
$a = "\01234567"; 
is zero bytes long (an empty password) for bcrypt.

The workaround, of course, is to make sure you don't ever pass NULL-bytes to password_hash.
up
9
ms1 at rdrecs dot com ΒΆ
6 years ago
Timing attacks simply put, are attacks that can calculate what characters of the password are due to speed of the execution.

More at...
https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy

I have added code to phpnetcomment201908 at lucb1e dot com's suggestion to make this possible "timing attack" more difficult using the code phpnetcomment201908 at lucb1e dot com posted.

$pph_strt = microtime(true);

//...
/*The code he posted for login.php*/
//...

$end = (microtime(true) - $pph_strt);

$wait = bcmul((1 - $end), 1000000);  // usleep(250000) 1/4 of a second

usleep ( $wait );

echo "<br>Execution time:".(microtime(true) - $pph_strt)."; ";

Note I suggest changing the wait time to suit your needs but make sure that it is more than than the highest execution time the script takes on your server.

Also, this is my workaround to obfuscate the execution time to nullify timing attacks. You can find an in-depth discussion and more from people far more equipped than I for cryptography at the link I posted. I do not believe this was there but there are others. It is where I found out what timing attacks were as I am new to this but would like solid security.
up
21
martinstoeckli ΒΆ
13 years ago
In most cases it is best to omit the salt parameter. Without this parameter, the function will generate a cryptographically safe salt, from the random source of the operating system.
up
14
Mike Robinson ΒΆ
11 years ago
For passwords, you generally want the hash calculation time to be between 250 and 500 ms (maybe more for administrator accounts). Since calculation time is dependent on the capabilities of the server, using the same cost parameter on two different servers may result in vastly different execution times. Here's a quick little function that will help you determine what cost parameter you should be using for your server to make sure you are within this range (note, I am providing a salt to eliminate any latency caused by creating a pseudorandom salt, but this should not be done when hashing passwords):

<?php
/**
 * @Param int $min_ms Minimum amount of time in milliseconds that it should take
 * to calculate the hashes
 */
function getOptimalBcryptCostParameter($min_ms = 250) {
    for ($i = 4; $i < 31; $i++) {
        $options = [ 'cost' => $i, 'salt' => 'usesomesillystringforsalt' ];
        $time_start = microtime(true);
        password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options);
        $time_end = microtime(true);
        if (($time_end - $time_start) * 1000 > $min_ms) {
            return $i;
        }
    }
}
echo getOptimalBcryptCostParameter(); // prints 12 in my case
?>