# base64url format

- Thread: https://externals.io/message/119243
- Original archive: https://news-web.php.net/php.internals/119243
- Started by Sara Golemon on 2023-01-09T18:49:28+00:00
- Messages: 10
- 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

---

## #119243 — Sara Golemon — 2023-01-09T18:49:28+00:00

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

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

---

## #119244 — Christoph M. Becker — 2023-01-09T18:55:36+00:00

URL: https://externals.io/message/119243#119244 · In reply to: #119243

> 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

---

## #119246 — Eric Mann via internals — 2023-01-09T19:39:08+00:00

URL: https://externals.io/message/119243#119246 · In reply to: #119243

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.


> 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/

---

## #119247 — tim@bastelstu.be — 2023-01-09T20:11:54+00:00

URL: https://externals.io/message/119243#119247 · In reply to: #119243

Hi


> 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.

---

## #119248 — Hans Henrik Bergan — 2023-01-09T21:12:43+00:00

URL: https://externals.io/message/119243#119248 · In reply to: #119247

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


>
> Hi
>
> 
> > […]
>
> 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.
>
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php

---

## #119249 — Derick Rethans — 2023-01-09T21:42:24+00:00

URL: https://externals.io/message/119243#119249 · In reply to: #119243

>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

---

## #119250 — David Gebler — 2023-01-09T21:56:15+00:00

URL: https://externals.io/message/119243#119250 · In reply to: #119249

On Mon, Jan 9, 2023 at 9:42 PM Derick Rethans <derick@derickrethans.nl>
wrote:

> 
> > […]
> and
> > […]
> which
> > […]
>
> 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?

---

## #119252 — Hans Henrik Bergan — 2023-01-10T12:01:40+00:00

URL: https://externals.io/message/119243#119252 · In reply to: #119250

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, Jan 9, 2023 at 9:42 PM Derick Rethans <derick@derickrethans.nl>
> wrote:
>
> > […]
>
>
> 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?

---

## #119253 — Larry Garfield — 2023-01-10T14:43:39+00:00

URL: https://externals.io/message/119243#119253 · In reply to: #119243

> 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

---

## #119264 — Sara Golemon — 2023-01-11T18:12:45+00:00

URL: https://externals.io/message/119243#119264 · In reply to: #119253

On Tue, Jan 10, 2023 at 8:44 AM Larry Garfield <larry@garfieldtech.com>
wrote:
>
> > […]
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
