Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CSP for securedrop.org #431

Merged
merged 2 commits into from
Apr 19, 2018
Merged

Add CSP for securedrop.org #431

merged 2 commits into from
Apr 19, 2018

Conversation

emkll
Copy link
Contributor

@emkll emkll commented Apr 6, 2018

Adds an initial CSP to securedrop.org website using django-csp to requirements and middleware.

CSP is mostly a defense-in-depth solution, and shouldn't break functionality. We can slowly iterate on this policy:

  • The big win here is that we disable unsafe-inline JavaScript.
  • Hashes are used for white listing inline style, which shouldn't break site functionality but could break appearance on upgrades. We should ensure there are no CSP style errors on upgrades.
  • unsafe-eval is required for client/common/js/common.js:645 and /client/tor/js/torEntry.js:89. We should consider fixing this, as it would be another big win.

We should also investigate using a report-uri (to which CSP violations are reported to) to both detect errors in the policy, and be aware of potential attack vectors (obviously while preserving user privacy and in line as the currently level of logging)

CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = (
"'self'",
'http://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harrislapiroff Can we bundle up these assets and serve directly? I'd prefer to omit the external call entirely, and drop the whitelist here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we can do that. This should only be used by the debug toolbar, but I think it's reasonable to want to have the CSP active even on dev.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll need someone to tell me if there are license concerns bundling this in our repo though when we eventually open source it...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked myself that jQuery is under MIT, so I believe this inclusion should be permissible regardless of how we ultimately license this repo. If for any reason we didn't want to include the file wholesale in the repo we could also add it as an npm dependency, but that seemed like a lot of overhead for this. I've added the file on its own in #439.

@conorsch
Copy link
Contributor

conorsch commented Apr 6, 2018

I'd very much like to implement the CSP in the nginx config, which would get us a single declaration that works on all the Django/Wagtail servers. Setting at the app level will require duplication in each website repo. Open to that, since as @emkll points out, it's essentially defense in depth.

@harrislapiroff
Copy link
Contributor

@conorsch I have two non-blocking concerns/questions about implementing this at the nginx level:

  1. I want to make sure that I catch conflicts with the CSP in dev. E.g., if I accidentally add an external JS file or something that's going to get blocked, I want to know that pretty quickly.

  2. Would that prevent us from hashing inline styles/javascript? We could mitigate that by always using javascript and CSS files, but it might require some wrangling, particularly with inline SVGs. We could also just allow all inline styles/js, but @emkll seemed negative on that when we spoke.

    (Related to that, actually, I do have some concern that our CSP practices have the potential to interfere with anything that requires dynamic inline styles. I don't think we have anything like that anywhere right now, but if we ever did anything with complex animation or data visualization it might become an issue. I think I need to understand the attacks and best practices around inline styles a little better, but we can also wait to cross that bridge if/when we get there.)

Add django-csp to requirements and middleware
Whitelisted inline scripts by hash and style to deny inline scripts and style.
Excluded /admin path for CSP as it was making very heavy use of inline JS.
unsafe-eval is required for client/common/js/common.js:645 and /client/tor/js/torEntry.js:89. We should consider fixing this, as it would allow XSS should user-supplied code finds it's way to an eval method.
@conorsch
Copy link
Contributor

I want to make sure that I catch conflicts with the CSP in dev. E.g., if I accidentally add an external JS file or something that's going to get blocked, I want to know that pretty quickly.

Absolutely. For now, you can trust that at least CI will catch CSP violations, via the dedicated Molecule "ci" scenario. As we push toward a more container-centric workflow, we can adapt the testing procedures to make sure we provide feedback as close as possible to the time of codechange. I'm optimistic that the app-level integration presented here will provide this extremely early.

Would that prevent us from hashing inline styles/javascript?

Ah, if you mean at the nginx level, yes, I suspect it would. Certainly not at the app level, though! (See changes.) The app-level changes will require more maintenance, but with substantially more control over the policy directives, which seems like a fine trade-off. @emkll will be available to consult on the CSP for all our sites, so lean heavily to get backup on decisions if you're ever unsure.

I do have some concern that our CSP practices have the potential to interfere with anything that requires dynamic inline styles.

Cross that bridge when we come to it. There's a compelling argument for more dynamic viz in both STN and PFT, but we don't have any work earmarked for that yet. Even when we do get to that point, we'll be glad of app-level integration for CSP.

No longer needed since
@harrislapiroff
Copy link
Contributor

I'm having a wacky time trying to figure out why we're getting all those evals in our generated javascript. I had assumed it was a result of using eval-source-map in our webpack config—but changing that to source-map didn't seem to make any difference. Will research further tomorrow.

@harrislapiroff
Copy link
Contributor

I still think that my theory is possibly correct, because I see evals in the code on dev, but I don't see it on staging.

@eloquence
Copy link
Member

@conorsch I'm trying to understand the state of the PR as you see it. Do you think this will be ready to merge as an interim solution? I notice that the PR contains significant hashing of inline CSS, which seems inappropriate for the nginx config, so if this isn't merged as-is, it sounds like we'd have to split cross-site CSP rules from per-site CSP rules? We're very close to launch, so I'm a little wary of keeping scope contained.

@conorsch
Copy link
Contributor

Didn't get to full review of this today, so a few off-the-cuff notes.

Do you think this will be ready to merge as an interim solution?

Yes, certainly. The changes are thin on docs. For example, how were the hashes for the assets obtained? Surely they were copy/pasted out of the web console during local development, but I don't see that procedure documented anywhere. When, not if, these break, we'll need to address that problem so more team members are comfortable with maintenance procedures here.

Some sort of dev-oriented tooling to check verify these settings are correct would be ideal. We don't have functional tests interacting with the website UI, so it'll be difficult to stay on top of any changes over time, as we munge the script and asset files. Still, I'm willing to put up with that friction as a candidate for ironing out the CSP workflow for inclusion across our other Wagtail sites.

Right now the failing tests need to be resolved—haven't spent time with those yet, will investigate starting tomorrow.

@harrislapiroff
Copy link
Contributor

Passing now. Two notes about the state of this PR:

  1. Currently it has in the policy unsafe-eval meaning that javascript eval() statements will run. This is necessary because of the way webpack is compiling our javascript, but it would be ideal to get rid of this. I'm looking into why webpack is using eval for compilation (you can see my comments above for the state of the research).
  2. Currently the CSP is blocking the django-debug-toolbar logo, because it's a base64 image. I think there should be some way to allow this (at least on dev) but I haven't figured it out yet. 😅

@harrislapiroff
Copy link
Contributor

It occurs to me that I should make a note on why tests are passing now. I think we have an inconsistent test failure somewhere. At a cursory glance it looks like it might be a result of certain model factories (which generate database objects with random content) sometimes generating objects that don't satisfy unique field constraints.

@conorsch
Copy link
Contributor

Update: I'm able to get trigger test failures when messing with some of the hardcoded hashes, but not all. In all cases, the CSP violation will be logged to the browser console, but that's not checked in testing, and would require human eyes to catch it.

I'll spend a moment this afternoon trying to cobble together barebones testing prior to merge here, just to give us confidence about not deploying breaking changes. Absent that, we'll need to be diligent about verifying changes locally when any assets are modified via PR. That sounds ripe for mistakes.

We'll also need to open follow-up issues based on the next steps @emkll outlines in the OP.

@conorsch
Copy link
Contributor

Currently the CSP is blocking the django-debug-toolbar logo, because it's a base64 image.

That I'm willing to put up with for now—the debug toolbar itself still displays fine, just the logo is missing. Fine. There's been substantial work on DDT to support CSP, e.g. django-commons/django-debug-toolbar@8b29127, so I'm optimistic we can iron out any kinks. Oddly I'm not seeing any CSP violations logged regarding DDT, so not fixing now. From the above, sounds like a data: addition to CSP_IMG_SRC is all that's needed, but I tried that locally and wasn't able to resolve.

This is necessary because of the way webpack is compiling our javascript, but it would be ideal to get rid of this.

For these and the other conditions @emkll notes in OP, I'll file follow-up issues. For now, I'm inclined to deploy what we have against staging and monitoring for changes. We'll need to verify no console errors during review whenever assets change. Ideally we can test for that, but hit my timebox on investigating that today, so will file a follow-up.

Copy link
Contributor

@conorsch conorsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's toss this at staging and bang on the hood. @harrislapiroff, will wait for your +1 before merge and deploy.

@harrislapiroff
Copy link
Contributor

I'm OK with merging this as is, given the ticket you opened 👍

@conorsch conorsch merged commit 7d12099 into master Apr 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants