Skip to content

Security

Ninaivu is meant to run on a home network, or behind a private network such as Tailscale or WireGuard. Do not forward router ports to it.

Reporting

Report a vulnerability privately through GitHub's "Report a vulnerability" button on the Security tab of the repository, or by email to the address in the repository's profile. Please do not open a public issue for a security problem. You will get an acknowledgement within a few days and a fix or a plan before anything is published.

What is in scope

  • Anything that lets one role see or change what another role may not.
  • Anything that lets a share link reach beyond what its maker could see.
  • Anything that sends a photograph, a name or a location off the machine without the administrator turning that on.
  • Anything that can modify or delete a source photograph.

Design notes

Sessions are HttpOnly cookies bound to one of the two apps. PIN and password attempts are rate-limited per address and per profile, and wrong administrator passwords pause sign-in by username for longer each time. The admin console is bound to localhost by default.

Every request is judged as coming from home (the home network, this computer, Tailscale, WireGuard) or from the internet (a public address, a forwarded port, a proxy or tunnel not named in trusted_proxies, Tailscale Funnel). From the internet, profiles without a PIN do not open, nobody browses without signing in, plain HTTP is refused, and the console refuses every request unless console_from_internet is turned on.

In a container the judgement has less to go on. Docker publishes a port by forwarding it — Docker Desktop on Mac and Windows, the userland proxy, IPv6 publishing — and every connection then reaches Ninaivu from Docker's own gateway address (172.17.0.1, 192.168.65.1), the phone on the Wi-Fi and a visitor through a forwarded router port alike. So inside a container Ninaivu treats every such connection as the internet: profiles need a PIN, and nobody browses without signing in. For home and the internet to be told apart again, put a reverse proxy in front (installers/caddy, installers/nginx) that passes the real address, set trusted_proxies to 1, and — because the proxy also reaches the container from the gateway — list that address or network in NINAIVU_TRUSTED_PROXY_ADDRESSES. The console is still reached from the host, which is all the shipped docker-compose.yml publishes it to; keep it that way. The plain-HTTP refusal goes by the address alone and cannot see through the forwarder, so never forward a router port to the image's port 5000.

The state backup holds the TLS CA key, the cloud encryption key and Google sign-in, and the SMTP password in config.json (session tokens are stored hashed, so the copy in it opens nothing). Keep it on the machine or encrypt it if it moves.

Plain HTTP

Some ways of running Ninaivu serve the family app over plain HTTP: the Linux installer's service, python -m ninaivu without --https, the Docker image (port 5000) and installers/windows/install-service.ps1 (port 5000). On plain HTTP, passwords, PINs, session cookies and the photographs themselves cross the home network unencrypted, and anyone on the same network (a guest on the Wi-Fi, a compromised device) can read them or take over a session. The launchers serve plain HTTP by default since 1.10.9 (an installation that already served HTTPS keeps it). Turn on the HTTPS switch on the console's Server page, start with --https once, use a reverse proxy with HTTPS (installers/caddy, installers/nginx), or reach Ninaivu only over a private network such as Tailscale or WireGuard. Never forward a router port to a plain-HTTP Ninaivu; since 1.0.5 Ninaivu refuses plain HTTP from a public address in any case.

Checking a download

Each release has a SHA256SUMS.txt beside the downloads, and a *-packages.txt for each one listing every package inside it with its own SHA-256. To check a download, in the folder it was downloaded to:

sha256sum -c --ignore-missing SHA256SUMS.txt                   # Linux
shasum -a 256 Ninaivu-<version>-macos-arm64-portable.zip        # macOS: compare with its line
Get-FileHash .\Ninaivu-<version>-windows-x64-portable.zip -Algorithm SHA256   # Windows: compare with its line

The checksums come from the same release page as the downloads: they show that a download is complete and is the file the release published, not that the release itself is genuine. A signed Ninaivu.exe or Ninaivu.app (the release notes say whether they are) is the stronger check: Windows shows the publisher, and macOS opens a notarised app without a warning. The release workflow publishes only from a commit whose tests passed, and refuses to publish over a release that already exists.