check WebSocket origin
This fixes a real cross-site WebSocket hijacking (CSWSH) vulnerability. If the attacker knows the URL of an NVR installation this user is authenticated to and the UUID of a camera, and can trick the user into visiting their webpage, they can grab the live stream. At least there's some entropy in the camera UUID, but it was never intended to be a secret.
This commit is contained in:
parent
307a3884a0
commit
4c9aa93fdf
|
@ -6,6 +6,12 @@ changes, see Git history.
|
|||
Each release is tagged in Git and on the Docker repository
|
||||
[`scottlamb/moonfire-nvr`](https://hub.docker.com/r/scottlamb/moonfire-nvr).
|
||||
|
||||
## unreleased
|
||||
|
||||
* security fix: check the `Origin` header on live stream WebSocket requests
|
||||
to avoid cross-site WebSocket hijacking (CSWSH).
|
||||
* RTSP connections always use the Retina library rather than FFmpeg.
|
||||
|
||||
## `v0.7.2` (2022-03-16)
|
||||
|
||||
* introduce a configuration file `/etc/moonfire-nvr.toml`; you will need
|
||||
|
|
|
@ -27,6 +27,36 @@ impl Service {
|
|||
uuid: Uuid,
|
||||
stream_type: db::StreamType,
|
||||
) -> ResponseResult {
|
||||
// Web browsers must supply origin:
|
||||
// https://datatracker.ietf.org/doc/html/rfc6455#section-4.1
|
||||
//
|
||||
// If present, verify it. Chrome doesn't honor the `s=` cookie's
|
||||
// `SameSite=Lax` setting for WebSocket requests, so this is the sole
|
||||
// protection against CSWSH.
|
||||
// https://christian-schneider.net/CrossSiteWebSocketHijacking.html
|
||||
if let Some(origin) = req.headers().get(http::header::ORIGIN) {
|
||||
let host = req
|
||||
.headers()
|
||||
.get(header::HOST)
|
||||
.ok_or_else(|| bad_req("missing Host header"))?;
|
||||
let origin = origin
|
||||
.to_str()
|
||||
.ok()
|
||||
.and_then(|o| url::Url::parse(o).ok())
|
||||
.ok_or_else(|| bad_req("bad Origin header"))?;
|
||||
let origin_host = origin
|
||||
.host_str()
|
||||
.ok_or_else(|| bad_req("bad Origin header"))?;
|
||||
if host.as_bytes() != origin_host.as_bytes() {
|
||||
bail_t!(
|
||||
PermissionDenied,
|
||||
"cross-origin request forbidden (request host {:?}, origin host {:?})",
|
||||
host,
|
||||
origin_host
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !caller.permissions.view_video {
|
||||
bail_t!(PermissionDenied, "view_video required");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue