Increasing the default BCrypt cost View as Markdown

Implemented (PHP 8.4)
Authors
Tim Düsterhus, timwolla@php.net
Date
2023-09-07
Version
1.0
Target
PHP 8.4
Implementation
https://github.com/php/php-src/commit/42a85fc5d95c0c5e867cafcca8ebfca9d90e6c88
Source
wiki.php.net/rfc/bcrypt_cost_2023

Votes

Increase the default BCrypt cost? closed
Yes
25
No
0
25 voters
ashnazg Yes 2023-09-27 14:06
bukka Yes 2023-09-22 09:13
crell Yes 2023-09-21 18:21
derick Yes 2023-09-21 18:04
dharman Yes 2023-09-25 19:11
galvao Yes 2023-09-27 06:30
girgias Yes 2023-09-21 17:32
josh Yes 2023-09-22 09:58
kalle Yes 2023-09-22 11:46
kguest Yes 2023-10-04 11:06
kocsismate Yes 2023-09-25 11:11
levim Yes 2023-09-25 19:43
marandall Yes 2023-10-02 19:48
mbeccati Yes 2023-09-22 13:01
nicolasgrekas Yes 2023-09-22 06:58
nielsdos Yes 2023-09-21 17:45
ocramius Yes 2023-09-21 18:00
petk Yes 2023-09-22 05:37
rasmus Yes 2023-10-02 17:44
remi Yes 2023-09-22 06:50
sergey Yes 2023-10-03 06:54
svpernova09 Yes 2023-09-21 18:03
theodorejb Yes 2023-09-21 17:40
timwolla Yes 2023-09-21 17:27
trowski Yes 2023-10-03 02:07
Increase the default BCrypt cost to? closed
11 (less than 160ms per hash)
12
12 (less than 330ms per hash)
14
26 voters
ashnazg 11 (less than 160ms per hash) 2023-09-27 14:07
bukka 11 (less than 160ms per hash) 2023-09-22 09:13
crell 12 (less than 330ms per hash) 2023-09-21 18:21
derick 11 (less than 160ms per hash) 2023-09-21 18:04
dharman 12 (less than 330ms per hash) 2023-09-25 19:15
galvao 12 (less than 330ms per hash) 2023-09-27 06:31
girgias 12 (less than 330ms per hash) 2023-09-21 17:32
josh 12 (less than 330ms per hash) 2023-09-22 09:59
kalle 12 (less than 330ms per hash) 2023-09-22 11:47
kguest 11 (less than 160ms per hash) 2023-10-04 11:07
kocsismate 12 (less than 330ms per hash) 2023-09-25 11:11
levim 11 (less than 160ms per hash) 2023-09-25 19:43
marandall 12 (less than 330ms per hash) 2023-10-02 19:48
mbeccati 11 (less than 160ms per hash) 2023-09-25 08:06
nicolasgrekas 11 (less than 160ms per hash) 2023-09-22 06:58
nielsdos 11 (less than 160ms per hash) 2023-09-22 09:00
nikic 11 (less than 160ms per hash) 2023-09-21 17:40
ocramius 12 (less than 330ms per hash) 2023-09-21 18:00
petk 12 (less than 330ms per hash) 2023-09-24 16:40
rasmus 11 (less than 160ms per hash) 2023-10-02 17:44
remi 11 (less than 160ms per hash) 2023-09-22 06:50
sergey 12 (less than 330ms per hash) 2023-10-03 06:55
svpernova09 12 (less than 330ms per hash) 2023-09-21 18:03
theodorejb 11 (less than 160ms per hash) 2023-09-21 17:38
timwolla 12 (less than 330ms per hash) 2023-09-21 17:27
trowski 12 (less than 330ms per hash) 2023-10-03 02:07

Discussions on the mailing list

PASSWORD_DEFAULT value
by Vinicius Dias · 5 messages
RFC: Increasing the default BCrypt cost
· 13 messages
[VOTE] Increasing the default BCrypt cost
· 20 messages

RFC text

Copied from wiki.php.net, last synchronized Oct 13, 2026 11:50 UTC

Introduction

PHP's default BCrypt cost for password_hash is unchanged since the addition of the password hashing API in PHP 5.5 which was 11 years ago.

BCrypt's variable cost factor is intended to provide adaptable security against increases in processing power and thus increased cracking speed. It appears appropriate to reconsider the default value after 11 years have passed.

Technical explanation of the cost factor

Any increase of the cost by 1 will double the time it takes to calculate a single BCrypt hash.

Choice of the cost factor

The cost should be selected to be as large as possible without degrading the user experience and without introducing disproportional hardware requirements. OWASP recommends “less than a second” as a rule of thumb.

Processing time for several different CPUs

These times were measured using PHP 8.2 with this script and were selected to represent a reasonable range of CPUs that might still be in use today. All tests were carried out using wall-power (i.e. the Laptops were plugged in and not running from battery).

<?php
for ($cost = 8; $cost <= 14; $cost++) {
	$start = microtime(true);
	for ($i = 0; $i < 100; $i++) {
		$dummy = password_hash((string) $i, \PASSWORD_BCRYPT, ['cost' => $cost]);
	}
	$end = microtime(true);
 
	printf("Cost %d: %f total (%f per hash)\n", $cost, ($end - $start), ($end - $start) / 100);
}

