# RFC: Increasing the default BCrypt cost

- Thread: https://externals.io/message/121004
- Original archive: https://news-web.php.net/php.internals/121004
- Started by tim@bastelstu.be on 2023-09-07T17:26:34+00:00
- Messages: 13
- RFC: Increasing the default BCrypt cost (Implemented (PHP 8.4)) — https://externals.io/rfc/bcrypt_cost_2023.md
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #121004 — tim@bastelstu.be — 2023-09-07T17:26:34+00:00

URL: https://externals.io/message/121004#121004

Hi

in response to the recent "PASSWORD_DEFAULT value" thread [1], I've 
created an RFC to discuss an increase of the default BCrypt costs for 
`password_hash()` from the current value of 10.

https://wiki.php.net/rfc/bcrypt_cost_2023

This message is intended to officially open the discussion period for 
that RFC.

Best regards
Tim Düsterhus

[1] https://externals.io/message/120993

---

## #121006 — Niels Dossche — 2023-09-07T18:05:07+00:00

URL: https://externals.io/message/121004#121006 · In reply to: #121004

Hi Tim


> Hi
> 
> in response to the recent "PASSWORD_DEFAULT value" thread [1], I've created an RFC to discuss an increase of the default BCrypt costs for `password_hash()` from the current value of 10.
> 
> https://wiki.php.net/rfc/bcrypt_cost_2023

These are the kind of things that are easily forgotten.
Thank you for taking care of this!

I just noticed one small detail.
From the RFC text: "All tests were carried out using wall-power." I guess you mean wall-time?

> 
> This message is intended to officially open the discussion period for that RFC.
> 
> Best regards
> Tim Düsterhus
> 
> [1] https://externals.io/message/120993
> 

Kind regards
Niels

---

## #121007 — tim@bastelstu.be — 2023-09-07T18:09:39+00:00

URL: https://externals.io/message/121004#121007 · In reply to: #121006

Hi


> I just noticed one small detail.
>  From the RFC text: "All tests were carried out using wall-power." I guess you mean wall-time?
> 

No, this means that the laptops whose CPUs where tested were plugged 
into the wall :-)

I've added a parenthesis clarifying what that intends to mean.

Best regards
Tim Düsterhus

---

## #121009 — Craig Francis — 2023-09-07T21:37:39+00:00

URL: https://externals.io/message/121004#121009 · In reply to: #121004

> in response to the recent "PASSWORD_DEFAULT value" thread [1], I've created an RFC to discuss an increase of the default BCrypt costs for `password_hash()` from the current value of 10.
> 
> https://wiki.php.net/rfc/bcrypt_cost_2023


Thanks Tim,

Just quickly running this on two AWS EC2 servers, to give rough figures for a VM (note usual issues like noisy neighbours, turbo-boost, thermal throttling, etc).

t2.nano

Cost 8: 2.083060 total (0.020831 per hash)
Cost 9: 4.115596 total (0.041156 per hash)
Cost 10: 8.238419 total (0.082384 per hash)
Cost 11: 16.334089 total (0.163341 per hash)
Cost 12: 32.693785 total (0.326938 per hash)
Cost 13: 65.587982 total (0.655880 per hash)
Cost 14: 131.358058 total (1.313581 per hash)

t2.small

Cost 8: 2.062625 total (0.020626 per hash)
Cost 9: 4.142067 total (0.041421 per hash)
Cost 10: 8.231646 total (0.082316 per hash)
Cost 11: 16.851889 total (0.168519 per hash)
Cost 12: 32.814440 total (0.328144 per hash)
Cost 13: 69.409889 total (0.694099 per hash)
Cost 14: 133.682196 total (1.336822 per hash)

Both nano and small only have 1 vCPU, have 0.5 vs 1 GiB RAM, and a different number of CPU Credits/hr.

