Title: PHP8, from a security point of view
Date: 2020-12-04 16:00

PHP8 was [released]( https://www.php.net/releases/8.0/en.php ) on the
26<sup>th</sup> of November 2020. It brought a lot of interesting things,
security-wise, but also showcases a couple of (minor) missed opportunities in my
opinion.

## Type safety

I'm a big fan on relying on typing to ensure security properties,
like [Trusted types](https://web.dev/trusted-types/) in javascript: it
shouldn't compile if it's not secure.

PHP8 won't try to cast string into numbers anymore, thanks to the 
[Saner string to number comparisons RFC]( https://wiki.php.net/rfc/string_to_number_comparison),
meaning that collision with hashes starting with `0e` and the likes are finally
a thing of the past! This is a subset of Snuffleupagus' [sloppy comparison
prevention](https://snuffleupagus.readthedocs.io/features.html#preventing-sloppy-comparisons) feature.

The [Consistent type errors for internal functions RFC]( https://wiki.php.net/rfc/consistent_type_errors)
will prevent things like `0 == strcmp($_GET['username'], $password)` bypasses,
since `strcmp` won't return `null` and spit a warning any longer,
but will throw a proper exception instead. This was also a [nice opportunity](https://externals.io/message/106522)
for PHP to add annotations for functions parameters and return types.

The [Stricter type checks for arithmetic/bitwise operators]( https://wiki.php.net/rfc/arithmetic_operator_type_checks)
and [PHP RFC: Reclassifying engine warnings](https://wiki.php.net/rfc/engine_warnings) RFC
are in the same spirit.


## JIT

PHP8 comes with a [JIT](https://wiki.php.net/rfc/jit) based on
[DynASM](https://luajit.org/dynasm.html), bringing an RWX memory space into
PHP's memory space, into a shared allocation, meaning that its offset won't
change between different PHP8+ processes.

Moreover, DynASM isn't designed with processing/compilation/execution of
untrusted code in mind, and doesn't do things like constants blinding and
advanced folding to mitigate against spraying, nor random padding/nop
insertion, nor ensuring that the memory region is never both writeable *and*
executable to prevent direct code injection. This means that it's now way
easier to gain native code execution when exploiting memory corruptions,
albeit to be fair, most attackers are happy with a php code execution,
and won't push further.

Having a JIT comes with a lot of code complexity and maintenance burden. I'll
be without doubt a [great source of
bugs](https://bugs.php.net/search.php?cmd=display&order_by=ts1&direction=DESC&limit=30&package_name[]=JIT),
for a minor speed improvement on real-life workloads.

## Cryptography 

- `password_hash` now automatically generates a salt, accepting a
	user-provided one is deprecated.
- `crypt` will now fail instead of silently falling back to
	[DES](https://en.wikipedia.org/wiki/Crypt_(C)#Traditional_DES-based_scheme)
	when an unknown salt format was provided. The parameter is also made
	mandatory, hashing without a salt is now unsupported.
- [RFC 5652](https://tools.ietf.org/html/rfc5652) is now exposed via the OpenSSL extension.


## Misc

- The [error control operator](https://www.php.net/manual/en/language.operators.errorcontrol.php), aka `@`
	won't silence fatal errors anymore, meaning that poorly written webshells will have more chances
	to leave traces in your logs.
- `libxml_disable_entity_loader` is now deprecated, even if it's not (yet) reflected in php's documentation.
	This is acceptable since PHP8 now requires at least libxml 2.9.0,
	which comes with external entity loading disabled by default.
- Access to undefined constants will throw an error, instead of being silently
  interpreted as a string, no more `SALT` being silently converted to `"SALT"`.
- `create_function` was [removed](https://github.com/php/php-src/commit/ee16d99504f0014c3d292809da927fb622293f41), closing its infamous code injection vector.
- `array_key_exists` throws when passed an array/object, instead of silently
  doing nonsense.
- The `e` modifier in `mb_ereg_replace` has been removed.
- Metadata associated with a phar will
  [no longer be unserialized](https://wiki.php.net/rfc/phar_stop_autoloading_metadata),
	killing a low-hanging
	[RCE vector](https://github.com/s-n-t/presentations/blob/master/us-18-Thomas-It's-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It.pdf).
- `FILTER_SANITIZE_MAGIC_QUOTES`, `get_magic_quotes_gpc` and `get_magic_quotes_runtime` have been removed,
  people will now have to do proper sanitization instead.
- As usual, a couple of memory safety issues were fixed,
	[some](https://bugs.php.net/bug.php?id=80242)
	[exploitable](https://bugs.php.net/bug.php?id=76618).
- [Snuffleupagus](https://github.com/jvoisin/snuffleupagus) is currently being
	ported to php8!

## Missed opportunities

[Undefined
variables](https://wiki.php.net/rfc/engine_warnings#undefined_variable), as
opposed to constants, are still not an error, meaning that things like `solt`
instead of `salt` might (and will) go unnoticed.

Converting an `Array` to a string will only yield a `Warning` instead of an
error, albeit that now that `__toString` [can *finally*
throw](https://wiki.php.net/rfc/tostring_exceptions), it might hopefully change
in the near future.

Albeit significant [CSPRNG](https://wiki.php.net/rfc/easy_userland_csprng)
[improvements](https://wiki.php.net/rfc/random-function-exceptions) have been
merged in PHP7, PHP8 didn't seize the opportunity to keep the momentum and to
aliases `rand` and `mt_rand` to `random_int`, like
[Snuffleupagus](https://snuffleupagus.readthedocs.io/features.html#weak-prng-via-rand-mt-rand) is doing.

An other missed opportunity in my opinion is that there is [still no
way](https://bugs.php.net/bug.php?id=50715) to disable some [PHP's wrappers](
https://www.php.net/manual/en/wrappers.php ), except via
[`stream_wrapper_unregister`](https://www.php.net/manual/en/function.stream-wrapper-unregister.php)
but this can be reversed with
[`stream_wrapper_restore`](https://www.php.net/manual/en/function.stream-wrapper-restore.php).
Wrappers are scary: the main use I've seen for the [`filter://`
one](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.filter) is
exfiltrating data via `
php://filter/convert.base64-encode/resource=/some/file`, and is a [decent
amount of](https://www.netsparker.com/blog/web-security/php-stream-wrappers/)
[arcane](https://lightless.me/archives/include-file-from-zip-or-phar.html)
[stuff]( https://gynvael.coldwind.pl/?lang=en&id=671 ) lurking in the shadows.
Providing a way to reduce this attack surface (like making streams opt-in)
would be welcome.
