A less-circumventable age verification protocol: good ol' files
Daniel Tang <[email protected]> Tue, 10 Mar 2026 22:41:27 -0400
| Newsgroups | gmane.linux.xdg.devel |
|---|---|
| Message-ID | <2178991.atdPhlSkOF@daniel-desktop3> |
I would like to propose a better way to implement age verification. The
existing proposals fall short of the anti-circumvention requirements.
The New York bill at https://www.nysenate.gov/legislation/bills/2025/S8102/amendment/A says:
> 1. "age assurance" shall mean any method to reasonably determine the
> age category of a user, using methods that reasonably prevent against
> circumvention. such method may include a method that meets the require-
> ments of article forty-five of this chapter, or may be a method that is
> identified pursuant to new regulations promulgated by the attorney
> general consistent with section fifteen hundred forty-five of this arti-
> cle.
Their first proposal was https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/113 .
That was rapidly circumvented by https://github.com/HaplessIdiot/ageverificationbypass/blob/main/bypassageverification.py .
Someone else's proposal was https://github.com/flatpak/xdg-desktop-portal/pull/1922 .
Both use D-BUS. I assert that the age verification must not use D-BUS.
While the first circumvention might require root, a child could wrap it
to not require root. They could just set the DBUS_SYSTEM_BUS_ADDRESS
environment variable. Then a Python server could start listening on
that file to falsely respond to requests.
The second proposal is 535 lines of code of D-BUS verboseness. That
feels wrong. Let's go back the Unix philosophy of "everything is a file".
What I propose is a root-owned world-unwritable directory at either:
* /etc/age-verification/$UID/ , or
* ${XDG_RUNTIME_DIR:-/run/user/$UID}/ageverification/ , or
* /var/lib/ageverification/$UID/
/etc is trusted by sudo's authentication, and by ImageMagick's security
blocking PDFs.XDG_RUNTIME_DIR can be better if /etc is out of scope
for the XDG mailing list and we want dynamic updates coexisting with
ReadonlyRoot. To prevent tampering, no directory there should be empty.
Originally I considered a /etc/age/$UID or /etc/birthday/$UID file, but
that offers less privacy. For additional privacy, each directory should
have 0711 permissions, so accesses can be granular. Then there should
be exactly one of the following empty regular files:
* "under-13"
* "13-to-15"
* "16-to-17"
* "18-plus"
Only when a different jurisdiction enacts conflicting age intervals, we
can discuss whether to include both.
An absent directory indicates a system service or test environment.
Each desktop environment shall be responsible for prompting existing
users and talking to a service or setuid helper binary unless the
CI=true environment variable is set or the UID is below 1000.
libpam_systemd can be used to delay login until any dynamic directory
is created.
The helper should have PAM-like interactions to dynamically drive a UI
like KDE or GNOME's current lockscreens. I originally considered
introducing a new PAM type, but too many setups might already assume
exactly the 4 traditional account/auth/password/session set, and I
don't know whether PAM is convenient with Flatpak. Anyway, Flatpak
shall be responsible that the directory is accessible and not
overmounted inside its sandbox. The "real-time application programming
interface" in the law can be `access(2)` on the appropriate file.
Each Linux distribution shall choose what helper to run, depending
on the local laws of its target market. It can also omit the helper, and
lock in the age bracket set at install-time. If the helper stores the
birthdate, it can add-then-remove files as time goes by. The
simplicity of this file-based approach establishes clear
responsibilities between operation system code and application code.
A simple protocol will be implemented faster, and this speed is
important, as mentioned in a comment in the second proposal, for LTS
distributions.
California Assembly Bill No. 1043 prohibits sharing "the digital
signal information with a third party for a purpose not required by
[the] title". Accordingly, https://github.com/systemd/systemd/pull/40954
may violate it by storing the birthdate. I am not a lawyer, and this is
not legal advice. But decopuling the birthdate from the files and
into the helper allows different distributions to follow the differing
opinions of their different legal counsel.
The anti-circumvention should center on requiring root access.
Reinstallation is no more of a concern than factory-resetting other
OSes. Parents are responsible for not adding their children to the
sudo group and adding a GRUB password. To make the anti-circumvention
complete, other distributions could need to, like Ubuntu, restrict
unprivileged user namespaces. The security of my whole proposal rests
on the fact that without userns, getuid(2) cannot be remapped, and
/etc cannot be overmounted. sudo and ImageMagick rely on this. These
two steps provide secure attestation of the user's age range.
The other proposals rest on one weak link, namely
DBUS_SYSTEM_BUS_ADDRESS. With so many programming languages, at least
one will have an age verification library that is vulnerable to an
attack where it fails to unsetenv(3) or reject this envvar. LD_PRELOAD
attacks are different and don't matter, because it's code not data, and
injected code can turn any application into any new application, so the
some may consider the person injecting the code to be the new legal
developer.
~ daniel . z . tg