valorin,
@valorin@phpc.social avatar

Overly complex code is a common source of security issues, one I often see when devs attempt to avoid filename collisions…
https://securinglaravel.com/p/security-tip-avoiding-filename-collisions
#PHP #Laravel

michael,
@michael@thms.uk avatar

@valorin this is interesting. My personal go to is usually Str::uuid() to get a UUID.

Essentially just even more randomness than you suggest, isn’t it? Or is there any downside?

valorin,
@valorin@phpc.social avatar

@michael Yep, UUID's will work too. I always forget about them (imho they are over-hyped and over-used).

Ultimately (v4) UUIDs they are just formatted randomness, which is the point I'm trying to make.

michael,
@michael@thms.uk avatar

@valorin yes, understood. Personally I like uuid, because I then don’t need to think about how many characters I need, but in reality one probably rarely needs the entropy of uuid for this purpose. (And I’m paranoid, so have a unique constraint on the DB too.)

I’m glad to see some validation that my thinking is good though 😊

timwolla,
@timwolla@phpc.social avatar

@valorin @michael A UUIDv4 has 122 Bits of randomness, the proposed Str::random(16) has floor(log_2 (62^16)) = 95 Bits of randomness.

95 Bits are probably fine for the vast majority of applications, but AFAIK is not yet a size that is considered to be collision free in practice (a 10^-6 chance of collisions happens with just 2^38 generated values).

timwolla,
@timwolla@phpc.social avatar

@valorin @michael My recommendation is bin2hex(random_bytes(16)), giving you example 128 Bits of randomness. 10^-6 chance of collision after 2^54 values - and equivalent to an AES-128 key which is considered to be unguessably secure today.

valorin,
@valorin@phpc.social avatar

@timwolla @michael Awesome, thanks for the correction! I shall update my article. 😁

I went for 16 purely because it matched the existing length in my example, but you're right that I should've used a longer one.
What would be the equivalent length for Str::random() to match 128 bits?

timwolla,
@timwolla@phpc.social avatar

@valorin @michael It currently uses (a simplified) b64 alphabet. b64 increases the size of the output by 33% (and inversely reduces the entropy by 25% for a given length). Thus you want a length of 16 * 4/3 = 22.

However:

timwolla,
@timwolla@phpc.social avatar

@valorin @michael The function's contract does not guarantee the use of a specific character set. It just claims the output is an alpha-numeric string. Nothing prevents the function from being changed to use a smaller alphabet in the future.

In fact it already supports a swappable implementation using Str::createRandomStringsUsing().

timwolla,
@timwolla@phpc.social avatar

@valorin @michael Depending on the use-case you might also be concerned about timing attacks that leak information about the generated string. I don't really trust the implementation to not leak anything, because it is highly non-trivial with the str_replace() and the while() loop in there.

In fact even bin2hex() is not completely safe, because it is implemented as a table lookup, the speed of which depends on the state of the CPU caches.

timwolla,
@timwolla@phpc.social avatar

@valorin @michael Unless you have the need for best performance, I recommend the Hex encoder from https://github.com/paragonie/constant_time_encoding. The CTE generally is "fast enough" and thus the secure default choice.

timwolla,
@timwolla@phpc.social avatar

@valorin @michael With PHP 8.3 you will also be able to use \Random\Randomizer::getBytesFromString() together with the \Random\Engine\Secure for a custom alphabet (Caveat: That method is affected by cache timing attacks and for a non-power-of-two-alphabet it requires the use of rejection sampling).

valorin,
@valorin@phpc.social avatar

@timwolla @michael Awesome, thanks for taking the time to explain all of that. 🙂

In the use case I'm talking about in the article, I don't think timing attacks are an issue, but I'll keep it mind for future.

We've plans to build support for PHP 8.3's Randomizer into Laravel, so we can replace that existing Str::random() with something stronger in the process too.

timwolla,
@timwolla@phpc.social avatar

@valorin @michael Agreed wrt timing attacks, but the not-well-defined behavior of Str::random() is problematic in practice with regard to calculating the safety.

bin2hex(random_bytes(16)) is never going to change its behavior with regard to statistical safety: It gives you 128 Bits of randomness and then transforms it into a printable representation.

valorin,
@valorin@phpc.social avatar

@timwolla Yes, there is definitely an implication of trust involved to use it, but that's implied with using a framework like Laravel that wraps and implements so much for you anyway, so I think the trade-off is acceptable for most users.

In my understanding, timing attacks are pretty specific and need to be targeted. Have there been any good examples of real attacks abusing this sort of vuln?
It feels like the issues with rand(), which can be exploited but only in very specific circumstances?

timwolla,
@timwolla@phpc.social avatar

@valorin IIRC timing attacks have been proven to be feasible over the Internet. Other than that, it depends on your threat model (as always with security). The cache timing leak that the constant-time-encoder protects against is probably very obscure, especially since the table for hex encoding should find into a single cache-line, but on the other hand just using the library is easy enough to do.

j3j5,
@j3j5@hachyderm.io avatar

@timwolla @valorin This is super interesting, thanks for this conversation, just to add for other readers, Laravel provides the Timebox class (which I learned about thanks to @valorin's awesome newsletter) which allows you to defend yourself from timing attacks when using operations that can be affected by it.

https://securinglaravel.com/p/security-tip-timebox-for-timing-attacks

timwolla,
@timwolla@phpc.social avatar

@j3j5 @valorin @valorin The Timebox is a band-aid at best:

  1. It adds a fixed overhead, for fast operations, such as hex encoding 16 bytes the overhead can be significant if the operation is performed repeatedly.
timwolla,
@timwolla@phpc.social avatar

@j3j5 @valorin @valorin

  1. It is still possible for it to leak timing information, because when the server waits for the timebox to expire, the request in question will sleep, thus not taking up CPU time, making more CPU time available to other requests.

By observing how long concurrent requests with a “constant runtime” take to execute, it can be determined whether the request in question is currently performing useful work (e.g. checking a hash) or whether it's sleeping.

timwolla,
@timwolla@phpc.social avatar

@j3j5 @valorin @valorin

The only reliable protection against timing attacks is using operations that are inherently constant time based on their construction. hash_equals() would be an example that is included in PHP itself. You should always use hash_equals() to compare strings that are secret / sensitive.

j3j5,
@j3j5@hachyderm.io avatar

@timwolla @valorin thanks! This only confirms my suspicions, teaching sand math was a mistake 😅

valorin,
@valorin@phpc.social avatar

@j3j5 @timwolla
This stuff always reminds me that the security industry is nuts, and there is always some other way to bypass or leak something, and it's so hard to keep across everything... and yet so much fun to be involved in.

  • All
  • Subscribed
  • Moderated
  • Favorites
  • php
  • ngwrru68w68
  • rosin
  • GTA5RPClips
  • osvaldo12
  • love
  • Youngstown
  • slotface
  • khanakhh
  • everett
  • kavyap
  • mdbf
  • DreamBathrooms
  • thenastyranch
  • magazineikmin
  • megavids
  • InstantRegret
  • tester
  • tacticalgear
  • ethstaker
  • normalnudes
  • cisconetworking
  • cubers
  • Durango
  • Leos
  • provamag3
  • modclub
  • anitta
  • JUstTest
  • All magazines