From 65482c5e9d0e098b1c2dd7457b212c0668b43730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 10 Aug 2017 18:32:10 +0200 Subject: [PATCH] Fix dapps CSP when UI is exposed externally (#6178) * Allow embeding on any page when ui-hosts=all and fix dev_ui * Fix tests. --- dapps/src/handlers/mod.rs | 18 ++++++++++++++---- dapps/src/lib.rs | 9 +++++++-- dapps/src/tests/helpers/mod.rs | 1 + parity/configuration.rs | 33 ++++++++++++++++++++++++++++----- parity/dapps.rs | 6 ++++++ 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs index 7937ce667..65c2cbe27 100644 --- a/dapps/src/handlers/mod.rs +++ b/dapps/src/handlers/mod.rs @@ -67,10 +67,20 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd // Allow fonts from data: and HTTPS. b"font-src 'self' data: https:;".to_vec(), // Allow inline scripts and scripts eval (webpack/jsconsole) - b"script-src 'self' 'unsafe-inline' 'unsafe-eval';".to_vec(), - // Same restrictions as script-src (fallback) with additional + { + let script_src = embeddable_on.as_ref() + .map(|e| e.extra_script_src.iter() + .map(|&(ref host, port)| address(host, port)) + .join(" ") + ).unwrap_or_default(); + format!( + "script-src 'self' 'unsafe-inline' 'unsafe-eval' {};", + script_src + ).into_bytes() + }, + // Same restrictions as script-src with additional // blob: that is required for camera access (worker) - b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' blob: ;".to_vec(), + b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' https: blob:;".to_vec(), // Restrict everything else to the same origin. b"default-src 'self';".to_vec(), // Run in sandbox mode (although it's not fully safe since we allow same-origin and script) @@ -90,7 +100,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd .into_iter() .chain(embed.extra_embed_on .iter() - .map(|&(ref host, port)| format!("{}:{}", host, port)) + .map(|&(ref host, port)| address(host, port)) ); let ancestors = if embed.host == "127.0.0.1" { diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 412e69daf..3eda70ab5 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -201,6 +201,7 @@ impl Middleware { remote: Remote, ui_address: Option<(String, u16)>, extra_embed_on: Vec<(String, u16)>, + extra_script_src: Vec<(String, u16)>, dapps_path: PathBuf, extra_dapps: Vec, dapps_domain: &str, @@ -209,7 +210,7 @@ impl Middleware { web_proxy_tokens: Arc, fetch: F, ) -> Self { - let embeddable = as_embeddable(ui_address, extra_embed_on, dapps_domain); + let embeddable = as_embeddable(ui_address, extra_embed_on, extra_script_src, dapps_domain); let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new( hash_fetch::urlhint::URLHintContract::new(registrar), sync_status.clone(), @@ -294,12 +295,14 @@ fn address(host: &str, port: u16) -> String { fn as_embeddable( ui_address: Option<(String, u16)>, extra_embed_on: Vec<(String, u16)>, + extra_script_src: Vec<(String, u16)>, dapps_domain: &str, ) -> Option { ui_address.map(|(host, port)| ParentFrameSettings { host, port, extra_embed_on, + extra_script_src, dapps_domain: dapps_domain.to_owned(), }) } @@ -320,8 +323,10 @@ pub struct ParentFrameSettings { pub host: String, /// Port pub port: u16, - /// Additional pages the pages can be embedded on. + /// Additional URLs the dapps can be embedded on. pub extra_embed_on: Vec<(String, u16)>, + /// Additional URLs the dapp scripts can be loaded from. + pub extra_script_src: Vec<(String, u16)>, /// Dapps Domain (web3.site) pub dapps_domain: String, } diff --git a/dapps/src/tests/helpers/mod.rs b/dapps/src/tests/helpers/mod.rs index 38dd82de6..30d3ba8f9 100644 --- a/dapps/src/tests/helpers/mod.rs +++ b/dapps/src/tests/helpers/mod.rs @@ -260,6 +260,7 @@ impl Server { remote, signer_address, vec![], + vec![], dapps_path, extra_dapps, DAPPS_DOMAIN.into(), diff --git a/parity/configuration.rs b/parity/configuration.rs index be355f394..fda91e231 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -20,6 +20,7 @@ use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::collections::BTreeMap; use std::cmp::max; +use std::str::FromStr; use cli::{Args, ArgsError}; use util::{Hashable, H256, U256, Bytes, version_data, Address}; use util::journaldb::Algorithm; @@ -551,6 +552,10 @@ impl Configuration { Ok(options) } + fn ui_port(&self) -> u16 { + self.args.flag_ports_shift + self.args.flag_ui_port + } + fn ntp_servers(&self) -> Vec { self.args.flag_ntp_servers.split(",").map(str::to_owned).collect() } @@ -560,12 +565,15 @@ impl Configuration { enabled: self.ui_enabled(), ntp_servers: self.ntp_servers(), interface: self.ui_interface(), - port: self.args.flag_ports_shift + self.args.flag_ui_port, + port: self.ui_port(), hosts: self.ui_hosts(), } } fn dapps_config(&self) -> DappsConfiguration { + let dev_ui = if self.args.flag_ui_no_validation { vec![("localhost".to_owned(), 3000)] } else { vec![] }; + let ui_port = self.ui_port(); + DappsConfiguration { enabled: self.dapps_enabled(), ntp_servers: self.ntp_servers(), @@ -575,11 +583,26 @@ impl Configuration { } else { vec![] }, - extra_embed_on: if self.args.flag_ui_no_validation { - vec![("localhost".to_owned(), 3000)] - } else { - vec![] + extra_embed_on: { + let mut extra_embed = dev_ui.clone(); + match self.ui_hosts() { + // In case host validation is disabled allow all frame ancestors + None => extra_embed.push(("*".to_owned(), ui_port)), + Some(hosts) => extra_embed.extend(hosts.into_iter().filter_map(|host| { + let mut it = host.split(":"); + let host = it.next(); + let port = it.next().and_then(|v| u16::from_str(v).ok()); + + match (host, port) { + (Some(host), Some(port)) => Some((host.into(), port)), + (Some(host), None) => Some((host.into(), ui_port)), + _ => None, + } + })), + } + extra_embed }, + extra_script_src: dev_ui, } } diff --git a/parity/dapps.rs b/parity/dapps.rs index f048f69ca..fddd050f5 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -40,6 +40,7 @@ pub struct Configuration { pub dapps_path: PathBuf, pub extra_dapps: Vec, pub extra_embed_on: Vec<(String, u16)>, + pub extra_script_src: Vec<(String, u16)>, } impl Default for Configuration { @@ -56,6 +57,7 @@ impl Default for Configuration { dapps_path: replace_home(&data_dir, "$BASE/dapps").into(), extra_dapps: vec![], extra_embed_on: vec![], + extra_script_src: vec![], } } } @@ -168,6 +170,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result, _dapps_domain: &str, _extra_embed_on: Vec<(String, u16)>, + _extra_script_src: Vec<(String, u16)>, ) -> Result { Err("Your Parity version has been compiled without WebApps support.".into()) } @@ -251,6 +255,7 @@ mod server { extra_dapps: Vec, dapps_domain: &str, extra_embed_on: Vec<(String, u16)>, + extra_script_src: Vec<(String, u16)>, ) -> Result { let signer = deps.signer; let parity_remote = parity_reactor::Remote::new(deps.remote.clone()); @@ -262,6 +267,7 @@ mod server { parity_remote, deps.ui_address, extra_embed_on, + extra_script_src, dapps_path, extra_dapps, dapps_domain,