base64url format

php.internals

Sara Golemon

3 years ago
I've been working with JWTs lately and that means working with Base64URL format. (Ref: https://www.rfc-editor.org/rfc/rfc4648#section-5 ) This is essentially the same thing as normal Base64, but instead of '+' and '/', it uses '-' and '_', respectively. It also allows leaving off the training '=' padding characters. So far, I've just been including polyfills like this: function base64url_decode(string $str): string { return base64_decode(str_pad(strtr($str, '-_', '+/'), (4 - (strlen($str) % 4)) % 4, '=')); } function base64_encode(string $str): string { return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); } These work fine, but they create a LOT of string copies along the way which shouldn't be necessary. Would anyone mind if skipped RFC and just added `base64url_encode()` and `base64url_decode()` to PHP 8.3? Can hold a vote if anyone objects, but this seems fairly non-controversial. -Sara

Christoph Becker

3 years ago
On 09.01.2023 at 19:49, Sara Golemon wrote:
> I've been working with JWTs lately and that means working with Base64URL > format. (Ref: https://www.rfc-editor.org/rfc/rfc4648#section-5 ) > This is essentially the same thing as normal Base64, but instead of '+' and > '/', it uses '-' and '_', respectively. It also allows leaving off the > training '=' padding characters. > > So far, I've just been including polyfills like this: > > function base64url_decode(string $str): string { > return base64_decode(str_pad(strtr($str, '-_', '+/'), (4 - > (strlen($str) % 4)) % 4, '=')); > } > > function base64_encode(string $str): string { > return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); > } > > These work fine, but they create a LOT of string copies along the way which > shouldn't be necessary. > Would anyone mind if skipped RFC and just added `base64url_encode()` and > `base64url_decode()` to PHP 8.3? > > Can hold a vote if anyone objects, but this seems fairly non-controversial.
IMO, go with a PR first. If that is controversial, an RFC can still be done. I, personally, would be more interested in base32, but that's another story.
-- Christoph M. Becker

Unnamed Person

3 years ago
I'm in support of such a feature, but would strongly advocate for an additional parameter to flag whether or not to include the trailing `=` pad. The trailing pad is optional according to RFC 4648, so I think leaving it off by default would be the ideal use case, but an optional `include_padding` flag or something along those lines would be helpful. On 1/9/23 10:49 AM, Sara Golemon wrote:
> I've been working with JWTs lately and that means working with Base64URL > format. (Ref:https://www.rfc-editor.org/rfc/rfc4648#section-5 ) > This is essentially the same thing as normal Base64, but instead of '+' and > '/', it uses '-' and '_', respectively. It also allows leaving off the > training '=' padding characters. > > So far, I've just been including polyfills like this: > > function base64url_decode(string $str): string { > return base64_decode(str_pad(strtr($str, '-_', '+/'), (4 - > (strlen($str) % 4)) % 4, '=')); > } > > function base64_encode(string $str): string { > return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); > } > > These work fine, but they create a LOT of string copies along the way which > shouldn't be necessary. > Would anyone mind if skipped RFC and just added `base64url_encode()` and > `base64url_decode()` to PHP 8.3? > > Can hold a vote if anyone objects, but this seems fairly non-controversial. > > -Sara >
-- Security Principles for PHP Applications <https://www.phparch.com/books/security-principles-for-php-applications/> *Eric Mann * Tekton *PGP:*0x63F15A9B715376CA <https://keybase.io/eamann> *P:*503.925.6266 *E:*eric@eamann.com eamann.com <https://eamann.com> ttmm.io <https://ttmm.io> Twitter icon <https://twitter.com/ericmann> LinkedIn icon <https://www.linkedin.com/in/ericallenmann/>

Tim Düsterhus

3 years ago
Hi On 1/9/23 19:49, Sara Golemon wrote:
> I've been working with JWTs lately and that means working with Base64URL > format. (Ref: https://www.rfc-editor.org/rfc/rfc4648#section-5 ) > This is essentially the same thing as normal Base64, but instead of '+' and > '/', it uses '-' and '_', respectively. It also allows leaving off the > training '=' padding characters. >
With JWTs you likely also want a constant time encoder that is not susceptible for cache-timing leaks [1]. For this reason https://github.com/paragonie/constant_time_encoding is a most-have dependency for my projects and I generally use the functions of that library by default, unless there is a reason not to (high performance required). That library also includes a b32 implementation that cmb wished. There's also https://www.php.net/manual/en/function.sodium-bin2base64.php which is constant-time and supports b64url, unfortunately it's not guaranteed to be available. Best regards Tim Düsterhus [1] It's likely more important for encrypted tokens, than only for signed ones.

Hans Henrik Bergan

3 years ago
when http_build_query() wanted to support different encoding schemes, PHP_QUERY_RFC1738 and PHP_QUERY_RFC3986 was made, instead of creating http_build_query_rfc1738() and http_build_query_rfc3986() , hmm On Mon, 9 Jan 2023 at 21:12, Tim Düsterhus <tim@bastelstu.be> wrote:

