Fix dapps CSP when UI is exposed externally (#6178)

* Allow embeding on any page when ui-hosts=all and fix dev_ui

* Fix tests.
This commit is contained in:
Tomasz Drwięga 2017-08-10 18:32:10 +02:00 committed by Gav Wood
parent ecd880c8e7
commit 65482c5e9d
5 changed files with 56 additions and 11 deletions

View File

@ -67,10 +67,20 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd
// Allow fonts from data: and HTTPS. // Allow fonts from data: and HTTPS.
b"font-src 'self' data: https:;".to_vec(), b"font-src 'self' data: https:;".to_vec(),
// Allow inline scripts and scripts eval (webpack/jsconsole) // 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) // 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. // Restrict everything else to the same origin.
b"default-src 'self';".to_vec(), b"default-src 'self';".to_vec(),
// Run in sandbox mode (although it's not fully safe since we allow same-origin and script) // 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() .into_iter()
.chain(embed.extra_embed_on .chain(embed.extra_embed_on
.iter() .iter()
.map(|&(ref host, port)| format!("{}:{}", host, port)) .map(|&(ref host, port)| address(host, port))
); );
let ancestors = if embed.host == "127.0.0.1" { let ancestors = if embed.host == "127.0.0.1" {

View File

@ -201,6 +201,7 @@ impl Middleware {
remote: Remote, remote: Remote,
ui_address: Option<(String, u16)>, ui_address: Option<(String, u16)>,
extra_embed_on: Vec<(String, u16)>, extra_embed_on: Vec<(String, u16)>,
extra_script_src: Vec<(String, u16)>,
dapps_path: PathBuf, dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>, extra_dapps: Vec<PathBuf>,
dapps_domain: &str, dapps_domain: &str,
@ -209,7 +210,7 @@ impl Middleware {
web_proxy_tokens: Arc<WebProxyTokens>, web_proxy_tokens: Arc<WebProxyTokens>,
fetch: F, fetch: F,
) -> Self { ) -> 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( let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar), hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status.clone(), sync_status.clone(),
@ -294,12 +295,14 @@ fn address(host: &str, port: u16) -> String {
fn as_embeddable( fn as_embeddable(
ui_address: Option<(String, u16)>, ui_address: Option<(String, u16)>,
extra_embed_on: Vec<(String, u16)>, extra_embed_on: Vec<(String, u16)>,
extra_script_src: Vec<(String, u16)>,
dapps_domain: &str, dapps_domain: &str,
) -> Option<ParentFrameSettings> { ) -> Option<ParentFrameSettings> {
ui_address.map(|(host, port)| ParentFrameSettings { ui_address.map(|(host, port)| ParentFrameSettings {
host, host,
port, port,
extra_embed_on, extra_embed_on,
extra_script_src,
dapps_domain: dapps_domain.to_owned(), dapps_domain: dapps_domain.to_owned(),
}) })
} }
@ -320,8 +323,10 @@ pub struct ParentFrameSettings {
pub host: String, pub host: String,
/// Port /// Port
pub port: u16, 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)>, 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) /// Dapps Domain (web3.site)
pub dapps_domain: String, pub dapps_domain: String,
} }

View File

@ -260,6 +260,7 @@ impl Server {
remote, remote,
signer_address, signer_address,
vec![], vec![],
vec![],
dapps_path, dapps_path,
extra_dapps, extra_dapps,
DAPPS_DOMAIN.into(), DAPPS_DOMAIN.into(),

View File

@ -20,6 +20,7 @@ use std::net::SocketAddr;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::cmp::max; use std::cmp::max;
use std::str::FromStr;
use cli::{Args, ArgsError}; use cli::{Args, ArgsError};
use util::{Hashable, H256, U256, Bytes, version_data, Address}; use util::{Hashable, H256, U256, Bytes, version_data, Address};
use util::journaldb::Algorithm; use util::journaldb::Algorithm;
@ -551,6 +552,10 @@ impl Configuration {
Ok(options) Ok(options)
} }
fn ui_port(&self) -> u16 {
self.args.flag_ports_shift + self.args.flag_ui_port
}
fn ntp_servers(&self) -> Vec<String> { fn ntp_servers(&self) -> Vec<String> {
self.args.flag_ntp_servers.split(",").map(str::to_owned).collect() self.args.flag_ntp_servers.split(",").map(str::to_owned).collect()
} }
@ -560,12 +565,15 @@ impl Configuration {
enabled: self.ui_enabled(), enabled: self.ui_enabled(),
ntp_servers: self.ntp_servers(), ntp_servers: self.ntp_servers(),
interface: self.ui_interface(), interface: self.ui_interface(),
port: self.args.flag_ports_shift + self.args.flag_ui_port, port: self.ui_port(),
hosts: self.ui_hosts(), hosts: self.ui_hosts(),
} }
} }
fn dapps_config(&self) -> DappsConfiguration { 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 { DappsConfiguration {
enabled: self.dapps_enabled(), enabled: self.dapps_enabled(),
ntp_servers: self.ntp_servers(), ntp_servers: self.ntp_servers(),
@ -575,11 +583,26 @@ impl Configuration {
} else { } else {
vec![] vec![]
}, },
extra_embed_on: if self.args.flag_ui_no_validation { extra_embed_on: {
vec![("localhost".to_owned(), 3000)] let mut extra_embed = dev_ui.clone();
} else { match self.ui_hosts() {
vec![] // 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,
} }
} }

View File

@ -40,6 +40,7 @@ pub struct Configuration {
pub dapps_path: PathBuf, pub dapps_path: PathBuf,
pub extra_dapps: Vec<PathBuf>, pub extra_dapps: Vec<PathBuf>,
pub extra_embed_on: Vec<(String, u16)>, pub extra_embed_on: Vec<(String, u16)>,
pub extra_script_src: Vec<(String, u16)>,
} }
impl Default for Configuration { impl Default for Configuration {
@ -56,6 +57,7 @@ impl Default for Configuration {
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(), dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
extra_dapps: vec![], extra_dapps: vec![],
extra_embed_on: vec![], extra_embed_on: vec![],
extra_script_src: vec![],
} }
} }
} }
@ -168,6 +170,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
configuration.extra_dapps, configuration.extra_dapps,
rpc::DAPPS_DOMAIN, rpc::DAPPS_DOMAIN,
configuration.extra_embed_on, configuration.extra_embed_on,
configuration.extra_script_src,
).map(Some) ).map(Some)
} }
@ -214,6 +217,7 @@ mod server {
_extra_dapps: Vec<PathBuf>, _extra_dapps: Vec<PathBuf>,
_dapps_domain: &str, _dapps_domain: &str,
_extra_embed_on: Vec<(String, u16)>, _extra_embed_on: Vec<(String, u16)>,
_extra_script_src: Vec<(String, u16)>,
) -> Result<Middleware, String> { ) -> Result<Middleware, String> {
Err("Your Parity version has been compiled without WebApps support.".into()) Err("Your Parity version has been compiled without WebApps support.".into())
} }
@ -251,6 +255,7 @@ mod server {
extra_dapps: Vec<PathBuf>, extra_dapps: Vec<PathBuf>,
dapps_domain: &str, dapps_domain: &str,
extra_embed_on: Vec<(String, u16)>, extra_embed_on: Vec<(String, u16)>,
extra_script_src: Vec<(String, u16)>,
) -> Result<Middleware, String> { ) -> Result<Middleware, String> {
let signer = deps.signer; let signer = deps.signer;
let parity_remote = parity_reactor::Remote::new(deps.remote.clone()); let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
@ -262,6 +267,7 @@ mod server {
parity_remote, parity_remote,
deps.ui_address, deps.ui_address,
extra_embed_on, extra_embed_on,
extra_script_src,
dapps_path, dapps_path,
extra_dapps, extra_dapps,
dapps_domain, dapps_domain,