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:
Scott Lamb
2022-03-22 14:28:25 -07:00
parent 307a3884a0
commit 4c9aa93fdf
2 changed files with 36 additions and 0 deletions

View File

@@ -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");
}