PhoneSnap is a local developer tool. This document describes its threat model so users can decide whether the wireless mode is appropriate for their network.
The wireless receiver is off on a new install. Nothing listens on the network until you turn it on, either by opening Set Up Wireless Shortcut… or with the Enable Wireless Receiver menu item. The choice persists across launches, and the same menu item turns it back off.
Installs that were already paired before this setting existed keep the receiver on, so a Shortcut already added to an iPhone continues to work.
While it is on, it binds every interface on port 8472 (override with
PHONESNAP_WIRELESS_PORT), so any device on the same LAN can reach it. A
typical home router does not forward that port, so it is not reachable from
the internet unless you have explicitly port-forwarded it.
PHONESNAP_WIRELESS_ENABLED=0 or =1 overrides the stored setting for a
single run without changing it.
The wired USB path uses Apple's ImageCaptureCore framework. It does not open a network listener and screenshots never leave the machine. If you have never turned the wireless receiver on, none of the wireless threat model below applies to you.
Protections and their limits:
- Pair ID as capability. The setup page and Shortcut download routes are gated only by knowledge of the random pair ID in the URL (9 random bytes, 12 base64url characters). The pair ID is distributed exclusively through the QR code / setup URL shown on the Mac — the listener is intentionally not advertised over Bonjour, because the generated Shortcut embeds the bearer token and anyone who can fetch it can authorize uploads.
- Bearer token. Uploads require
Authorization: Bearer <token>with a 32-byte random token, compared in constant time. Query-string tokens are not accepted. - Host binding. The setup page and the generated Shortcut point at the
host the request arrived through, but only after that host is checked
against an allowlist: the Mac's Bonjour hostname, its current IPv4 address,
localhost,127.0.0.1, and::1, each on the receiver's own port. Any otherHostheader falls back to the Bonjour hostname. Without this check, a request carrying an attacker-chosenHostwould yield a signed Shortcut that delivered the bearer token and every future screenshot to that address — a DNS-rebinding path to credential exfiltration that does not require the attacker to be on your network at all. - Authentication before buffering. Upload requests are authenticated while headers are parsed, before any request body is read into memory. An unauthenticated peer cannot make the receiver buffer a body.
- Connection limits. At most 4 concurrent sessions; a new connection may
evict one that is still waiting for its headers, so idle unauthenticated
peers cannot starve a real upload. Headers must arrive within 5 seconds and
the complete request within 30 seconds, after which the receiver responds
408and closes. The header block is capped at 64 KiB (431beyond that). - Body limits. Uploads are capped at 32 MiB (33,554,432 bytes), require a
well-formed
Content-Length(missing, duplicate, negative, or non-decimal values are rejected before a body is read), and must decode as an image before being saved.Transfer-Encodingother thanidentityis refused with501. - Decoding limits. Image dimensions are read from the file header and rejected above 50,000,000 pixels before any decode is attempted. This bounds decompression bombs, where a small compressed file expands to many gigabytes in memory. Vector and PDF content is refused rather than rasterized, and decoding is serialized so concurrent uploads cannot multiply decoder memory.
- File writes. Saved filenames are generated locally from a timestamp and never taken from the sender. Each destination is claimed with an exclusive rename, so simultaneous saves cannot silently overwrite one another and a partially written PNG is never visible at the final path.
- Logging. Pair IDs and tokens are redacted from application logs. Request paths are logged in a fixed redacted form and unrecognized methods are not echoed.
- No TLS. Traffic is plain HTTP on the local network. Anyone who can observe your LAN traffic (open Wi-Fi, hostile router) can capture the token. Do not use wireless mode on untrusted networks; the wired capture path is unaffected, though the listener still runs.
- Impact of token compromise. An attacker with the token can push images to your Mac. Uploaded images are written to the save folder and copied to the clipboard, so treat a compromised token as a clipboard-injection risk and rotate the credentials as described below.
- Signing route resource use. The Shortcut download route spawns a
/usr/bin/shortcuts signsubprocess and is gated only by the pair ID. Only one signing run is permitted at a time — concurrent requests receive503withRetry-After— and each run is capped by a 30-second timeout with a 40-second deadline on the surrounding request. Signed bytes are cached per upload URL, token, and batch size, so repeated downloads do not respawn subprocesses. - Credential storage. The pair ID and token persist in
UserDefaults(not the Keychain). They are readable by any process running as your user — the same trust level as the screenshots folder itself.
- An attacker who can read your LAN traffic. There is no TLS; see above.
- An attacker who obtains the pair ID. It is the only gate on the setup and Shortcut download routes. Rotate if you believe it has leaked.
- Any process running as your user. It can read the credentials out of
UserDefaultsand the screenshots out of the save folder. - Malicious image content itself. PhoneSnap bounds decode size, but decoding is still performed by the system image frameworks.
Choose Rotate Pairing… from the menu bar icon and confirm. A new pair ID and token are generated immediately, and the setup window shows the new QR code. Every Shortcut already added to an iPhone stops working and must be set up again from that code.
Rotate if you think the setup link, QR code, or token has been seen by anyone else.
The same thing can be done by hand — delete the stored values and relaunch:
defaults delete dev.phonesnap.PhoneSnap PhoneSnapWirelessPairID 2>/dev/null
defaults delete dev.phonesnap.PhoneSnap PhoneSnapWirelessToken 2>/dev/nullWhen running the unbundled binary (swift run PhoneSnap), the defaults domain
is the executable name instead — remove the matching
PhoneSnapWirelessPairID / PhoneSnapWirelessToken keys from PhoneSnap.
Please do not open a public issue for security-sensitive reports.
Use GitHub private vulnerability reporting:
https://github.com/Aqu1bp/PhoneSnap/security/advisories/new
Expected response:
- Acknowledgement within 7 days.
- Status update within 30 days, even if the fix is still in progress.
- Public disclosure after a fix is available, unless the reporter and maintainer agree that a different timeline is safer.
If GitHub's private reporting flow is unavailable, contact the maintainer through their GitHub profile and include only enough detail to establish a private channel.