mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-26 07:05:56 -05:00
parent
21da924d84
commit
fd7438dd28
@ -6,6 +6,11 @@ changes, see Git history.
|
|||||||
Each release is tagged in Git and on the Docker repository
|
Each release is tagged in Git and on the Docker repository
|
||||||
[`scottlamb/moonfire-nvr`](https://hub.docker.com/r/scottlamb/moonfire-nvr).
|
[`scottlamb/moonfire-nvr`](https://hub.docker.com/r/scottlamb/moonfire-nvr).
|
||||||
|
|
||||||
|
## unreleased
|
||||||
|
|
||||||
|
* [#219](https://github.com/scottlamb/moonfire-nvr/issues/219): fix
|
||||||
|
live stream failing with `ws close: 1006` on URLs with port numbers.
|
||||||
|
|
||||||
## 0.7.4 (2022-04-13)
|
## 0.7.4 (2022-04-13)
|
||||||
|
|
||||||
* upgrade to Retina 0.3.9, improving camera interop and diagnostics.
|
* upgrade to Retina 0.3.9, improving camera interop and diagnostics.
|
||||||
|
@ -19,6 +19,50 @@ use crate::{mp4, web::plain_response};
|
|||||||
|
|
||||||
use super::{bad_req, Caller, ResponseResult, Service};
|
use super::{bad_req, Caller, ResponseResult, Service};
|
||||||
|
|
||||||
|
/// Checks the `Host` and `Origin` headers match, if the latter is supplied.
|
||||||
|
///
|
||||||
|
/// Web browsers must supply origin, according to [RFC 6455 section
|
||||||
|
/// 4.1](https://datatracker.ietf.org/doc/html/rfc6455#section-4.1).
|
||||||
|
/// It's not required for non-browser HTTP clients.
|
||||||
|
///
|
||||||
|
/// 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).
|
||||||
|
fn check_origin(headers: &header::HeaderMap) -> Result<(), super::HttpError> {
|
||||||
|
let origin_hdr = match headers.get(http::header::ORIGIN) {
|
||||||
|
None => return Ok(()),
|
||||||
|
Some(o) => o,
|
||||||
|
};
|
||||||
|
let host_hdr = headers
|
||||||
|
.get(header::HOST)
|
||||||
|
.ok_or_else(|| bad_req("missing Host header"))?;
|
||||||
|
let host_str = host_hdr.to_str().map_err(|_| bad_req("bad Host header"))?;
|
||||||
|
|
||||||
|
// Currently this ignores the port number. This is easiest and I think matches the browser's
|
||||||
|
// rules for when it sends a cookie, so it probably doesn't cause great security problems.
|
||||||
|
let host = match host_str.split_once(':') {
|
||||||
|
Some((host, _port)) => host,
|
||||||
|
None => host_str,
|
||||||
|
};
|
||||||
|
let origin_url = origin_hdr
|
||||||
|
.to_str()
|
||||||
|
.ok()
|
||||||
|
.and_then(|o| url::Url::parse(o).ok())
|
||||||
|
.ok_or_else(|| bad_req("bad Origin header"))?;
|
||||||
|
let origin_host = origin_url
|
||||||
|
.host_str()
|
||||||
|
.ok_or_else(|| bad_req("bad Origin header"))?;
|
||||||
|
if host != origin_host {
|
||||||
|
bail_t!(
|
||||||
|
PermissionDenied,
|
||||||
|
"cross-origin request forbidden (request host {:?}, origin {:?})",
|
||||||
|
host_hdr,
|
||||||
|
origin_hdr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
pub(super) fn stream_live_m4s(
|
pub(super) fn stream_live_m4s(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
@ -27,36 +71,7 @@ impl Service {
|
|||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
stream_type: db::StreamType,
|
stream_type: db::StreamType,
|
||||||
) -> ResponseResult {
|
) -> ResponseResult {
|
||||||
// Web browsers must supply origin:
|
check_origin(req.headers())?;
|
||||||
// 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 {
|
if !caller.permissions.view_video {
|
||||||
bail_t!(PermissionDenied, "view_video required");
|
bail_t!(PermissionDenied, "view_video required");
|
||||||
}
|
}
|
||||||
@ -220,3 +235,34 @@ impl Service {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn origin_port_8080_okay() {
|
||||||
|
// By default, Moonfire binds to port 8080. Make sure that specifying a port number works.
|
||||||
|
let mut hdrs = header::HeaderMap::new();
|
||||||
|
hdrs.insert(header::HOST, "nvr:8080".try_into().unwrap());
|
||||||
|
hdrs.insert(header::ORIGIN, "http://nvr:8080/".try_into().unwrap());
|
||||||
|
assert!(check_origin(&hdrs).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn origin_missing_okay() {
|
||||||
|
let mut hdrs = header::HeaderMap::new();
|
||||||
|
hdrs.insert(header::HOST, "nvr".try_into().unwrap());
|
||||||
|
assert!(check_origin(&hdrs).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn origin_mismatch_fails() {
|
||||||
|
let mut hdrs = header::HeaderMap::new();
|
||||||
|
hdrs.insert(header::HOST, "nvr".try_into().unwrap());
|
||||||
|
hdrs.insert(header::ORIGIN, "http://evil/".try_into().unwrap());
|
||||||
|
assert!(check_origin(&hdrs).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user