Sanitize innert document #12524

Closed
wants to merge 10 commits into
from

Projects

None yet

7 participants

@IgorMinar
Member

No description provided.

@googlebot

We found a Contributor License Agreement for you (the sender of this pull request) and all commit authors, but as best as we can tell these commits were authored by someone else. If that's the case, please add them to this pull request and have them confirm that they're okay with these commits being contributed to Google. If we're mistaken and you did author these commits, just reply here to confirm.

@googlebot googlebot added the cla: no label Aug 7, 2015
@IgorMinar IgorMinar added cla: yes and removed cla: no labels Aug 7, 2015
@IgorMinar
Member

hmm.. tests are failing because different browsers enumerate over the list of tags in a different order which results in non-deterministic. I'll either have to sort the attribute list or change the matcher to account for this.

@lgalfaso
Member
lgalfaso commented Aug 8, 2015

@IgorMinar even after sorting the attributes, there are two test failures with IE9

IE 9.0.0 (Windows 7) HTML htmlParser should throw badparse if text content contains "<" followed by an ASCII letter without matching ">" FAILED
    Expected { tag : 'a', attrs : { body : '', bar< : '' } } to equal undefined.
IE 9.0.0 (Windows 7) HTML htmlParser should parse empty value attribute of node FAILED
    Expected null to equal { tag : 'option', attrs : { selected : '', value : '' } }.

I think it should be possible to fix the second issue with something like https://github.com/angular/angular.js/blob/master/src/jqLite.js#L159
The first issue, I am not sure on how to work around it

