Content Security Policy (#5790)

* Adding CSP headers.

* Adding Content-Security-Policy headers.

* Fixing test.

* CSP in ws server responses.
This commit is contained in:
Tomasz Drwięga 2017-06-28 09:12:02 +02:00 committed by Arkadiy Paronyan
parent 57626b60e7
commit c7a043b864
5 changed files with 52 additions and 7 deletions

View File

@ -38,15 +38,53 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]); headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
// Embedding header: // Embedding header:
if let Some(embeddable_on) = embeddable_on { if let Some(ref embeddable_on) = embeddable_on {
headers.set_raw( headers.set_raw("X-Frame-Options", vec![
"X-Frame-Options", format!("ALLOW-FROM http://{}", address(embeddable_on)).into_bytes()
vec![format!("ALLOW-FROM http://{}", address(&embeddable_on)).into_bytes()] ]);
);
} else { } else {
// TODO [ToDr] Should we be more strict here (DENY?)? // TODO [ToDr] Should we be more strict here (DENY?)?
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]); headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
} }
// Content Security Policy headers
headers.set_raw("Content-Security-Policy", vec![
// Allow connecting to WS servers and HTTP(S) servers.
// We could be more restrictive and allow only RPC server URL.
b"connect-src http: https: ws: wss:;".to_vec(),
// Allow framing any content from HTTP(S).
// Again we could only allow embedding from RPC server URL.
// (deprecated)
b"frame-src 'self' http: https:;".to_vec(),
// Allow framing and web workers from HTTP(S).
b"child-src 'self' http: https:;".to_vec(),
// We allow data: blob: and HTTP(s) images.
// We could get rid of wildcarding HTTP and only allow RPC server URL.
// (http require for local dapps icons)
b"img-src 'self' 'unsafe-inline' data: blob: http: https:;".to_vec(),
// Allow style from data: blob: and HTTPS.
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
// 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(),
// 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)
b"sandbox allow-same-origin allow-forms allow-modals allow-popups allow-presentation allow-scripts;".to_vec(),
// Disallow subitting forms from any dapps
b"form-action 'none';".to_vec(),
// Never allow mixed content
b"block-all-mixed-content;".to_vec(),
// Specify if the site can be embedded.
match embeddable_on {
Some((ref host, ref port)) if host == "127.0.0.1" => {
format!("frame-ancestors {} {};", address(&(host.to_owned(), *port)), address(&("localhost".to_owned(), *port)))
},
Some(ref embed) => format!("frame-ancestors {};", address(embed)),
None => format!("frame-ancestors 'self';"),
}.into_bytes(),
]);
} }

View File

@ -121,4 +121,8 @@ pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(), headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
"X-Content-Type-Options missing: {:?}", headers "X-Content-Type-Options missing: {:?}", headers
); );
assert!(
headers.iter().find(|header| header.starts_with("Content-Security-Policy: ")).is_some(),
"Content-Security-Policy missing: {:?}", headers
)
} }

View File

@ -41,7 +41,7 @@ export default class DappIcon extends Component {
src={ src={
app.type === 'local' app.type === 'local'
? `${dappsUrl}/${app.id}/${app.iconUrl}` ? `${dappsUrl}/${app.id}/${app.iconUrl}`
: `${dappsUrl}${app.image}` : `${app.image}`
} }
/> />
); );

View File

@ -64,7 +64,7 @@ describe('ui/DappIcon', () => {
it('renders other apps with correct URL', () => { it('renders other apps with correct URL', () => {
expect(render({ app: { id: 'test', image: '/test.img' } }).props().src).to.equal( expect(render({ app: { id: 'test', image: '/test.img' } }).props().src).to.equal(
`${DAPPS_URL}/test.img` `/test.img`
); );
}); });
}); });

View File

@ -140,6 +140,9 @@ fn add_security_headers(res: &mut ws::ws::Response) {
headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec())); headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec()));
headers.push(("X-XSS-Protection".into(), b"1; mode=block".to_vec())); headers.push(("X-XSS-Protection".into(), b"1; mode=block".to_vec()));
headers.push(("X-Content-Type-Options".into(), b"nosniff".to_vec())); headers.push(("X-Content-Type-Options".into(), b"nosniff".to_vec()));
headers.push(("Content-Security-Policy".into(),
b"default-src 'self';form-action 'none';block-all-mixed-content;sandbox allow-scripts;".to_vec()
));
} }
fn auth_token_hash(codes_path: &Path, protocol: &str, save_file: bool) -> Option<H256> { fn auth_token_hash(codes_path: &Path, protocol: &str, save_file: bool) -> Option<H256> {