Derick Rethans

3 years ago
On 9 January 2023 18:49:28 GMT, Sara Golemon <pollita@php.net> wrote:
>I've been working with JWTs lately and that means working with Base64URL >format. (Ref: https://www.rfc-editor.org/rfc/rfc4648#section-5 ) >This is essentially the same thing as normal Base64, but instead of '+' and >'/', it uses '-' and '_', respectively. It also allows leaving off the >training '=' padding characters. > >So far, I've just been including polyfills like this: > >function base64url_decode(string $str): string { > return base64_decode(str_pad(strtr($str, '-_', '+/'), (4 - >(strlen($str) % 4)) % 4, '=')); >} > >function base64_encode(string $str): string { > return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); >} > >These work fine, but they create a LOT of string copies along the way which >shouldn't be necessary. >Would anyone mind if skipped RFC and just added `base64url_encode()` and >`base64url_decode()` to PHP 8.3?
Should these be new functions, or options to base64_encode instead? I'd guess base64_decode could just accept both? cheers Derick

David Gebler

3 years ago
On Mon, Jan 9, 2023 at 9:42 PM Derick Rethans <derick@derickrethans.nl> wrote:
> On 9 January 2023 18:49:28 GMT, Sara Golemon <pollita@php.net> wrote: > >I've been working with JWTs lately and that means working with Base64URL > >format. (Ref: https://www.rfc-editor.org/rfc/rfc4648#section-5 ) > >This is essentially the same thing as normal Base64, but instead of '+' > and > >'/', it uses '-' and '_', respectively. It also allows leaving off the > >training '=' padding characters. > > > >So far, I've just been including polyfills like this: > > > >function base64url_decode(string $str): string { > > return base64_decode(str_pad(strtr($str, '-_', '+/'), (4 - > >(strlen($str) % 4)) % 4, '=')); > >} > > > >function base64_encode(string $str): string { > > return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); > >} > > > >These work fine, but they create a LOT of string copies along the way > which > >shouldn't be necessary. > >Would anyone mind if skipped RFC and just added `base64url_encode()` and > >`base64url_decode()` to PHP 8.3? > > Should these be new functions, or options to base64_encode instead? I'd > guess base64_decode could just accept both?
I think from a UX/DX perspective, separate functions would be my preference, base64_url_encode and base64_url_decode (extra underscore which I feel is more consistent with PHP stock library). One consideration though is that base64_urlencode or base64_url_encode are function names which are likely already defined by a number of userland projects or libraries, since it's a very common thing to do with the prevalence of JWTs, so if the RFC process is being bypassed in this case, a new optional parameter to base64_encode might be better. But I think it would be weird to have base64_encode(bool $urlEncode = false) or something, which is presumably what it would look like. Dare I float the suggestion of a Base64 class, making base64_encode and base64_decode functions aliases for Base64::encode() and Base64::decode() respectively, then new Base64::urlEncode() and urlDecode() methods?

Hans Henrik Bergan

3 years ago
how about base64_encode(string $string, int $flags = 0): string with $flags accepting BASE64_RFC4648 and BASE64_NO_PADDING or something to that effect? On Mon, 9 Jan 2023 at 22:56, David Gebler <davidgebler@gmail.com> wrote:

Larry Garfield

3 years ago
On Mon, Jan 9, 2023, at 12:49 PM, Sara Golemon wrote:
> I've been working with JWTs lately and that means working with Base64URL > format. (Ref: https://www.rfc-editor.org/rfc/rfc4648#section-5 ) > This is essentially the same thing as normal Base64, but instead of '+' and > '/', it uses '-' and '_', respectively. It also allows leaving off the > training '=' padding characters. > > So far, I've just been including polyfills like this: > > function base64url_decode(string $str): string { > return base64_decode(str_pad(strtr($str, '-_', '+/'), (4 - > (strlen($str) % 4)) % 4, '=')); > } > > function base64_encode(string $str): string { > return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); > } > > These work fine, but they create a LOT of string copies along the way which > shouldn't be necessary. > Would anyone mind if skipped RFC and just added `base64url_encode()` and > `base64url_decode()` to PHP 8.3? > > Can hold a vote if anyone objects, but this seems fairly non-controversial. > > -Sara
It sounds to me like there's enough discussion to be had on the How to warrant a formal RFC. I get the sense once the bikeshedding is done it will pass, but it's going to need the discussion/review. --Larry Garfield

Sara Golemon

3 years ago
On Tue, Jan 10, 2023 at 8:44 AM Larry Garfield <larry@garfieldtech.com> wrote:
> > > Can hold a vote if anyone objects, but this seems fairly
non-controversial.
> > > > It sounds to me like there's enough discussion to be had on the How to
warrant a formal RFC. I get the sense once the bikeshedding is done it will pass, but it's going to need the discussion/review.
>
Yep, that's my reading at this point to. Expect an RFC to be inbound with the initial feedback integrated (e.g. new funcs vs flags). -Sara