@googlebot googlebot added cla: no and removed cla: yes labels Aug 11, 2015
@IgorMinar IgorMinar added cla: yes and removed cla: no labels Aug 11, 2015
@googlebot googlebot added cla: no and removed cla: yes labels Aug 11, 2015
@gkalpak gkalpak and 1 other commented on an outdated diff Aug 17, 2015
src/ngSanitize/sanitize.js
@@ -153,6 +154,42 @@ function $SanitizeProvider() {
return buf.join('');
};
}];
+
+
+ /**
+ * @ngdoc method
+ * @name $sanitizeProvider#enableSvg
+ * @kind function
+ *
+ * @description
+ * Enables a subset of svg to be supported by the sanitizer.
+ *
+ * **Warning**: By enabling this setting without taking other precautions, you might expose your
+ * application to click-hijacking attacks. In these attacks, a sanitize svg could be positioned
@gkalpak
gkalpak Aug 17, 2015 Member

sanitize svg ??

@gkalpak gkalpak and 1 other commented on an outdated diff Aug 17, 2015
src/ngSanitize/sanitize.js
+ * application to click-hijacking attacks. In these attacks, a sanitize svg could be positioned
+ * outside of the containing element and be rendered over other elements on the page (e.g. a login
+ * link). Such behavior can then result in phishing incidents.
+ *
+ * To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg
+ * tags within the sanitized content:
+ *
+ * ```
+ * .rootOfTheIncludedContent svg {
+ * overflow: hidden !important;
+ * }
+ * ```
+ *
+ * @param {boolean=} regexp New regexp to whitelist urls with.
+ * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called
+ * without a argument or self for chaining otherwise.
@gkalpak
gkalpak Aug 17, 2015 Member

a --> an

@IgorMinar
IgorMinar Sep 8, 2015 Member

fixed. thanks.

@petebacondarwin petebacondarwin added this to the 1.4.x milestone Sep 10, 2015
@petebacondarwin petebacondarwin commented on the diff Sep 11, 2015
.travis.yml
@@ -19,10 +19,10 @@ env:
- JOB=docs-e2e BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
- - JOB=unit BROWSER_PROVIDER=browserstack
- - JOB=docs-e2e BROWSER_PROVIDER=browserstack
- - JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
- - JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
+# - JOB=unit BROWSER_PROVIDER=browserstack
+# - JOB=docs-e2e BROWSER_PROVIDER=browserstack
+# - JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
+# - JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
@petebacondarwin
petebacondarwin Sep 11, 2015 Member

Is this disabling just temporary or should we give up with browserstack altogether?

@IgorMinar
IgorMinar Sep 11, 2015 Member

I think it's just temporary

@petebacondarwin petebacondarwin commented on an outdated diff Sep 11, 2015
src/ngSanitize/sanitize.js
@@ -153,6 +154,42 @@ function $SanitizeProvider() {
return buf.join('');
};
}];
+
+
+ /**
+ * @ngdoc method
+ * @name $sanitizeProvider#enableSvg
+ * @kind function
+ *
+ * @description
+ * Enables a subset of svg to be supported by the sanitizer.
+ *
+ * **Warning**: By enabling this setting without taking other precautions, you might expose your
@petebacondarwin
petebacondarwin Sep 11, 2015 Member

I would put this warning inside a <div class="alert alert-warning"> block to make it stand out more.

@petebacondarwin petebacondarwin and 1 other commented on an outdated diff Sep 11, 2015
src/ngSanitize/sanitize.js
@@ -262,11 +289,30 @@ function makeMap(str, lowercaseKeys) {
return obj;
}
+var inertBodyElement;
+(function(window) {
+ var doc;
+ if (window.document && window.document.implementation) {
+ doc = window.document.implementation.createHTMLDocument("inert");
+ } else {
+ throw $sanitizeMinErr('noinert', "Can't create an inert html document");
+ }
+ var docElement = doc.documentElement || doc.getDocumentElement();
+ var bodyElements = docElement.getElementsByTagName('body');
+ if (bodyElements.length === 1) {
@petebacondarwin
petebacondarwin Sep 11, 2015 Member

Is it possible that bodyElements can have length > 1? If so, can we not just take the first one?

@IgorMinar
IgorMinar Sep 11, 2015 Member

usually there should be only one body, but in IE there is none, so we need to create it. I'll add a comment to that code

@petebacondarwin petebacondarwin and 1 other commented on an outdated diff Sep 11, 2015
src/ngSanitize/sanitize.js
+ html = inertBodyElement.innerHTML; //trigger mXSS
+ inertBodyElement.innerHTML = html;
+ } while (html !== inertBodyElement.innerHTML);
+
+ var node = inertBodyElement.firstChild;
+ while (node) {
+ switch (node.nodeType) {
+ case 1: // ELEMENT_NODE
+ handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
+ break;
+ case 3: // TEXT NODE
+ handler.chars(node.textContent);
+ break;
+ }
+ var nextNode;
+ if (!(nextNode = node.firstChild)) {
@petebacondarwin
petebacondarwin Sep 11, 2015 Member

I think this block can be simplified:

    var nextNode;
    if (!(nextNode = node.firstChild)) {
      if (node.nodeType == 1) {
        handler.end(node.nodeName.toLowerCase());
      }
      nextNode = node.nextSibling;
      if (!nextNode) {
        while (nextNode == null) {
          node = node.parentNode;
          if (node === inertBodyElement) break;
          nextNode = node.nextSibling;
          if (node.nodeType == 1) {
            handler.end(node.nodeName.toLowerCase());
          }
        }
      }
    }
    node = nextNode;
@IgorMinar
IgorMinar Sep 11, 2015 Member

nice catch

@petebacondarwin
Member

A few small comments but otherwise LGTM

@petebacondarwin
Member

How far back can this be backported?

@IgorMinar
Member

@petebacondarwin since there are breaking changes included in here, I don't think that we should backport this to older versions. It's safer to keep it in master only.

mhevery and others added some commits May 1, 2015
@mhevery @IgorMinar mhevery refactor($sanitize): new implementation of the html sanitized parser
This implementation is based on using inert document parsed by the browser

Closes #11442
Closes #11443
a59f423
@IgorMinar IgorMinar fix($sanitize): support void elements, fixups, remove dead code, typos 45bc1fa
@IgorMinar IgorMinar feat($sanitize): make svg support an opt-in
BREAKING CHANGE: The svg support in  is now an opt-in option

Applications that depend on this option can use  to turn the option back on,
but while doing so, please read the warning provided in the  documentation for
information on preventing click-hijacking attacks when this option is turned on.
8cff6eb
@IgorMinar IgorMinar fix($compile): properly sanitize xlink:href attribute interoplation ab3dfaf
@IgorMinar IgorMinar chore(travis): disable browserstack builds for now
we don't really pay attention to them anyway.
eb86054
@IgorMinar IgorMinar fix($sanitize): add mXSS protection 76f7926
@IgorMinar IgorMinar fix($sanitize): strip urls starting with 'unsafe:' as opposed to 'uns…
…afe'
60bad80
@IgorMinar IgorMinar refactor($sanitize): remove <script> from valid block elements
the script and style tag are explicitly blacklisted, so this doesn't change any functionality.
the change is done to improve code clarity.
919b1b2
@IgorMinar IgorMinar refactor(): rename local variables to improve code clarity 4328f80
@IgorMinar IgorMinar test($sanitize): add a test to prove that html comments are being str…
…ipped
2b3a724
@mhevery mhevery added a commit that closed this pull request Sep 18, 2015
@mhevery @petebacondarwin mhevery + petebacondarwin refactor($sanitize): new implementation of the html sanitized parser
This implementation is based on using inert document parsed by the browser

Closes #11442
Closes #11443
Closes #12524
35a2153
@mhevery mhevery closed this in 35a2153 Sep 18, 2015
@IgorMinar IgorMinar added a commit that referenced this pull request Sep 18, 2015
@IgorMinar @petebacondarwin IgorMinar + petebacondarwin feat($sanitize): make svg support an opt-in
Closes #12524

BREAKING CHANGE: The svg support in  is now an opt-in option

Applications that depend on this option can use  to turn the option back on,
but while doing so, please read the warning provided in the  documentation for
information on preventing click-hijacking attacks when this option is turned on.
181fc56
@IgorMinar IgorMinar added a commit that referenced this pull request Sep 18, 2015
@IgorMinar @petebacondarwin IgorMinar + petebacondarwin chore(travis): disable browserstack builds for now
we don't really pay attention to them anyway.

Closes #12524
9b84fca
@IgorMinar IgorMinar added a commit that referenced this pull request Sep 18, 2015
@IgorMinar @petebacondarwin IgorMinar + petebacondarwin fix($sanitize): add mXSS protection
Closes #12524
bc0d8c4
@IgorMinar IgorMinar added a commit that referenced this pull request Sep 18, 2015
@IgorMinar @petebacondarwin IgorMinar + petebacondarwin refactor($sanitize): remove <script> from valid block elements
the script and style tag are explicitly blacklisted, so this doesn't change any functionality.
the change is done to improve code clarity.

Closes #12524
2c22c57
@h3rald
h3rald commented Sep 29, 2015

@IgorMinar, @petebacondarwin

Hi, what do you mean with "it's safer to keep it in master only"? Is there any plan to include this security fix in a 1.4.x release?

@petebacondarwin
Member

safer to keep it in master only

This means that since there are breaking changes, trying to backport it to older versions could cause havoc with live applications. The message is that if you are concerned about these security features then you should upgrade to 1.5 as soon as it comes out.

@h3rald
h3rald commented Sep 29, 2015

Hi Pete, thanks a lot for the clarification.

I read through the commits and the only breaking change seems to be the fact that SVG sanitization is not enabled by default (and if enabled you recommend extra care), right?

The fact that you are now relying on the browser itself to parse HTML code by creating an inert HTML document should not be too much of a problem because AFAIK this functionality is available in all major browsers (including IE9+...

I was notified of this fix by a security notification issued throughout my company which recommended that we apply the patch manually as a temporary workaround until the fix is officially distributed.
If the fix is not applied, apparently it may be possible to perform a XSS attack in certain conditions -- is it right?

Thanks again for your time!

P.S.: I actually tried to apply the fixes to v1.4.3 and it doesn't seem to break much (I still have to test it with directives using SVG though).

@petebacondarwin
Member

I'll have a further chat with @IgorMinar about this.

@IgorMinar
Member

aside from the option to disable svg support (which is not a security issue if you set overflow: hidden on the element containing the sanitized output), I don't believe that there is a reason for considering the previous implementation insecure. The new implementation is simpler and easier to maintain going forward.

The reason why we didn't backport this to 1.4 was that the new implementation could parse html slightly differently which could result in hard-to-debug issues in the rendered output (but not security vulnerabilities).

@h3rald
h3rald commented Sep 30, 2015

Hi Igor,

Thanks a lot for your comment: it really clarifies things a lot!

My original concern about backporting was due to the fact that this particular pull request was notified to me as a "fix" for possible vulnerabilities affecting Angular sanitization. Based on your comments (and also after doing a bit of testing with both implementations at this point) I think that perhaps the security notification I received was perhaps a bit misleading or simply tagged incorrectly. I will investigate further within my company on why this happened.

Thanks a lot for your support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment