Security review pass 11: Sockeye public routes; all passes done
From code: the 2FA flaw (F5) is also in Sockeye and the business server (F41, critical; mitigation: turn 2FA off until fixed); the licence-signing private keys are hardcoded in Sockeye's source (F42); one-shot licence fetch by DB ID (F39); trial request routes without rate limits (F40). Review only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ keep it that way while findings are open.
|
||||
| 8 | Email and notifications (`direct-smtp`, templates, delivery) | Done 2026-10-23 |
|
||||
| 9 | Client (XSS, token storage, `npm audit` triage), CORS, headers, Swagger UI | Done 2026-10-23 |
|
||||
| 10 | Deployment: installers, Linux setup script, nginx, systemd, config and data permissions | Done 2026-10-23 |
|
||||
| 11 | Sockeye public routes (licence fetch, trial requests) | Next |
|
||||
| 11 | Sockeye public routes (licence fetch, trial requests) | Done 2026-10-23 |
|
||||
|
||||
The order is by risk to internet-facing servers. Findings from any pass go in **Findings**.
|
||||
|
||||
@@ -186,6 +186,8 @@ Status: *candidate* (seen in code, not yet confirmed), *confirmed*, *fixed in ca
|
||||
|
||||
| ID | Severity | Status | Title |
|
||||
|---|---|---|---|
|
||||
| F41 | **Critical** | confirmed (code) | F5 also in Sockeye (internet-facing admin login) and in the 8.2.x business AyaNova server |
|
||||
| F42 | High | confirmed (code) | The licence-signing RSA private keys are hardcoded in Sockeye's source and binaries |
|
||||
| F5 | **Critical** | **confirmed** | 2FA login step accepts an empty or missing temp token: the password is skipped for users with 2FA on |
|
||||
| F1 | Critical (installs that kept it) | **confirmed** | Placeholder JWT secret in the LAN/IIS and Linux server config templates |
|
||||
| F19 | Critical | **confirmed** (import) | Any logged-in user, customer contacts included, can import report templates, whose JavaScript runs in the server's browser |
|
||||
@@ -223,6 +225,8 @@ Status: *candidate* (seen in code, not yet confirmed), *confirmed*, *fixed in ca
|
||||
| F29 | Low | confirmed (code) | `server-info` gives every login, customer contacts included, the server DB ID and full licence details |
|
||||
| F35 | Low | confirmed | No security headers from the server itself (self-hosted), no CSP anywhere, CORS any origin, public Swagger UI, download keys in URLs |
|
||||
| F38 | Low | confirmed (code) | Linux hardening: no systemd sandboxing, service user can replace program files, desktop runs as root, unused Docker files with a fixed database password |
|
||||
| F39 | Low | confirmed (code) | Licence fetch is one-shot by DB ID, and the DB ID is shown anonymously while unlicensed: someone else can take a new key first |
|
||||
| F40 | Low | confirmed (code) | Trial request routes: no rate limit, non-cryptographic verification codes, a fixed bot-check value |
|
||||
| F22 | Low | confirmed | Minor report and job route gaps: template export by any download key, job status, logs and cancel by GUID without an owner check, browser without sandbox |
|
||||
|
||||
**Release decisions.** John, 2026-10-23: **F13 to F17 are urgent and are fixed in 8.3.0**, as
|
||||
@@ -757,6 +761,57 @@ droplet, stores them in `/root/ayanova-secrets.txt` (600), makes `config.json` 6
|
||||
runs AyaNova as `www-data`, installs unattended upgrades, uses PGDG PostgreSQL on its default
|
||||
localhost listener, and gets a Let's Encrypt certificate with HSTS and the headers in F35.
|
||||
|
||||
### F41 The 2FA flaw (F5) in Sockeye and the business server (pass 11, from code)
|
||||
|
||||
Sockeye is a near-copy of this codebase: `sockeye/server/Controllers/AuthController.cs:168` matches
|
||||
the 2FA step on `TempToken` alone and sets it to `string.Empty` after success, exactly as F5.
|
||||
Sockeye's login is on the internet (sockeye.ayanova.com) and holds the customer list, licences
|
||||
and subscription servers; the business AyaNova server (8.2.x) has the same code. If an account on
|
||||
either has 2FA turned on, it can be reached with no password and a guessed code. **Immediate
|
||||
mitigation (no code change):** turn 2FA off on those accounts until the fix is deployed; with 2FA
|
||||
off the account is not matched by this query and the password protects it as usual. Then apply
|
||||
the F5 fix to Sockeye too. Other shared-code findings apply to Sockeye as well (F7, F8, F12, F19,
|
||||
F21, F30; F1 if its config kept a placeholder); its admin UI is used by John alone, so only the
|
||||
anonymous ones (F5, F41) are urgent there.
|
||||
|
||||
### F42 Licence-signing private keys in Sockeye source (pass 11, from code)
|
||||
|
||||
`sockeye/server/biz/RavenKeyFactory.cs` holds two RSA private keys as string literals (lines 158
|
||||
and 222; not reproduced here) and signs licence keys with them. They are therefore in the Sockeye
|
||||
repository and its history (self-hosted Gitea), in every build of Sockeye, and on the Sockeye
|
||||
droplet in the deployed DLL. Anyone who obtains any of those can create licence keys that every
|
||||
AyaNova server accepts. AyaNova checks licences against the matching public key
|
||||
(`License.cs`, "our real 2016 public key"). Fix: load the private key at run time from a file or
|
||||
environment variable on the Sockeye server only (root-readable, outside the repo and the
|
||||
published files) and remove it from the source. Rotation (new key pair, new public key in
|
||||
AyaNova, every licence reissued) is a business decision for John; without rotation the keys stay
|
||||
in history, so access to the repository and its backups must be treated as access to the keys.
|
||||
|
||||
### F39 One-shot licence fetch by DB ID (pass 11, from code)
|
||||
|
||||
`RvfController` (`POST rvf`, anonymous) returns the newest active licence for a DB ID, once:
|
||||
`FetchedOn` is set on the first fetch and later fetches get 404. DB IDs are random (32 bytes) but
|
||||
not secret: the anonymous API root page shows it while a server is unlicensed (F11) and
|
||||
`server-info` shows it to every login (F29). Someone who knows it can take a newly issued key
|
||||
first; the real server then never receives it (a support case; the key only works for that DB
|
||||
ID, but it names the licensee and features). Fix: let the same DB ID fetch its key again (the key
|
||||
is bound to the DB ID, so one-shot adds nothing), and stop showing the DB ID anonymously.
|
||||
|
||||
### F40 Trial request routes (pass 11, from code)
|
||||
|
||||
`POST rvr` (AyaNova trial request) and `POST subscription-server-trial-request` are anonymous with no
|
||||
rate limit. Each `rvr` request with a new DB ID stores a request and sends Sockeye's fixed
|
||||
verification email to the address given; the email confirm code (`StringUtil.GenFetchCode`) is
|
||||
10 letters from `System.Random`, not a cryptographic generator. The subscription-server request's
|
||||
only bot check is a fixed value (`RValue == "totallylegit"`). Approval is manual, so none of this
|
||||
issues a licence; the exposure is email to arbitrary addresses and a cluttered request list.
|
||||
Fix: nginx `limit_req` on these routes; `RandomNumberGenerator` for codes; optionally a CAPTCHA
|
||||
on the website forms.
|
||||
|
||||
OK in pass 11: no licence is issued without John's approval; a DB ID with a purchased licence
|
||||
cannot request a trial; licence keys are bound to the DB ID and signed (AyaNova verifies the
|
||||
signature).
|
||||
|
||||
### F2 Windows folder permissions (from the 8.3.0 installer test)
|
||||
|
||||
`lan.iss` and `standalone.iss` set `Permissions: users-modify` on `{app}` (the program
|
||||
@@ -837,3 +892,4 @@ roles that should not see the schedule (pass 7).
|
||||
| 2026-10-23 | 8 Email and notifications | Code reading only, nothing sent. Read `NotifyController` (direct SMTP and message), `Mailer`, `GlobalOpsNotificationSettings` controller, biz and model, `NotifyEventHelper`, `CoreJobNotify`, `CoreJobCustomerNotify`, `NotifySubscriptionBiz`. F3 and F4 confirmed by code; F17 confirmed and re-rated Medium (names and links, no reports). New: F30, F31, F32. **Action for John: rotate the FastMail app password in F32** |
|
||||
| 2026-10-23 | 9 Client, headers, CORS, Swagger | Code reading of every `v-html` use in `raven-client` and its data source; `npm audit` read-only against the lockfile (no install, no change), triaged to shipped packages; server and nginx headers, CORS, Swagger. New: F33, F34 (needs John's decision on two front-end packages), F35; F14 extended (`unit/customerunit`) |
|
||||
| 2026-10-23 | 10 Deployment | Code reading: `lan.iss`, `standalone.iss`, the four `config.json` templates, the standalone PostgreSQL configuration, the hosted setup script in `sockeye-client` (secrets, permissions, systemd unit, nginx, certbot), `dist/docker`. F2 confirmed and extended (`config.json` readable by all users); F9 extended (hosted droplets start with the default SuperUser password). New: F36, F37, F38. John then confirmed hosted droplets have a restrictive cloud firewall (F36 re-rated Low), accepted F37, and explained the trial default password (F9 note added) |
|
||||
| 2026-10-23 | 11 Sockeye public routes | Code reading of `RvfController` (licence fetch), `RvrController` and `SubscriptionServerTrialRequestController` (trial requests), `TrialLicenseRequestBiz`, the code generator, Sockeye's `AuthController` and token middleware, and where licences are signed. New: **F41** (F5 in Sockeye and the business server; turn 2FA off until fixed), **F42** (signing private keys in source), F39, F40. **All 11 passes done** |
|
||||
|
||||
Reference in New Issue
Block a user