vrek: camera traffic is plain HTTP; TLS investigation deferred to tomorrow

- Record that ONVIF, snapshots and ISAPI all use plain HTTP and that
  MediaMTX pulls RTSP with the login in the URL; no password crosses the
  wire in the clear, but everything else does.
- Both cameras answer HTTPS with a self-signed certificate, yet digest
  that works on port 80 is refused on 443; repeated attempts tripped the
  camera's login lock, so testing resumes tomorrow, one attempt at a time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2026-09-19 18:31:09 -05:00
parent 12d0604dfd
commit c0e987d06e

View File

@ -977,3 +977,18 @@
{"id":"evt-2rjmf1et51v9","type":"node.status_changed","subject":"iss-6f346cm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"from":"open","to":"deferred"},"at":"2026-09-19T23:23:36.381Z","parents":["evt-hpgxjted58jq"],"hash":"eaa4110035104fffad97589f444babb35938750837bb4a33935b8e532029d8d1"}
{"id":"evt-hwmtgatb4tav","type":"node.updated","subject":"iss-wdpf95s","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"attrs":{"revisit":null,"deferred_because":"Deferred indefinitely by the user on 2026-09-19: nice to have, and the camera page already says it."}}},"at":"2026-09-19T23:23:37.862Z","parents":["evt-2rjmf1et51v9"],"hash":"6e1c438de637b1bfe6901751bde7101a57e94316ef3eac6d0f4c119f0ab84e0d"}
{"id":"evt-1qqv3a71m62r","type":"node.status_changed","subject":"iss-wdpf95s","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"from":"open","to":"deferred"},"at":"2026-09-19T23:23:37.866Z","parents":["evt-hwmtgatb4tav"],"hash":"08753d7ee6314709c527835159e5da04a4cecd3b2be69a7f3d5b8b529caf2f18"}
{"id":"evt-hgj83ejnr95s","type":"node.created","subject":"fnd-vygp293","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"finding","title":"Every call the app makes to a camera is plain HTTP or RTSP; no password is sent in the clear, but everything else is","body":"Read from the code on 2026-09-19. ONVIF goes over http:// on the camera's port (src/lib/camera.ts), with the onvif library's WS-Security, so the password travels as a SHA-1 digest with a nonce rather than in the clear. Snapshots follow the camera's own snapshot URI, which is http, with digest or basic auth (basic only if the camera offers no digest challenge, which would put the password on the wire). ISAPI uses node:http explicitly with digest auth (src/lib/isapi.ts). Video is pulled by MediaMTX over rtsp:// on TCP, and that source URL carries the username and password, so those credentials do cross the network in a recoverable form. Everything else is readable by anyone on that network segment: snapshots, video, settings documents, the camera's serial and MAC. Browser to app is plain HTTP too, which is why the session cookie is only marked secure behind a proxy that terminates TLS. The cameras themselves advertise isSupportHttps, certificate import and a self-signed certificate they can generate (fnd-qa5n7s5's capabilities), and MediaMTX supports RTSPS, so encrypting these paths is possible; the obstacle is trusting a self-signed certificate properly rather than turning verification off, which would be worse than plain HTTP because it would look safe.","status":"current","owner":"prn-q80g8mz","attrs":{"sources":[{"node":"iss-b41wnpb","note":"src/lib/camera.ts, src/lib/isapi.ts, src/lib/video.ts and the camera capabilities read on 2026-09-19"}],"as_of":"2026-09-19"}},"at":"2026-09-19T23:27:10.082Z","parents":["evt-1qqv3a71m62r"],"hash":"57977d8f7953218fc84456c28774de28a48a90cc31f862c014c16d10368ab5be"}
{"id":"evt-24kehq6822j3","type":"edge.added","subject":"fnd-vygp293","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"informs","from":"fnd-vygp293","to":"gol-6q1q5mr"},"at":"2026-09-19T23:27:10.083Z","parents":["evt-hgj83ejnr95s"],"hash":"45deac0d79eeba1a18e756e83f54d7a9c82a85b05eb56ee627634f1a315ce752"}
{"id":"evt-vvpz099x707j","type":"edge.added","subject":"fnd-vygp293","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"informs","from":"fnd-vygp293","to":"pri-tyrxdz9"},"at":"2026-09-19T23:27:10.084Z","parents":["evt-24kehq6822j3"],"hash":"aa44393003c1c55ad808595c98d9d1793bdb512543ee6d9dfbdad4031990ebe7"}
{"id":"evt-fmre2fmnz1aw","type":"node.created","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Investigate encrypting the app's traffic to the cameras","body":"Per fnd-vygp293: ONVIF, snapshots and ISAPI all go over plain HTTP, and MediaMTX pulls video over RTSP with the login in the source URL. Investigate, read-only first, what it would take to encrypt each path and whether it is worth it on a LAN. Questions to answer with evidence: does the camera answer ONVIF, ISAPI and snapshots over HTTPS on port 443, and with what certificate (self-signed by default, so what is its subject and lifetime); can it be given a certificate we trust (the capabilities advertise certificate import, self-signed export and CSR-style handling); does it support RTSPS, and does MediaMTX pull from it; what breaks, since the snapshot and stream URIs the camera advertises are http/rtsp, so the app would have to rewrite them; how certificate trust would work in Node without disabling verification, for example pinning the camera's certificate per camera in the registry. The trap to avoid: turning verification off, which looks safe and is not. Then a recommendation, including doing nothing on a trusted LAN, and what changes if the app is ever reached from outside. Also worth noting separately: the app itself is served over plain HTTP, so the admin session cookie is not marked secure.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T23:27:21.170Z","parents":["evt-vvpz099x707j"],"hash":"b178e7cac2f5ae3f4b5b7daf1fccbd5283b5d5d9dd1b2eac3fd542a0c67cc1e9"}
{"id":"evt-nmecd3aw36na","type":"edge.added","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-aret59y","to":"gol-6q1q5mr"},"at":"2026-09-19T23:27:21.172Z","parents":["evt-fmre2fmnz1aw"],"hash":"b6bf26de53bceebaa3c9a2cac158a5b0e1059de1643f7409aab69f927db11076"}
{"id":"evt-4sqzd8t0080q","type":"edge.added","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-aret59y","to":"src/lib/camera.ts"},"at":"2026-09-19T23:27:21.173Z","parents":["evt-nmecd3aw36na"],"hash":"7f5f35f0197c948073bc8b88a261ffc9352456c2e2a9a13380bf288a25cad44a"}
{"id":"evt-trfkcaw0fmsp","type":"edge.added","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-aret59y","to":"src/lib/isapi.ts"},"at":"2026-09-19T23:27:21.174Z","parents":["evt-4sqzd8t0080q"],"hash":"6c81347a69bd920ff90a9e0c41e2d11e6b2ef012c072966c6a27ace6b31ef547"}
{"id":"evt-8wn4cqhcdq7f","type":"edge.added","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-aret59y","to":"src/lib/video.ts"},"at":"2026-09-19T23:27:21.175Z","parents":["evt-trfkcaw0fmsp"],"hash":"47424b1053490d3f79b972e53dfcaa8de84213d92d902f7898ae289c7165e288"}
{"id":"evt-tentp8y9yec4","type":"edge.added","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-aret59y","to":"area:security"},"at":"2026-09-19T23:27:21.176Z","parents":["evt-8wn4cqhcdq7f"],"hash":"d80909538d6f3e55c909d3b22dcd422e8fd01338510ec1e972a97a2ebb74c8f1"}
{"id":"evt-4hkcq1zg97js","type":"edge.added","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-aret59y","to":"area:device"},"at":"2026-09-19T23:27:21.177Z","parents":["evt-tentp8y9yec4"],"hash":"5c23dd196a9edd65fd180541dadab43a4e7f701205ed97ed2112dd589bd22f84"}
{"id":"evt-9yndg25pyww2","type":"node.created","subject":"fnd-0b0q7xw","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"finding","title":"Both cameras answer HTTPS with a self-signed certificate, but digest that works on port 80 is refused on 443; repeated attempts locked the account","body":"Read-only probing on 2026-09-19. Ports open on both cameras: 80, 443, 554 (RTSP) and 8000 (Hikvision SDK); 322 (RTSPS) is closed, so encrypted video would need enabling on the camera, if the firmware offers it. HTTPS on 443 answers and challenges for credentials. The certificate is self-signed: subject and issuer are the same random hex name (per camera), valid 2024-01-01 to 2033-12-29, so nothing in a normal trust store signs it, and curl without -k, like Node, refuses it. The digest challenge is character-for-character the same scheme and realm on both ports, yet the account that authenticates over HTTP was refused (401) over HTTPS on deviceInfo and userCheck, with plain digest, with anyauth, with basic, with Connection: close and with TLS pinned to 1.2. Hypothesis, untested: this firmware expects the session flow its own web app uses (/ISAPI/Security/sessionLogin, a challenge-and-hash exchange) on HTTPS, rather than HTTP digest. Caution learned the hard way: after those failed attempts the camera also began refusing the credentials over HTTP, which matches Hikvision's illegal-login lock (about five failures, roughly 30 minutes), and that also blocks the app's own reads while it lasts. Any further TLS testing should be one attempt at a time, with the lock given time to clear in between.","status":"current","owner":"prn-q80g8mz","attrs":{"sources":[{"node":"iss-aret59y","note":"Cameras …af2e and …af54, addresses redacted per dec-kgm44qw; nc port checks, openssl s_client, curl against ISAPI over both ports"}],"as_of":"2026-09-19"}},"at":"2026-09-19T23:30:53.721Z","parents":["evt-4hkcq1zg97js"],"hash":"b815e17838bf24ff32db3df8f2a5fdb3633111d7105ef44e07745ed0fcee8c67"}
{"id":"evt-bwt21kmnwevq","type":"edge.added","subject":"fnd-0b0q7xw","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"informs","from":"fnd-0b0q7xw","to":"iss-aret59y"},"at":"2026-09-19T23:30:53.722Z","parents":["evt-9yndg25pyww2"],"hash":"c0086bd3560a4d62196790b080ced77ab65927c243ded6063dd0b8dd00973329"}
{"id":"evt-7e1estxqp6zv","type":"edge.added","subject":"fnd-0b0q7xw","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"informs","from":"fnd-0b0q7xw","to":"gol-6q1q5mr"},"at":"2026-09-19T23:30:53.723Z","parents":["evt-bwt21kmnwevq"],"hash":"d677fee3803b51c9db624bf64fd12b6aaf02652d29c8b7d195c67ae731dbab12"}
{"id":"evt-mpvp081sd401","type":"node.updated","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"attrs":{"revisit":"2026-09-20","deferred_because":"User asked on 2026-09-19 for a reminder tomorrow: the camera's login lock from today's failed HTTPS attempts needs to expire before testing again, one attempt at a time, starting with the sessionLogin theory."}}},"at":"2026-09-19T23:30:56.101Z","parents":["evt-7e1estxqp6zv"],"hash":"6f8e43c0e73d650e648c2182ade62f998d8371a75bdeb24bbf6d98c29fe0a1e8"}
{"id":"evt-jjb91xaqzfh8","type":"node.status_changed","subject":"iss-aret59y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"from":"open","to":"deferred"},"at":"2026-09-19T23:30:56.103Z","parents":["evt-mpvp081sd401"],"hash":"91475111219c80eca4cd67e727da928b0925436019ef3349566a492331613ad3"}