Commit Graph
225 Commits
Author SHA1 Message Date
Isaac Connor da04fd65aa fix: require auth for every ajax request regardless of view refs GHSA-vvw3-j4p4-4rgx
The ZM_OPT_USE_AUTH gate in index.php exempted view=login and view=none
so those pages can render without a session. The same condition also
guarded the ajax dispatcher, so view=none&request=<name> (or
view=login&request=<name>) reached web/ajax/<name>.php with no user.
ajax/stats.php's raw branch has no permission check of its own and
returned Events rows (Name, Cause, Notes, DiskSpace) joined with the
monitor name to anonymous clients.

Apply the login/none exemption only when there is no request, so any
request= without a user gets the 401. No ajax handler is meant to be
reached unauthenticated: login and logout are POST actions on views,
skin.js skips its polling on the login and none views, and the auth
revalidation probe already uses view=request and expects a 401/403 for
a dead session.

Also check canView('Events') at the top of ajax/stats.php, matching
getStatsTableHTML() used by its non-raw branch, so the raw branch no
longer depends on the gate alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 6699ae212ea8c5d7b9ee29f38c05c0135635fe8f)
2026-09-24 19:46:37 -04:00
Isaac Connor a229f04d0e Make actions can ono longer be performed with POST a debug 2026-08-28 22:31:29 -04:00
Isaac ConnorandClaude Opus 5 712994b196 fix: only store sessions for clients that carry them
Every request through index.php starts a session and always dirties it:
zm_session_set_remote_addr() writes remoteAddr, and index.php stores skin,
css and navbar_type. ZMSessionHandler::write() then persisted that session
unconditionally, so any request arriving without a ZMSESSID cookie left a
Sessions row behind that nothing would ever load again.

Viewing an event polls the event's server every ZM_WEB_REFRESH_STATUS
seconds via monitorUrl, which is absolute when the monitor has a Server
row. Those cross-origin ajax polls carry auth in the URL and no cookie, so
each one added a Sessions row every few seconds. Bot scans of the login
page did the same.

Persist a session only when the client presented our cookie, or when
zm_session_persist() marks it as one we are issuing: login, and the
postLoginQuery stashed before redirecting to the login page.

Verified on a live install by logging row counts from the save handler:
three cookieless requests skipped the write and left the count unchanged,
while a cookie-jar run wrote on the request that returned the cookie.
php -l clean on both files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GAFKf86P78WqniPEP2b45J
2026-08-25 17:36:31 -04:00
Isaac ConnorandClaude Opus 5 b9f565854f refactor: stop authenticating the request when auth.php is included
auth.php ended in a 130-line block at file scope, so merely including the file
authenticated the current request: it read $_REQUEST, opened a session, queried
the database, and on a login could rewrite the user's stored password hash and
populate $_SESSION. Any caller that wanted one of the functions in the file got
all of that as a side effect, and the order of includes decided when it ran.
HostController requires auth.php twice purely to reach generateAuthHash() and
validateToken().

Move the block into zm_authenticate_request() and call it explicitly from the
two places that want it, web/index.php and AppController::beforeFilter(). The
function returns the ZM\User or null and still sets the global $user, so the
views, ajax handlers and API controllers that read that global are unaffected.
HostController now gets only the function definitions from its requires, which
is all it ever wanted.

Inside a function the five `unset($user)` calls would drop the local binding
and leave the global set, so they become `$user = null` - the idiom the rest of
the file already uses for this, and one that keeps isset($user) false for the
gate at index.php:255. The block's other locals ($ret, $username, $password,
$sql) no longer leak into the caller's scope, which in beforeFilter() means they
can no longer collide with the variables of the same name it assigns just after.

The body is otherwise unchanged; `git diff -w` shows only the wrapper, those
five assignments and the return.

Tests: tests/php/test_auth_no_include_side_effects.php tokenises auth.php and
asserts nothing executes at file scope, with a fixture check so a broken
detector cannot pass vacuously. 4 assertions, all pass. Verified it reports the
pre-refactor file's file-scope block, so it would have caught this.