We recently discussed hashing and costs at one of our OWASP meetings, we came to conclusion that the default of 10 for bcrypt probably should be increased, but only to 11 for typical websites. The main concern was about making denial-of-service attacks easier (think of a normal website developer, who won't limit the number of login attempts).

It's also worth keeping in mind the difference between online vs offline attacks, what it's being used for, human behaviour when it comes to choosing bad passwords ("123456" and "Password1!" will still be guessed very quickly), etc.

Craig

---

## #121010 — Hans Henrik Bergan — 2023-09-08T08:13:51+00:00

URL: https://externals.io/message/121004#121010 · In reply to: #121009

@Craig warning, it's very random what kind of CPU performance you get
on your t2 instances, the CPUs vary greatly from modern to many years
old.

I know of Fortune 500 companies that have automated systems to spin up
t2 instances until they randomly get "a good one", then discard the
others, because the cpu performance vary so widely



>
> 
> > […]
>
>
> Thanks Tim,
>
> Just quickly running this on two AWS EC2 servers, to give rough figures for a VM (note usual issues like noisy neighbours, turbo-boost, thermal throttling, etc).
>
> t2.nano
>
> Cost 8: 2.083060 total (0.020831 per hash)
> Cost 9: 4.115596 total (0.041156 per hash)
> Cost 10: 8.238419 total (0.082384 per hash)
> Cost 11: 16.334089 total (0.163341 per hash)
> Cost 12: 32.693785 total (0.326938 per hash)
> Cost 13: 65.587982 total (0.655880 per hash)
> Cost 14: 131.358058 total (1.313581 per hash)
>
> t2.small
>
> Cost 8: 2.062625 total (0.020626 per hash)
> Cost 9: 4.142067 total (0.041421 per hash)
> Cost 10: 8.231646 total (0.082316 per hash)
> Cost 11: 16.851889 total (0.168519 per hash)
> Cost 12: 32.814440 total (0.328144 per hash)
> Cost 13: 69.409889 total (0.694099 per hash)
> Cost 14: 133.682196 total (1.336822 per hash)
>
> Both nano and small only have 1 vCPU, have 0.5 vs 1 GiB RAM, and a different number of CPU Credits/hr.
>
> We recently discussed hashing and costs at one of our OWASP meetings, we came to conclusion that the default of 10 for bcrypt probably should be increased, but only to 11 for typical websites. The main concern was about making denial-of-service attacks easier (think of a normal website developer, who won't limit the number of login attempts).
>
> It's also worth keeping in mind the difference between online vs offline attacks, what it's being used for, human behaviour when it comes to choosing bad passwords ("123456" and "Password1!" will still be guessed very quickly), etc.
>
> Craig
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php

---

## #121018 — drealecs@gmail.com — 2023-09-08T16:49:58+00:00

URL: https://externals.io/message/121004#121018 · In reply to: #121004

> Hi
>
> in response to the recent "PASSWORD_DEFAULT value" thread [1], I've
> created an RFC to discuss an increase of the default BCrypt costs for
> `password_hash()` from the current value of 10.
>
> https://wiki.php.net/rfc/bcrypt_cost_2023
>
>

I think 12 looks reasonable.
I've performed some tests myself on private hosted servers with
newer hardware with good results for 12 around 0.1 seconds.

Can this be integrated into PHP 8.3, as it's not a new feature that can
cause problems?
Pushing it to 8.4 will delay the real usage with 2-3 more years already.

I feel like the hardware performance improvements (specifically single
thread performance) slightly increased in the past 3-4 years, and soon most
of the hosting providers will be using it.

Thank you for looking into this. Having good security configuration by
default is important.

Regards,
Alex

---

## #121032 — tim@bastelstu.be — 2023-09-10T16:05:58+00:00

URL: https://externals.io/message/121004#121032 · In reply to: #121018

Hi


> > […]
> 
> I think 12 looks reasonable.
> I've performed some tests myself on private hosted servers with
> newer hardware with good results for 12 around 0.1 seconds.

wow, that is a 33% reduction even compared to the Xeon E-2246G and thus 
hard to believe. What CPU is that?

> Can this be integrated into PHP 8.3, as it's not a new feature that can
> cause problems?

The release managers for PHP 8.3 would need to decide that. However I'd 
rather not include this in PHP 8.3 at this point.

> Pushing it to 8.4 will delay the real usage with 2-3 more years already.

IMO this is fine. Common frameworks can and do already use a different 
default. Symfony apparently is at 13 by default. Laravel uses 10, but 
I've already pinged someone on Mastodon to maybe have a look at the 
results of this RFC:

https://phpc.social/@timwolla/111025125667858110

The current default of 10 is not insecure and rolling this out a little 
more slowly will mean that more and more of the old and slow hardware 
will be retired and replaced by modern hardware, lessening the impact.

> I feel like the hardware performance improvements (specifically single
> thread performance) slightly increased in the past 3-4 years, and soon most
> of the hosting providers will be using it.
>

 From my experience as a developer of a software that is commonly run on 
shared hosting, web hosters *love* their ancient hardware, because it's 
fully depreciated from a taxation / accounting PoV and every extra day 
it is used is "free money". Customers commonly are not able to tell they 
are running with tens of other customers on this ancient hardware and 
thus won't complain ("loading times of 1 second are fine").

Best regards
Tim Düsterhus

---

## #121046 — Hans Henrik Bergan — 2023-09-12T15:56:17+00:00

URL: https://externals.io/message/121004#121046 · In reply to: #121032

>web hosters *love* their ancient hardware

No kidding. dreamhost.com host over 1.5 million websites, presumably most
are on their "Shared Unlimited" package, which runs on
AMD Opteron 4122, a high-end server CPU from 2010.
Some benchmarks there:

hanshenrik@jonathan-dayton:~$ cat /proc/cpuinfo | head
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 16
model           : 8
model name      : AMD Opteron(tm) Processor 4122
stepping        : 0
microcode       : 0x10000da
cpu MHz         : 2200.000
cache size      : 512 KB
physical id     : 0
hanshenrik@jonathan-dayton:~$ php -v
PHP 8.2.5 (cli) (built: Apr 13 2023 18:45:57) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.5, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.5, Copyright (c), by Zend Technologies
hanshenrik@jonathan-dayton:~$ hyperfine 'php -r
'\''password_hash("password1234",PASSWORD_BCRYPT,["cost"=>9]);'\'''
Benchmark 1: php -r
'password_hash("password1234",PASSWORD_BCRYPT,["cost"=>9]);'
  Time (mean ± σ):     122.7 ms ±   2.4 ms    [User: 78.1 ms, System: 33.7
ms]
  Range (min … max):   120.0 ms … 127.5 ms    22 runs

hanshenrik@jonathan-dayton:~$ hyperfine 'php -r
'\''password_hash("password1234",PASSWORD_BCRYPT,["cost"=>10]);'\'''
Benchmark 1: php -r
'password_hash("password1234",PASSWORD_BCRYPT,["cost"=>10]);'
  Time (mean ± σ):     166.4 ms ±   2.7 ms    [User: 115.1 ms, System: 39.3
ms]
  Range (min … max):   163.0 ms … 171.2 ms    18 runs

hanshenrik@jonathan-dayton:~$ hyperfine 'php -r
'\''password_hash("password1234",PASSWORD_BCRYPT,["cost"=>11]);'\'''
Benchmark 1: php -r
'password_hash("password1234",PASSWORD_BCRYPT,["cost"=>11]);'
  Time (mean ± σ):     246.0 ms ±   5.2 ms    [User: 198.2 ms, System: 34.5
ms]
  Range (min … max):   241.0 ms … 256.5 ms    12 runs

hanshenrik@jonathan-dayton:~$ hyperfine 'php -r
'\''password_hash("password1234",PASSWORD_BCRYPT,["cost"=>12]);'\'''
Benchmark 1: php -r
'password_hash("password1234",PASSWORD_BCRYPT,["cost"=>12]);'
  Time (mean ± σ):     409.7 ms ±   3.6 ms    [User: 355.6 ms, System: 41.6
ms]
  Range (min … max):   405.3 ms … 416.6 ms    10 runs

hanshenrik@jonathan-dayton:~$ hyperfine 'php -r
'\''password_hash("password1234",PASSWORD_BCRYPT,["cost"=>13]);'\'''
Benchmark 1: php -r
'password_hash("password1234",PASSWORD_BCRYPT,["cost"=>13]);'
  Time (mean ± σ):     729.3 ms ±  10.6 ms    [User: 672.5 ms, System: 43.8
ms]
  Range (min … max):   717.3 ms … 754.5 ms    10 runs

must say, surprisingly good performance for a 2010 cpu



> Hi
>
> 
> > […]
>
> wow, that is a 33% reduction even compared to the Xeon E-2246G and thus
> hard to believe. What CPU is that?
>
> > […]
>
> The release managers for PHP 8.3 would need to decide that. However I'd
> rather not include this in PHP 8.3 at this point.
>
> > […]
>
> IMO this is fine. Common frameworks can and do already use a different
> default. Symfony apparently is at 13 by default. Laravel uses 10, but
> I've already pinged someone on Mastodon to maybe have a look at the
> results of this RFC:
>
> https://phpc.social/@timwolla/111025125667858110
>
> The current default of 10 is not insecure and rolling this out a little
> more slowly will mean that more and more of the old and slow hardware
> will be retired and replaced by modern hardware, lessening the impact.
>
> > […]
> most
> > […]
>
>  From my experience as a developer of a software that is commonly run on
> shared hosting, web hosters *love* their ancient hardware, because it's
> fully depreciated from a taxation / accounting PoV and every extra day
> it is used is "free money". Customers commonly are not able to tell they
> are running with tens of other customers on this ancient hardware and
> thus won't complain ("loading times of 1 second are fine").
>
> Best regards
> Tim Düsterhus
>
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php

---

## #121047 — tim@bastelstu.be — 2023-09-12T16:01:27+00:00

URL: https://externals.io/message/121004#121047 · In reply to: #121046

Hi


> > […]
> 
> No kidding. dreamhost.com host over 1.5 million websites, presumably most
> are on their "Shared Unlimited" package, which runs on
> AMD Opteron 4122, a high-end server CPU from 2010.
> Some benchmarks there:

It appears your benchmarks are broken, because you're also measuring the 
startup cost of PHP itself. You should see an approximate doubling in 
time taken for each increase of the cost, but you have 122ms for 9 and 
166ms for 10.

A simple microtime(true) benchmark loop as used with my test script 
should be fine for ballpark estimates, as `password_hash()` is pretty 
heavy, dwarfing the measurement overhead.

Best regards
Tim Düsterhus

---

## #121050 — Hans Henrik Bergan — 2023-09-12T21:35:56+00:00

URL: https://externals.io/message/121004#121050 · In reply to: #121047

>you're also measuring the startup cost of PHP itself.

yup correct

>A simple microtime(true) benchmark loop as used with my test script should be fine for ballpark estimates

agreed

>dwarfing the measurement overhead.

wouldn't count out random cpu context switch/background noise
completely, see my hyperfine benchmark for cost 13:
>  Range (min … max):   717.3 ms … 754.5 ms    10 runs
- over 10 runs, the fastest was 717ms and the slowest was 754ms, so
background noise made at least 37ms difference there.. but yeah
anyway, here's from your script:

hanshenrik@jonathan-dayton:~$ time php bench.php
Cost 8: 2.022854 total (0.020229 per hash)
Cost 9: 4.176210 total (0.041762 per hash)
Cost 10: 8.121596 total (0.081216 per hash)
Cost 11: 16.919405 total (0.169194 per hash)
Cost 12: 33.059071 total (0.330591 per hash)
Cost 13: 66.761731 total (0.667617 per hash)
Cost 14: 140.695820 total (1.406958 per hash)

real    4m31.854s
user    4m13.115s
sys     0m2.168s
hanshenrik@jonathan-dayton:~$ cat /proc/cpuinfo | head
(...)
model name      : AMD Opteron(tm) Processor 4122


>
> Hi
>
> 
> > […]
>
> It appears your benchmarks are broken, because you're also measuring the
> startup cost of PHP itself. You should see an approximate doubling in
> time taken for each increase of the cost, but you have 122ms for 9 and
> 166ms for 10.
>
> A simple microtime(true) benchmark loop as used with my test script
> should be fine for ballpark estimates, as `password_hash()` is pretty
> heavy, dwarfing the measurement overhead.
>
> Best regards
> Tim Düsterhus

---

## #121082 — drealecs@gmail.com — 2023-09-18T02:40:27+00:00

URL: https://externals.io/message/121004#121082 · In reply to: #121032

> Hi
>
> 
> > […]
>
> wow, that is a 33% reduction even compared to the Xeon E-2246G and thus
> hard to believe. What CPU is that?
>
>
That was a new Xeon Gold 5416S.
I got lucky being able to run some tests on it before being pushed to
production replacing a slightly older one, a Xeon Gold 5218.
I'll be able to test that as well, but I feel like anyway the hardware I
used is not what is usually in general.
Actually, it is even less relevant, as it is meant to be used by a high
performing MySQL server and not for running PHP.


> > […]
>
> IMO this is fine. Common frameworks can and do already use a different
> default. Symfony apparently is at 13 by default. Laravel uses 10, but
> I've already pinged someone on Mastodon to maybe have a look at the
> results of this RFC:
>
> https://phpc.social/@timwolla/111025125667858110
>
> The current default of 10 is not insecure and rolling this out a little
> more slowly will mean that more and more of the old and slow hardware
> will be retired and replaced by modern hardware, lessening the impact.
>
>
Understood, yes, I agree.


> > […]
> most
> > […]
>
>  From my experience as a developer of a software that is commonly run on
> shared hosting, web hosters *love* their ancient hardware, because it's
> fully depreciated from a taxation / accounting PoV and every extra day
> it is used is "free money". Customers commonly are not able to tell they
> are running with tens of other customers on this ancient hardware and
> thus won't complain ("loading times of 1 second are fine").
>
>
Yes, I think I evaluated the hardware upgrade lifecycle to be around 5
years, but in reality it's 10-15 years.
And also the CPU options used by hosting providers are cost oriented, to
get the most performance per dollar.

Thank you,
Alex

---

## #121107 — tim@bastelstu.be — 2023-09-20T10:16:25+00:00

URL: https://externals.io/message/121004#121107 · In reply to: #121004

Hi


> in response to the recent "PASSWORD_DEFAULT value" thread [1], I've
> created an RFC to discuss an increase of the default BCrypt costs for
> `password_hash()` from the current value of 10.
> 
> https://wiki.php.net/rfc/bcrypt_cost_2023
> 
> This message is intended to officially open the discussion period for
> that RFC.

The minimum 14 days of discussion will be over tomorrow. I believe the 
RFC is clearly written, sufficiently explains possible drawbacks and 
gives enough data to make an information decision.

As such I don't expect any more meaningful discussion and plan open the 
vote shortly after the 14 days are actually over to get this off my list.

Best regards
Tim Düsterhus

---

## #121228 — steve@tobtu.com — 2023-10-05T00:18:33+00:00

URL: https://externals.io/message/121004#121228 · In reply to: #121009

> On 09/07/2023 4:37 PM CDT Craig Francis <craig@craigfrancis.co.uk> wrote:
> 
> We recently discussed hashing and costs at one of our OWASP meetings, we came to conclusion that the default of 10 for bcrypt probably should be increased, but only to 11 for typical websites. The main concern was about making denial-of-service attacks easier (think of a normal website developer, who won't limit the number of login attempts).
> 

I fought long and hard to get bcrypt down from cost 12 to only cost 10 even though cost 9 was over powered vs the other settings. At the time cost "8.1" was equivalent to the other settings. After the recent settings increase of the others, cost "8.7" is equivalent. Currently all of the OWASP settings come directly from me except bcrypt. The only way I was able to get it down to cost 10 was giving equivalent settings to cost 12 for the other algorithms. Just to show that those numbers were obviously too high. Note that bcrypt cost 12 is currently as strong as PBKDF2-HMAC-SHA256 with ~6,000,000 iterations. Which is an insane number for web auth.

Anyway I don't look forward to arguing with OWASP to keep bcrypt at cost 10. Even though it's currently 2.5x stronger than the other accepted settings.