Intel(R) Core(TM) i5-2430M CPU @ 2.40GHz (2011)

Representative for an aging and low-end developer laptop used by non-professional developers / students / lower-wage countries.

Cost 8: 2.119621 total (0.021196 per hash)
Cost 9: 4.067830 total (0.040678 per hash)
Cost 10: 8.063046 total (0.080630 per hash)
Cost 11: 16.157652 total (0.161577 per hash)
Cost 12: 32.794920 total (0.327949 per hash)
Cost 13: 65.690789 total (0.656908 per hash)
Cost 14: 131.897865 total (1.318979 per hash)

Apple M1 Pro (2021)

Representative of a recent commonly used developer laptop.

Cost 8: 1.519649 total (0.015196 per hash)
Cost 9: 3.139973 total (0.031400 per hash)
Cost 10: 6.101330 total (0.061013 per hash)
Cost 11: 12.102118 total (0.121021 per hash)
Cost 12: 24.038673 total (0.240387 per hash)
Cost 13: 48.072537 total (0.480725 per hash)
Cost 14: 96.567422 total (0.965674 per hash)

Intel(R) Xeon(R) E-2246G CPU @ 3.60GHz (2019)

Representative of a modern bare metal production server.

Cost 8: 1.011392 total (0.010114 per hash)
Cost 9: 2.021493 total (0.020215 per hash)
Cost 10: 3.967762 total (0.039678 per hash)
Cost 11: 7.885475 total (0.078855 per hash)
Cost 12: 15.852770 total (0.158528 per hash)
Cost 13: 31.636997 total (0.316370 per hash)
Cost 14: 63.183766 total (0.631838 per hash)

Intel(R) Xeon(R) CPU E31245 @ 3.30GHz (2011)

Representative of an aging bare metal production server.

Cost 8: 1.635034 total (0.016350 per hash)
Cost 9: 3.203396 total (0.032034 per hash)
Cost 10: 6.402601 total (0.064026 per hash)
Cost 11: 12.824214 total (0.128242 per hash)
Cost 12: 25.627993 total (0.256280 per hash)
Cost 13: 52.337828 total (0.523378 per hash)
Cost 14: 104.786531 total (1.047865 per hash)

AMD EPYC 7002 (2019)

Representative of a common host CPU within a modern virtualized and shared-CPU cloud environment. (Note: The author of this RFC was unable to verify the exact CPU model, due to the virtualization layer. The model name is what the cloud provider claims it to be).

Cost 8: 1.462180 total (0.014622 per hash)
Cost 9: 2.955791 total (0.029558 per hash)
Cost 10: 5.859765 total (0.058598 per hash)
Cost 11: 11.603300 total (0.116033 per hash)
Cost 12: 24.426527 total (0.244265 per hash)
Cost 13: 48.486861 total (0.484869 per hash)
Cost 14: 99.683899 total (0.996839 per hash)

Proposal

The default BCrypt cost shall be increased from 10 to either 11 (doubling the time) or 12 (quadrupling the time), depending on the secondary vote.

A cost of 12 is well below 0.5 seconds on all tested CPUs (with the slowest CPU being at 330ms) and should feel sufficiently snappy for an interactive login in a website even when needing to rehash a cost 10 hash during login.

The more conservative increase to 11 stays below 200ms for all tested CPUs and still provides reasonable response times when a handful of hashes need to be calculated within a single request (e.g. when generating and hashing multiple recovery codes for multi-factor authentication).

Backward Incompatible Changes

Users that rely on the default BCrypt cost will experience increased processing time when generating or validating BCrypt hashes. It naturally follows that the CPU usage of the application servers will increase.

For high-traffic websites it might be necessary to add additional application servers to handle the increased load. Alternatively the application developer will need to explicitly specify the existing BCrypt costs of 10, provided they still consider it appropriate for their threat model.

The increase of the costs is the entire point of this RFC and thus the reasoning for this possibly breaking change is given above.

Proposed PHP Version(s)

Next PHP 8.x (8.4).

RFC Impact

To SAPIs

None.

To Existing Extensions

None.

To Opcache

None.

New Constants

None.

php.ini Defaults

None.

Open Issues

None.

Unaffected PHP Functionality

Anything not BCrypt is unaffected (i.e. everything not password_* with PASSWORD_BCRYPT / BCrypt hashes). Users that explicitly specify the BCrypt cost are also unaffected.

Future Scope

None.

Proposed Voting Choices

2/3 majority to increase the default costs:

Vote: Increase the default BCrypt cost? — Yes: 25 · No: 0 (closed)

Simple majority to decide the new costs:

Vote: Increase the default BCrypt cost to? — 11 (less than 160ms per hash): 12 · 12 (less than 330ms per hash): 14 (closed)

Patches and Tests

https://github.com/php/php-src/pull/12367

Implementation

https://github.com/php/php-src/commit/42a85fc5d95c0c5e867cafcca8ebfca9d90e6c88

References

Rejected Features

None.