Repository audited: https://github.com/syncthing/syncthing
Date: 2026-07-08
Methodology: Full file-by-file review of the source tree by four parallel read-only security auditor agents, each assigned a balanced batch of files (~599 source files total across cmd/, internal/, lib/, gui/, meta/, script/, test/, and root build files). Each agent read every file in its batch and looked for backdoors, data exfiltration, and suspicious security weaknesses in the context of the project as a whole.
No backdoors, data exfiltration, or deliberately weakened security were found.
No CRITICAL or HIGH severity issues were identified in any of the four batches. There is no evidence of:
- Hardcoded credentials, private keys, or magic auth tokens in production code
- Hidden authentication bypasses or kill switches
- Data exfiltration to undocumented / attacker-controlled endpoints
- Telemetry beyond the documented opt-in usage report (verified: the usage report sends only aggregated counts and feature flags — no file names, paths, contents, device IDs, certs, or private keys)
- Obfuscated payloads, base64-encoded hidden destinations, or runtime-decoded strings
- Time-bombed behavior or supply-chain fetch-execute patterns
- Deliberately weakened crypto, hardcoded IVs/keys, or broken randomness for security-sensitive operations
- Upgrade signature verification bypass (the embedded key is a public ECDSA P-521 key used only for verification; signed digests bind archive name + binary contents)
The findings below are all defense-in-depth hardening gaps or documented opt-in features — none rise to the level of a backdoor or suspected exfiltration that the user asked us to prioritize.
| Batch | Files | Coverage | Result |
|---|---|---|---|
| 1 | 148 | cmd/ (syncthing main, cli, stdiscosrv, strelaysrv, infra services), internal/ (db, sqlite, gen, slogutil), meta/, script/, root build files |
Clean (1 MEDIUM, 1 LOW, 2 INFO) |
| 2 | 126 | First half of lib/: api (auth, CSRF, tokens, support bundle), config, connections (TCP/QUIC/relay), dialer, discover, events, fs (basicfs/watch/xattr) |
Clean (1 LOW, 2 INFO) |
| 3 | 131 | Middle of lib/: fs (casefs/folding/walkfs/tempname), geoip, httpcache, ignore, locations, model (core sync engine), nat, netutil, osutil, pmp, start of protocol |
Clean (no findings) |
| 4 | 194 | Rest of lib/: protocol (encryption, wireformat, deviceid), rand, relay, scanner, signature, syncthing, tlsutil, upgrade (signing/verification), upnp, ur (usage/crash reporting), versioner (incl. external), GUI JS app, test/ |
Clean (1 LOW, 3 INFO) |
- Location:
cmd/infra/strelaypoolsrv/stats.go:146-178(withstatusAddrsourced from the registered relay's query string inmain.go:147) - Description:
fetchStatsissues an HTTP GET to"http://" + net.JoinHostPort(statusHost, statusPort) + "/status", wherestatusAddris taken directly from the relay operator's registration query string. The host is not validated against loopback, private, or link-local ranges. - Impact: A malicious or compromised relay operator can set
statusAddrto an internal target (e.g.169.254.169.254:80cloud metadata endpoint,127.0.0.1:port, or any internal IP:port), causing the pool server to perform an outbound GET from its vantage point. Exfiltration is limited (the response is parsed as a typedstatsstruct), but internal network probing / metadata-endpoint reachability is possible. - Note: This affects an infrastructure server (
strelaypoolsrv), not the end-user Syncthing client. It is the only finding in the audit that lets an external party influence an outbound request target. - Recommendation: Validate
statusHostagainst the relay's announced IP and rejectIsLoopback(),IsPrivate(),IsUnspecified(), and link-local addresses.
- Location:
cmd/infra/strelaypoolsrv/main.go:200-217 - Description: The public-facing
strelaypoolsrvTLS listener setsMinVersion: tls.VersionTLS10and enables 3DES cipher suites (TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_3DES_EDE_CBC_SHA) and non-PFS RSA key-exchange suites. TLS 1.0 and 3DES are deprecated; 3DES is vulnerable to Sweet32. - Impact: This is materially weaker than the other infra servers (
stdiscosrvandstrelaysrvboth enforce TLS 1.2 minimum with no 3DES). Appears to be legacy compatibility, not a backdoor. - Recommendation: Raise
MinVersiontotls.VersionTLS12and drop 3DES / plain-RSA suites to match the other infra servers.
- Location:
lib/api/api.go:1251 - Description:
getSupportBundlewrites a backup copy of the generated ZIP to the config directory with mode0o666(world-readable + world-writable). The bundle contains logs, panic files, pprof profiles, connection stats, usage-report preview, and a partially-redacted config. - Impact: Not a backdoor — a weak default. Any other local user can read the bundle (local info disclosure of file paths, device IDs, addresses, profile data) and can replace/truncate it.
- Recommendation: Use
0o600(owner-only), consistent with the sensitivity of the bundle contents.
- Location:
lib/versioner/external.go:153 - Description: The "external" versioner runs a user-configured command from
cfg.Versioning.Params["command"]viaexec.Commandwhenever a file is archived, substituting%FILE_PATH%and folder info into the command words. - Why it's acceptable: No shell is involved (
go-shellquote.Split+exec.Command(words[0], words[1:]...)); words containing both unsafe metacharacters and template placeholders are rejected;STGUIAUTH/STGUIAPIKEYare stripped from the child environment. The command and file path both originate from trusted sources (user config and the local sync engine). An attacker would need write access to the config — which already implies GUI/admin compromise. - Recommendation: Keep the documentation link in the error message current so users understand the quoting requirement.
lib/api/api_auth.go:250-264/lib/config/ldapconfiguration.go:13— LDAPInsecureSkipVerifyis a documented opt-in config (insecureSkipVerify, defaultfalse) for self-signed/internal-CA LDAP servers. Not a backdoor.lib/discover/global.go:120,140— Global discoveryInsecureSkipVerifyis keyed off the URL?id=<deviceID>(in which case identity is enforced by the stronger device-ID-pinningidCheckingHTTPClient) or an explicit?insecureopt-in. Not a backdoor.lib/syncthing/syncthing.go:268— BEP sync-connection TLS setsInsecureSkipVerify = trueby design: Syncthing authenticates peers by the SHA-256 of their device certificate (Device ID) at the application layer, not by TLS chain validation. This is the documented TOFU/pinned-identity model. Relay-client TLS is compensated the same way (lib/relay/client/static.go:206-235).lib/ur/usage_report.go:366— Usage-report HTTP client allowsURPostInsecurely(opt-in, default false) for posting to self-hosted collection endpoints with self-signed certs. Failure/crash reporting usestlsutil.SecureDefaultWithTLS12()with no skip-verify.cmd/syncthing/main.go:393-401,cmd/syncthing/cli/client.go:57-66,cmd/strelaysrv/main.go:178—InsecureSkipVerify: trueagainst the local GUI's self-signed device certificate (identity, not PKI). Documented and correct.cmd/infra/stcrashreceiver/util.go:24-36,cmd/infra/ursrv/serve/serve.go:291-300—X-Forwarded-Foris trusted to derive a client IP for Sentry dedup ID (salted, rotated, truncated SHA-256) and usage-report address storage. No auth/access decision depends on these values.stdiscosrv --httpmode trusting the proxy header is the explicit proxied-deployment contract.lib/rc/rc.go:37— HardcodedAPIKeyconstant is test-harness-only (passed to a locally-launched child syncthing viaSTGUIAPIKEY); not embedded in production binaries.
These high-sensitivity areas were specifically scrutinized and confirmed free of backdoors/exfiltration:
- Usage reporting (
lib/ur/) — Sends only aggregated counts and feature flags (version, platform, folder/device counts, total/max file counts and MiB, memory usage, SHA256/cpu benchmark, NAT type, boolean feature-configured flags). No file names, file contents, folder paths, device IDs, certificates, private keys, or config secrets are sent.UniqueIDis a random per-installation token, not the device ID. Gated on explicit user acceptance (URAccepted >= 2); posts to configurableURURL(defaulthttps://data.syncthing.net/newdata). Candidate builds force-enable reporting — documented beta-channel behavior. - Crash reporting (
cmd/syncthing/crash_reporting.go,lib/ur/failurereporting.go) — Opt-in viaCREnabled; posts to user-configuredCRURL(defaulthttps://crash.syncthing.net/newcrash).filterLogLinesactively strips log lines before upload. Uses TLS 1.2 with no skip-verify. - Upgrade verification (
lib/upgrade/,lib/signature/) — Embedded public ECDSA P-521 key used only for verification;ecdsa.Verifywith SHA-256; signed digest covers archive name + binary contents; rejects when signature is missing; sizes capped. No hardcoded private keys, no alternate keys, no "accept if unsigned" path. - Device/folder encryption (
lib/protocol/encryption.go) — AEAD throughout: ChaCha20-Poly1305 (extended-nonce) for randomized encryption, AES-SIV (miscreant) for deterministic encryption. Nonces fromcrypto/rand. Key derivation: scrypt (N=32768) for password→folder-key, HKDF-SHA256 for folder-key→file-key. Padding hides small-file size leakage. No hardcoded keys/IVs, no ECB, no weakened mode. - Secure RNG (
lib/rand/securesource.go) — Reads exclusively fromcrypto/rand.Reader;Seed()panics by design. All session, CSRF, and API-key tokens flow through this. - TLS config (
lib/tlsutil/tlsutil.go) — TLS 1.3-only and TLS 1.2-min with curated ciphers; Ed25519/ECDSA-P256 cert generation; private keys written0o600; serials from secure RNG. - REST API auth (
lib/api/) — bcrypt password hashing (constant-time),crypto/rand-backed session/CSRF/API-key tokens with expiry and max-active caps, login body size limit, anti-brute-force sleep, CSRF required for/rest/(API-key bearer bypass is intentional for programmatic clients). Support bundle redactsAPIKey,Password,User,EncryptionPassword. - Wire protocol validation (
lib/protocol/protocol.go) —checkFilenamerejects non-canonical paths,"",".","..", absolute paths, and../-prefixed names (prevents path traversal over the wire). Message/header/request sizes bounded. LZ4 decompression bounded. - Sync engine path/symlink handling (
lib/model/,lib/fs/) —basicfs.rooted()canonicalizes and joins against root; watch events outside the root are rejected;O_NOFOLLOWon Unix opens. Symlink traversal explicitly tested (TestSymlinkTraversalRead/Write,TestDeleteBehindSymlink,TestTraversesSymlink). - Build tooling / scripts —
exec.Commanduses arg vectors throughout (nosh -c); codesign/keychain passwords passed as separate args.docker-entrypoint.shusesexec "$@". No build-time backdoor flags. - GUI JavaScript (
gui/default/syncthing/) — All HTTP calls target the local backend (rest) or local asset files. CSRF cookie correctly wired to the device ID.$translateProvider.useSanitizeValueStrategy('escape')enabled. Noeval,new Function,document.write, or remote-script injection. External URLs in templates are static docs/license links only.
Syncthing's codebase shows no signs of malicious intervention. The architecture's security-sensitive primitives (authentication, CSRF, token generation, TLS, device-identity pinning, peer-wire validation, encryption, secure randomness, upgrade signature verification, and telemetry) are implemented correctly and without hidden bypasses. The four findings above MEDIUM/LOW severity are conventional hardening gaps in infrastructure/auxiliary code (an SSRF surface in the relay pool server, a legacy TLS configuration, an over-permissive file mode on a diagnostic artifact) and a by-design external-command execution that is properly mitigated. None constitute a backdoor or data exfiltration path.
The most actionable recommendation is to remediate the SSRF in strelaypoolsrv (Finding 1) and the world-writable support-bundle permissions (Finding 3), and to modernize the strelaypoolsrv TLS configuration (Finding 2) — but these are hardening improvements, not evidence of compromise.