Search code examples
angularxsshtml-escape-characterscheckmarxcross-site

How to secure from XSS for services consumed from Angular and non-Angular clients


This question is for an app using an Angular UI (v8.2.14) calling Spring/Java services. I'm trying to figure out how to be secure from XSS vulnerabilities in the most efficient way, but there seem to be some contradictory issues. Here's an example to explain:

  1. The Java service is going to return this string as part of the response: "John & Terri".

  2. We scan for XSS vulnerabilities using Checkmarx and moving to Contrast. Both report that the string should be sanitized and escaped before returning it in a response. So I do the following before returning it: String stringForResponse = StringEscapeUtils.escapeHtml("John & Terri"); //using apache commons library. The security scans are now happy with the server-side java code.

  3. The Angular now displays the string as "John & Terri". A little research in the Angular docs reveals that Angular does the sanitizing and escaping FOR US, unless we tell it not to. So, it appears that the string is being escaped twice, then the browser "un-escapes" it once, and thus instead of "&" we get "&" displayed. (of course, this would apply to any other characters that get escaped by the string util method.

  4. We can resolve this by using the following in the UI code, which tells Angular that you have done the sanitize/escaping yourself and Angular should not repeat it: domSanitizer.bypassSecurityTrustHtml(variableThatIsDisplayedInUI); This works and "John & Terri" is displayed properly. However, I have the following concerns with implementing this as our XSS approach throughout the application:

A. This is a lot of work just to make the security scanner happy, when Angular was already handling the XSS issue anyway. B. Angular docs warns this should be rarely used for special cases. I translate that to "Angular is stable and very well tested by all of us using the framework. Doubtful your XSS code will be as thorough." I would agree with that sentiment. C. However, some of our services are also consumed by HTTP clients other than the Angular framework. So, if we can't trust that those other clients are going to sanitize our response, then it sounds like we should sanitize everything in responses.

PROPOSED SOLUTION: The only thing I can think of to satisfy all these constraints is to provide two sets of endpoints to the backend Java services:

  • one set of endpoints to be used only by the angular UI. We would not escape the responses of these endpoints and rely on Angular to take care of it.
  • one set of endpoints for all other consumers of the services. The logic of each of these endpoints would call the corresponding "angular only" endpoint, then take the response from it and escape all of its string fields, before returning the response. And of course, we would just have to ignore the code scanners when they report XSS issues for the endpoints that we have designated for Angular-UI use.

I'd appreciate advise on our proposed solution or any alternative. Thanks!


Solution

  • Don't create a separate API exclusively for the Angular app

    If the resources are returning escaped characters, the UI can always use [innerHtml] to render this text. Angular will sanitize the data passed to innerHtml by removing any scripts or potentially dangerous characters, but it will allow HTML character entity references like &

    <span [innerHtml]="responseText"></span>
    

    If there is additional text you need to display in the UI that is being escaped/removed by Angular's sanitization process you can create a "safe" pipe to bypass this (once you are sure the data from the API is indeed safe)

    Example of a safe pipe