Sanitize OR add Content-Security-Policy for SVGs

Created on 9 April 2017, over 7 years ago
Updated 23 October 2024, about 2 months ago

Problem/Motivation

People want to use SVGs on their websites. Unfortunately SVGs are a security disaster as they allow embedded CSS and JS. Which means that allowing users to upload SVGs should be viewed in the same was as allowing them to upload JS or create inline JS. That is to say that permission to do that should be require a permission that has a security warning.

Here is a really good article about SVGs - https://www.owasp.org/images/0/03/Mario_Heiderich_OWASP_Sweden_The_image... - the tldr; is

Allowing SVG for upload == allowing HTML for upload

Proposed resolution

Requiring additional permissions to upload SVGs is probably not going to happen since we don't have that type of granular permissions on fields. Also people want to make image styles play nice with SVGs - see #2652138: Image styles do not play nicely with SVGs β†’ .

Maybe we can leverage the content security policy header to prevent this. This is how github does it - see https://github.com/github/markup/issues/289.

Remaining tasks

Add Content Security Policy headers for SVGs using .htaccess and web.config
Consider adding warning to image fields when people allow SVGs to be uploaded.

User interface changes

None

API changes

None

Data model changes

None

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component

image.module

Created by

πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

Live updates comments and jobs are added and updated live.
  • Security improvements

    It makes Drupal less vulnerable to abuse or misuse. Note, this is the preferred tag, though the Security tag has a large body of issues tagged to it. Do NOT publicly disclose security vulnerabilities; contact the security team instead. Anyone (whether security team or not) can apply this tag to security improvements that do not directly present a vulnerability e.g. hardening an API to add filtering to reduce a common mistake in contributed modules.

Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    mr.baileys β†’ made their first commit to this issue’s fork.

  • Merge request !9919Add CSP headers for svg files. β†’ (Open) created by mr.baileys
  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    * Converted the patch to a MR, rerolled for 11.x
    * Added a test to confirm the presence of the CSP header for file downloads.

    I'm not sure we should be adding the CSP header (Content-Security-Policy: "default-src 'none') for none image/svg+xml downloads, as that seems out of scope for this issue and might break existing functionality.

  • Pipeline finished with Failed
    about 2 months ago
    Total: 135s
    #318213
  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    @mr.baileys: It should probably at least work properly with the "csp" module. An easy solution would be to the set the priority of the respond event such that our event handler is guaranteed to run after their event handler. Then we could check whether the header is already set and do nothing in this case and just hope that the user knows what they're doing.

  • Pipeline finished with Failed
    about 2 months ago
    Total: 135s
    #318243
  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    Re-titling, since the scope of this issue has shifted to adding a content security policy header, rather than sanitizing the file.

    @rgpublic: makes sense, I added a condition so that the default CSP header is only set if none has been set earlier. I think it's up to CSP and other contrib modules to alter/set handler priorities?

  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    @mr.baileys: Yes, you are probably right. Currently they don't do this (I just checked, I see no explicit priority in their code), but I hope/assume their code gets after ours anyway because this is in core and their code is in contrib. If not, they could easily solve this by adding a priority - so: fine for me.

  • πŸ‡ΊπŸ‡ΈUnited States DamienMcKenna NH, USA

    There's also seckit β†’ which also can set the CSP headers.

  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    The evil thing is: If we set sth. here and anyone keeps overwriting it they can inadvertently create a security hole because people can upload SVGs with a XSS payload again :-(

    But I don't want to spoil the party. Glad this is finally moving forward.

    In a perfect world, core would provide a CSP service that anyone can *add* their stuff to if they want so nothing gets destroyed except intentionally. But this is probably way out-of-scope for this issue...

    It's quite an unfortunate situtation, but OTOH seckit / csp etc. are not modules anyone not savvy with these security issues will probably install lightheartedly. So perhaps we can just assume IF they change sth. they should know that then THEY have full responsibility over what security problems arise. Perhaps these contrib module owners should be informed that this is happening and add an appropriate readme note or sth. to their installation instructions...

    Just my 0,02€

  • πŸ‡¨πŸ‡¦Canada xmacinfo Canada

    There's also seckit which also can set the CSP headers.

    Can we set the CSP heasers on both public and private served files? Is Seckit able to add CSP headers on files?

  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    @xmacinfo: for public files, I think the only way to add the header is through via web server directives (MR here includes a change to .htaccess for this reason)

  • πŸ‡¨πŸ‡¦Canada gapple

    πŸ‘‹ CSP module maintainer here.
    CSP module provides APIs for modules to amend a site's policy β†’ . In most cases it's pretty straightforward, but gets more involved for cases where it's not just adding an additional external domain to a directive. The goal of the module is that anyone can install the module for a security improvement, that it's easily configurable for site builders to add exceptions not captured automatically (& ideally harden the default config with basic knowledge), but also flexible for developers to handle any more complex uses (and to do so safely without breaking other site functionality).
    Module subscribers with the same priority will execute after core, so it's the module's responsibility to preserve/amend/replace any value previously set by core.

    CSP has its own issue for applying a different policy to private files ( ✨ Provide different CSP policy for private files Active ), though there hasn't been any work done on it so far. If a special value is added to core for SVGs, then I'll update the module to match. If there's consensus on what should be added to core, I'll probably have a new release of the module before it's committed to core πŸ™‚.
    Alternately, the CSP module would be a good way to evaluate a potential solution (at least for private files, I'm not sure about the module altering htaccess), with users who have opted in to using a policy on their site. It would have to be an optional feature for the current major version, but could be enabled by default for new modules installs (and a site status message could prompt existing sites to try enabling it).

    This issue for core would add a static policy that can be configured in a service parameter: πŸ“Œ Add a default CSP and clickjacking defence and minimal API for CSP to core Active
    Instead of providing a single policy set, maybe it should be keyed policies to allow for additional cases (e.g. default, private-files, svg, etc, each with report-only and enforced values).

  • The Needs Review Queue Bot β†’ tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    Tried to address the feedback from the bot in #48.

  • Pipeline finished with Failed
    10 days ago
    Total: 601s
    #366786
  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    There were some test fails.

    The core.services.yml definition needed to be updated after πŸ“Œ Add default autoconfigure to all *.services.yml and remove event_subscriber tags Fixed .
    Since this issue is about updating the htaccess we also need to update the scaffold htaccess.

    The other test fails didn't look related to me, but I'm not sure.

  • Pipeline finished with Failed
    10 days ago
    Total: 701s
    #366924
  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    Great there's finally so much progress on this. SVGs are an important part of modern scalable webistes. Just read the issue description again, though, after all that time and noticed:

    * web.config
    *Consider adding warning to image fields when people allow SVGs to be uploaded.

    Both still valid IMHO. We'd need the web.config to support Windows servers. This is in scope for this issue, I guess.

    And - the second point - if people finally allow SVGs to be uploaded a warning should be visible to double-check whether these restrictions actually work. If e.g people haven't enabled AllowOverride on their Apache server, all hell breaks lose. This is such a serious security hazard that we shouldn't take this too light-heartedly. Perhaps a reference to a drupal.org-hosted page where people can download an example evil.svg to easily test their installation? This could be spun-off into a separate issue of course. It might be out-of-scope for this issue, but I still think such a warnig is very important before we can actually reap the benefits i.e. people finally being able to actually upload SVGs without worrying too much about security.

  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    We'd need the web.config to support Windows servers. This is in scope for this issue, I guess.

    If this is D11 only, then we are no longer shipping web.config β†’ , so no changes required unless we backport to D10.

  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    Backporting to 10.x can be a followup, IMO, so I removed the web.config from the issue summary.

    Consider adding warning to image fields when people allow SVGs to be uploaded.

    It seems better for this issue to ship the CSP and consider that good enough. I removed the idea bout labels from the issue summary as well.

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Very neat!

    Think we will need a change record for the new service and maybe a 2nd CR for the change to htaccess? Know we got a few projects where we disabled the scafold of that so we would have to manually add that change.

  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    Although it'd only become relevant IF people actually uploaded SVGs - and that would IMHO still require a lot more than a mere CR (Explicit warning, Test SVG provided etc). I hope people won't allow it right now without that protection. This issue can just be considered a first step. It only tightens security where there wasn't any security whatsoever up until now. This should perhaps at least be clarified in the CR. People can already add these lines in their htaccess if they want to if they have auto-scaffolding disabled for some reason. It doesn't mean (by itself) that they should/could already allow SVG uploads... Just my 0,02€

  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    I added change records as suggested in #54. Thanks for the review!

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Fixing link

  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    Thanks, alexpott. I did a bit more of an update to the issue summary to make it more of a focused summary of the conversation and current status.

    There's currently a failing test and I'm unsure if it's related or what the cause is.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @greggles I retested - it was a random fail. So all green again.

  • πŸ‡ΊπŸ‡ΈUnited States phenaproxima Massachusetts

    I did a code review here and everything looks good. No complaints.

    I cannot attest to the quality of the fix itself because content security policy stuff is very much not my forte. But if it's good enough for GitHub, and the security team members who are in this issue and have reviewed, then I trust their judgment.

    Tests are green, so I say ship it.

  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    Thanks @phenaproxima!

    mr.baileys and I are doing another round of testing and will ping others to get more eyes on the security component of this.

  • Pipeline finished with Failed
    5 days ago
    Total: 824s
    #370340
  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    I did some more manual testing and realized that the previous rule allowed `malicious.sVg` to bypass the protection. I updated the htaccess rule to be case insensitive. I've now manually confirmed this protection works on public and private svg files.

    Thanks for the feedback, @larowlan. I accepted the one suggested change.

    The proposed new header for all private files was introduced by gapple (CSP maintainer) so it feels pretty likely to be widely acceptable.

  • Pipeline finished with Success
    5 days ago
    Total: 1403s
    #370339
  • πŸ‡¨πŸ‡¦Canada gapple

    Applying default-src 'none' to other private files should only have an impact if sites were serving HTML files, and would be irrelevant to other responses like image files, so it might be worth leaving for discussion in πŸ“Œ Add a default CSP and clickjacking defence and minimal API for CSP to core Active or breaking out to a separate issue. It could then be done in combination to applying the same change for the files directory htaccess so that private and public files would have consistent behaviour, like SVGs would with this issue.

  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    Thanks for your thoughts, gapple, and setting back to RTBC.

    I forgot to mention I confirmed this is the header used by github for svgs content they serve.

    I agree with gapple's comments that the change to all private binaryresponses could be broken out to another issue and we would still have most of the value in this issue.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    I definitely think this issue should be only about setting a CSP for SVG files - the bit where we add a default CSP for all other private files needs discussion and additional thought.

  • Pipeline finished with Failed
    5 days ago
    Total: 588s
    #370636
  • Pipeline finished with Failed
    5 days ago
    Total: 649s
    #370641
  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    MR is green again and I addressed the feedback in #65.

  • πŸ‡³πŸ‡±Netherlands heine

    Some questions:

    • Could the .htaccess change interfere with the use of typical theme-provided SVGs?
    • Do SVGs pose risks for users outside of an http request, eg when downloading them and viewing them? This risk of course exists with other file types, but less so for jpg/png images.
    • Are typical XML-issues such as XXE, XSLT a possible issue, if so, in what contexts?
    • Are we sure all possible threats from SVGs in img/object/background contexts are tackled? Which specs apply?
  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    Some other questions to consider - I've added numbers to them to facilitate discussion.

    1 Could the .htaccess change interfere with the use of typical
    theme-provided SVGs?

    Great question. I don't think it's very common for svgs to use these features (I've only seen it once on a non-Drupal mapping site) but it made me realize the change could be inside of the htaccess for the files directory and therefore only apply to a subset of files. That feels like it reduces the risk this change will negatively impact while still retaining most of the value in protecting sites from malicious files. I asked mr.baileys and he agreed on that idea, but we could certainly discuss it more.

    2 Do SVGs pose risks for users outside of an http request, eg when downloading them and viewing them? This risk of course exists with other file types, but less so for jpg/png images.

    I think they do pose a risk, but solving it feels like a separate problem to me. I filed ✨ Filter SVGs before uploading by default Postponed and linked it to this as related for followup that could address this risk. The situation today is that site admins are enabling SVG uploads on their site often and there is no protection for a variety of risks that creates. This issue attempts to solve one of the risks (XSS), but is not a complete protection against all risks.

    3 Are typical XML-issues such as XXE, XSLT a possible issue, if so, in what contexts?

    As far as I understand those risks they are not relevant on a typical Drupal site and would only be relevant on sites that parse the XML of the SVG file. These changes don't affect that for better/worse.

    * Are we sure all possible threats from SVGs in img/object/background contexts are tackled? Which specs apply?

    I'm not sure this mitigates all possible threats (several additional threats are mentioned in this issue already). I'm sure the change in this issue is progress in securing sites against cross site scripting, but not a solution to all risks of SVG files.

  • Pipeline finished with Success
    4 days ago
    Total: 449s
    #371630
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @greggles great idea only changing the .htaccess in the files directory. This move made me think 2 things.

    1. We need a update function to rewrite existing sites .htaccess files I think
    2. What about sites which use nginx or another server... what would be neat is something on the status report that confirms this protection is working. That way we could point to documentation on how to set this up.

  • Pipeline finished with Success
    4 days ago
    Total: 451s
    #372504
  • Pipeline finished with Failed
    3 days ago
    Total: 512s
    #373555
  • Pipeline finished with Success
    3 days ago
    Total: 342s
    #373587
  • Pipeline finished with Canceled
    3 days ago
    Total: 307s
    #373611
  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    I added an update hook (system_update_11103) to update existing .htaccess files.

  • Pipeline finished with Failed
    3 days ago
    Total: 402s
    #373618
  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    2. What about sites which use nginx or another server... what would be neat is something on the status report that confirms this protection is working. That way we could point to documentation on how to set this up.

    I added a hook_requirements-check to test for the presence of a CSP-header on public SVG-files. I'm assuming that if there is a CSP-header, the site owner knows what they are doing, if it is missing we issue the warning. We could make it more strict and check the contents of the header if necessary.

    Currently linking to the change record for more information.

  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)
  • Pipeline finished with Failed
    3 days ago
    Total: 140s
    #373732
  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    I don't think that will work properly: The URL of the website would have to be specified from the command line. That's not necessarily the case. Neither is it guaranteed that the web server is really routed in such a way that it can call itself. And the check will only check for the existence of any header. Not that the protection really works. So, I have some doubts it's the best approach. IMHO much better would be a test on /admin/reports/status. This test could be an entry that is initially always under the "Warnings" category and says

    "SVG security could not be tested properly. Please make sure you have an appropriate Content-Security-Policy header configured before allowing the upload of SVG files".

    If there are any JS errors or JS is disabled in the browser this warning will just remain. We won't convey a false impression of safety.

    Then there's an object-tag after that entry including an SVG file under public:// with a JS payload. An object tag and not an img tag because in this case scripts are executed. That JS payload would remove that entry and put a different entry under the "OK/Verified section".

    Then there's a fallback JS that would move it to the "Error" category and say: "SVG security test failed."

    A check along these lines would be way better IMHO. It avoids the problems mentioned above and is really a functional test that will make absolutely sure that scripts are not executed.

  • πŸ‡§πŸ‡ͺBelgium mr.baileys πŸ‡§πŸ‡ͺ (Ghent)

    @rgpublic: I understand your concerns, consider that code a poc (I think this might also be a candidate to be handled in a follow-up issue)

    A check along these lines would be way better IMHO. It avoids the problems mentioned above and is really a functional test that will make absolutely sure that scripts are not executed.

    Would there be a risk that the JS-test gives a false positive: embedded scripts blocked due to browser/browser config on the administrator's machine, while not blocked for other contexts/browsers/users? (compared to checking for the header, which guarantees that the change made in this issue works across the board)

  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    @mr.baileys: A very valid concern IMO, thanks for bringing this up! The server-based check, however, could also lead to false positives: If the CSP header is there but configured in such a way that it won't properly apply we have a problem right there. In this case we'd need to parse the whole CSP and see if it really applys. Or if the URL leads to a proxy and the proxy returns a different/404 page which has that header etc. Just two problems I see with a server-based check (apart from the other problems mentioned). We *could* in theory add a different, JS based check and fetch the header with XMLHttpRequest from JS. This would circumvent at least the domain not reachable/domain not configured from the command line problem and give an additional line of security.

    If we checked that JS inside the object SVG is not executed AND fetch the headers via JS and check whether they are really there, I think this would be quite safe.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @rgpublic system_requirements is how admin/reports/status is generated - I agree we should only do this when not in the CLI. We already have quite a few requirements that only run when if ($phase === 'runtime' && PHP_SAPI !== 'cli') {

    The current implementation in system_requirements is problematic because of adding the file to public... and then removing it. That's not free. Not sure I have a better idea yet. And maybe that means punting the system_requirement part to a follow-up is a good idea.

  • πŸ‡ΊπŸ‡ΈUnited States greggles Denver, Colorado, USA

    The concept of the check for CSP header on the status page feels a like a pretty big additional functionality for core. It seems like something security_review could handle, so I filed ✨ Check for CSP on private and public SVG files Active to track it there.

  • πŸ‡©πŸ‡ͺGermany rgpublic DΓΌsseldorf πŸ‡©πŸ‡ͺ πŸ‡ͺπŸ‡Ί

    @alexpott: Ah, understood. Thanks for the clarification. I'm not sure if it's necessary to remove the test file again. It could be created if it doesn't exist yet and otherwise just used. Still, I think a JS based approach would be safer. At the very least the CSP header should be checked for more than its mere existence, I guess.

  • The Needs Review Queue Bot β†’ tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

  • Pipeline finished with Canceled
    2 days ago
    Total: 379s
    #374334
  • Pipeline finished with Failed
    2 days ago
    Total: 2281s
    #374340
Production build 0.71.5 2024