Something went wrong. Try again.
A lightweight proxy service, allowing you to use old Twitter clients with Bluesky
Something went wrong. Try again.
4.4 kB
Markdown
at main
Security in detail #
This page is for people who want to know exactly what protects the bridge and its host.
- App passwords only. After signing in, mockingbird inspects the scope of the Bluesky session. Anything other than an app password (
com.atproto.appPassorcom.atproto.appPassPrivileged) is cancelled at Bluesky straight away, and the sign-in is refused. - No stored passwords. Username-and-password sessions are looked up by an HMAC-SHA256 of the handle and password (keyed with the server secret). The password is held in memory only for the request that uses it. Sign-in is only repeated when there is no cached session, since Bluesky limits how often you can sign in.
- Encrypted tokens. Bluesky access and refresh tokens are sealed with AES-256-GCM, bound to their database row, and refreshed automatically. If a session is cancelled at Bluesky, the app gets Twitter's usual "not authorised" reply.
- Guessing protection. Failed sign-ins are limited per address (10, then a lock-out that doubles from one minute to one hour) and per account (5, then two minutes doubling to an hour). Someone else guessing your password never locks out a session you already have.
- OAuth trade-off. Old apps come with their own OAuth keys, which mockingbird cannot know. So the OAuth token mockingbird issues (256 random bits, prefixed with your user number as Twitter did) works like a password on its own: whoever holds it can use the account until it is cancelled. Only a hash of it is stored. If you know an app's OAuth secret, add it to
MB_OAUTH_CONSUMERSand mockingbird will then check every signature, timestamp and one-time value from that app. The signing code is tested against the published RFC 5849 and Twitter examples. - Sign-in links can't be turned against you. Anyone can start an OAuth sign-in and choose where the browser goes afterwards, so a link from a stranger could otherwise hand them your account. The sign-in page shows that destination before you type anything. For apps mockingbird cannot verify, it never redirects to a website automatically: after signing in you see a warning and must tap Continue yourself. Apps that use their own URL scheme (
myapp://) or a PIN work as before, and apps listed inMB_OAUTH_CONSUMERSare redirected directly. Each sign-in link works once, and three wrong PINs cancel it. - No reaching into your network (SSRF protection). People signing in choose the addresses mockingbird fetches from (their handle's website, their Bluesky server). Every outgoing connection is checked at the moment it connects. Connections to the machine itself, private and home networks (RFC 1918), link-local, carrier-grade NAT and Tailscale (100.64.0.0/10), IPv6 local ranges, multicast, and the tricks that smuggle those through IPv6 are all refused. Only HTTPS is allowed; redirects, response sizes and time are all capped. Tests cover DNS rebinding and redirect tricks.
- Network sandbox (Cloudflare set-up). In
deploy/pi, a separate nftables table, loaded before Docker starts, drops any new connection from the bridge's container network to private, LAN, Tailscale and loopback addresses, and to the host itself. The bridge's port is published on127.0.0.1only. The container runs read-only, as a non-root user, with no Linux capabilities andno-new-privileges. - Limits. There are rate limits per address and per account, request bodies are limited to 64 KB (5 MB for photo uploads), and every step has a timeout.
- Safe output. XML is escaped and stripped of characters that would break old parsers. JSONP callback names are validated, and JSONP is only served to requests without credentials, so a website can't read your timeline through a browser that remembers your password. Web pages use Go's
html/templateand a strict Content Security Policy. All database queries are parameterised, and secrets are compared in constant time. - Privacy-respecting post pages. The public
/p/pages won't show posts from people who have asked Bluesky not to show their posts to signed-out visitors. - Quiet logs. Passwords, tokens,
Authorizationheaders and request bodies are never logged. A filter also scrubs anything that looks like a credential, including in crash reports. - Supply chain. Go dependencies are pinned in
go.sum, and base images and GitHub Actions are pinned by digest. CI runsgovulncheckon every change.