Not covered by tests: the login, logout, auth-hash and API token flows this
touches. auth.php cannot be included without a database (User.php pulls in
database.php, which connects at include time), so the check is structural.
Needs manual testing on an installed tree before merging.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01477mR97vfnK6zczbHgzq6T
2026-08-17 20:52:10 -04:00
Buff3st-0v3rfl0w 144a04ca4f fix(auth): prevent token authentication bypass using instanceof check 2026-07-09 16:19:46 -04:00
Isaac ConnorandClaude Opus 4.6 e15d2a6427 feat: add HLS byte-range playback for event video
Write a single continuous fragmented MP4 per event and generate an HLS
m3u8 manifest with byte-range references into that file. This enables
seamless browser playback via video.js's built-in http-streaming (VHS)
without needing separate segment files.

C++ changes:
- VideoStore tracks fragment boundaries (moof+mdat byte offsets and
  durations) as packets are written, by monitoring avio_tell() around
  keyframe writes in write_packet()
- Add writeM3U8() method that generates EXT-X-VERSION:7 byte-range
  manifests with EXT-X-MAP for the init segment
- Event writes a live m3u8 (no EXT-X-ENDLIST) on each new fragment
  for in-progress viewing, and a final VOD manifest at event close
- Change movflags to frag_keyframe+empty_moov+default_base_moof
  (default_base_moof required by HLS fMP4 spec, faststart removed
  as it's meaningless with empty_moov)

PHP/web changes:
- New view_hls.php endpoint serves pre-built m3u8 with auth tokens
- event.php detects index.m3u8 and uses HLS as primary source with
  direct MP4 as fallback
- CSRF exemption for view_hls in index.php

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:40:12 -04:00
Isaac Connor c0029fe8d1 Don't add postLoginQuery to url if it is empty 2026-03-12 15:03:45 -04:00
IgorA100 a9e332bdbe Correction as recommended by "copilot" (index.php) 2026-03-04 18:11:11 +03:00
IgorA100 ae148db5ac Chore: Optimized saving of "navbar_type" in session and cookies (index.php) 2026-03-01 22:36:28 +03:00
IgorA100 dd35971142 Fix: Saving "navbar_type" in cookies (index.php)
Also, _REQUEST is more important than _COOKIE
Therefore, we check _REQUEST after checking _COOKIE
2026-02-28 21:53:39 +03:00
IgorA100 b07d86fe90 Merge branch 'ZoneMinder:master' into patch-862983 2026-01-09 14:28:39 +03:00
Isaac Connor 8e71110305 Merge pull request #4513 from ZoneMinder/copilot/fix-event-redirect-after-login
Fix login redirect by passing postLoginQuery as URL parameter
2026-01-07 13:33:58 -05:00
Isaac Connor 80fc7a2c6a Fix logging statement 2026-01-04 18:27:31 -05:00
copilot-swe-agent[bot]andSteveGilvarry 5a70180140 Fix login redirect to preserve original event URL
- Modified web/index.php to pass postLoginQuery as URL parameter during redirect
- Updated web/skins/classic/views/login.php to check both $_REQUEST and $_SESSION for postLoginQuery
- This fixes the timing issue where session variable may not be available when login form renders

Co-authored-by: SteveGilvarry <7613276+SteveGilvarry@users.noreply.github.com>
2026-01-02 02:01:35 +00:00
IgorA100 4c98fcf47e Added a new constant DIR_EXPORTS_DOWNLOAD (index.php)
Because a separate directory is easier to monitor for generated files for download. This is planned for implementation in future PRs.
2025-12-18 00:44:23 +03:00
Isaac Connor df5eb15c76 Fix warning 2025-07-10 12:16:32 -04:00
Isaac Connor 7c81957aa2 Extend session around the setting of css, skin and navbar_type. Default navbar_type to what is in session 2025-07-05 12:02:43 -04:00
Isaac Connor 225b428592 Rework left vs top as NAVBAR_TYPE 2025-07-05 11:46:06 -04:00
Isaac Connor 55b99e19ab Use a redirect on homeview to fix having query params added on 2024-12-13 12:19:30 -05:00
Isaac Connor 65e098c35b If no view, default to home view 2024-11-23 13:36:59 -05:00
Isaac Connor 1884b18611 Don't run actions if we intend to redirect 2024-11-22 16:29:35 -05:00
Isaac Connor 0ac7434590 Further deprecate ZM_BASE_URL. Ssee #3761 2024-09-13 09:54:41 -04:00
Isaac Connor 663665ccfa Add possible config override of skin and css, forcing their use 2024-05-30 13:44:29 -04:00
Isaac Connor 2a06e0faf2 Make view does not exist an error instead of fatal 2024-05-08 14:28:40 -04:00
Isaac Connor 4df4f5146a Log error_message if set 2024-01-21 11:31:06 -05:00
Isaac Connor 72e8ee0e5e Move session inclusion down to where it is first used. 2023-12-29 11:06:46 -05:00
Isaac Connor 326a4ca201 Clean out old unused debugging code. The code further down is more useful 2023-12-29 11:06:46 -05:00
Isaac Connor ccfcd33855 modal requests require csrf-magic. 2023-12-05 09:47:35 -05:00
Isaac Connor 02f922d4b7 Don't include csrf on ajax requests. We only return json for ajax and the csrf kills the json parsing. 2023-11-28 10:16:58 -05:00
Isaac Connor 6a68e5577a If view does not exist, redirect to console instead of just dying 2023-11-09 09:57:27 -05:00
Isaac Connor 969baa3891 Convert user from an array to a User object 2023-04-23 12:57:29 -04:00
Isaac Connor 764d48d378 If the css in cookie is invalid, clear it so that the logs don't fill up with the warnings 2023-03-22 08:06:02 -04:00
Isaac Connor 80797e97d7 Move actions process to after the unauth check. In the past the action did the logging in but that is no longer the case. We can now assume in actions that the user is at least logged in. 2023-02-20 10:16:44 -05:00
Isaac Connor 0c316662e0 Remove php4 support 2023-01-03 15:50:18 -05:00
Isaac Connor 8c35190b0a Move loading of global Servers array into config.php. 2023-01-03 13:16:03 -05:00
Isaac Connor f9a3c5fc83 Don't log error when ignoring action if it is an ajax request 2022-11-16 09:28:33 -05:00
Isaac Connor 89c6e22052 Remove check for ZM_WEB_DIR is writable. 2022-11-15 14:44:45 -05:00
Isaac Connor 6199613c40 Merge branch 'add_event_data' 2022-10-31 18:00:32 -04:00
Isaac Connor 6edbee7c70 Handle empty but defined REQUEST[action] 2022-10-31 17:59:34 -04:00
Isaac Connor 91e728c0d3 Sort servers in global array 2022-10-25 13:31:40 -04:00
Isaac Connor ce62bb1297 Minor cleanup, remove dead code 2022-10-17 17:25:36 -04:00
Isaac Connor c0a4c05e84 Only perform actions on post. Doing them on GET allows doing actions from things like img tags which is not good. 2022-10-03 15:49:48 -04:00
Isaac Connor c7ded07794 Only look for action if there is a view 2022-09-27 10:49:50 -04:00
Isaac Connor 7874825064 put back including config.php 2022-06-03 10:09:36 -04:00
Isaac Connor 4a6ad1f880 move dateFormatters to config. This is so that they get setup even if not using index.php. Like cakephp for example. 2022-06-02 20:13:37 -04:00
Isaac Connor efe2f0b0a1 Add overriding timeFormatter pattern 2022-06-01 16:20:36 -04:00
Isaac Connor 655daff383 Add timeFormatter 2022-06-01 16:16:38 -04:00
Isaac Connor ce6bb6140a Setup global dateFormatter and dateTimeFormatter. Once config is initialized, redo them if a different local was specified, and then apply custom patters if specified. 2022-06-01 15:49:50 -04:00
Isaac Connor 8f05b4788f Add default global dateFormatter. Default to en_US but after config is loaded we can replace it. 2022-06-01 13:33:41 -04:00
Isaac Connor efc1ec770c Debug the contents of REQUEST on every hit. 2022-02-15 18:52:46 -05:00