7

I have a translation key which is actually a HTML code, both encoded as well as unencoded.

   $scope.translations = {
    "html_code" : "<script>alert('Alert!');</script>",
    "html_code_full" : "<script>alert('Alert!');</script>",
    "greeting" : "Welcome!" 
  }

When I use these values to display the translated text in view, I use two methods:

  1. As directive <span translate>{{translations.html_code}}</span>
  2. As filter {{translations.html_code|translate}}

I try the same for translations.html_code_full. Here's the code for view:

translations.html_code = {{translations.html_code|translate}}
translations.html_code = <span translate>{{translations.html_code}}</span>

translations.html_code_full = {{translations.html_code_full|translate}}
translations.html_code_full = <span translate>{{translations.html_code_full}}</span>

This is the output I get:

translations.html_code = &lt;script&gt;alert('Alert!');&lt;/script&gt; 
translations.html_code = <script>alert('Alert!');</script> 


translations.html_code_full = <script>alert('Alert!');</script> 
translations.html_code_full =

As it's clear that directive implementation is encoding the translation key to HTML, but filter is not. Why is this difference in output between directive vs filter implementation? And why am I not getting an alert if it's rendering the HTML?

Here is the plunk I created for demo.

Aniket Sinha
  • 6,001
  • 6
  • 37
  • 50

2 Answers2

1

The AngularJS frame work is protecting your application from XSS attacks.

Cross-site scripting carried out on websites accounted for roughly 84% of all security vulnerabilities documented by Symantec as of 2007.

-- Wikipedia - Cross-site scripting

So what are you really trying to do? Maybe we can show you how to do it in a safe manner.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • That is exactly what I was trying to do. In another web app, I was able to get the alert, but somehow I was unable to reproduce in the demo. I just wanted to know the *safer* way to use translate i.e filter vs directive, to prevent XSS. – Aniket Sinha Jan 20 '16 at 09:25
  • A good place to start is the [AngularJS Developer Guide - Security](https://docs.angularjs.org/guide/security). Then look at the [AngularJS $sce API Reference](https://docs.angularjs.org/api/ng/service/$sce). SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier. – georgeawg Jan 20 '16 at 10:10
  • 2
    i have the same issue. i can reproduct a css attack locally in my app with the translate DIRECTIVE while filter seems to be safe - but if i try to make a plunkr reflecting this issue the css doesn't work and the script gets filtered out correctly. tried versions 2.8.1 and 2.9.0 of ng-translate. tried different sanitaze strategies, but didn't work either. i wonder if there is a security hole for the translate directive out there. – Patrick Kelleter Jan 27 '16 at 17:30
  • for me if I use the filter it will catch a attack. But then it fails for a #/{{{}.")));alert(1)//";}} style attack. If I use the directive it catches the latter but fails for the former!!!!!! – Gurnard Nov 07 '17 at 16:32
  • @Gurnard I'm unable to understand `#/{{{}.")));alert(1)//";}} `. Can you explain this piece of code, apart from alert of course? – Aniket Sinha Nov 07 '17 at 21:58
  • Basically this can be added to the end of the URL in place of a state parameter. When this is then output as a value in a template it will execute the javascript within as an expression. – Gurnard Nov 08 '17 at 09:11
0

For me the translate library did not cover all the instances. So I needed to cover a normal script attack and an Angular aimed attack. We read part of a URL as a key and then display that as a message so open to attack in the URL.

The 2 URLs that I tested against:

/mypage.html#/{{{}.")));alert(1)//";}}

mypage.html#/%3Cscript%3Ealert('foo')%20%3C/script%3E

For me the first one would be escaped correctly if using the translate directive in the page but not the filter. The second one was a successful attack when using the directive in the page but was escaped correctly when using the filter.

In the end I escaped, translated and checked in the controller before assigning the value to the scope variable. So now there is no translate directive or filter in the page.

   function translateParam(paramData, defaultKey){
        var deferred = $q.defer();
        var trusted =  $sce.trustAsHtml(paramData);
        $translate(trusted).then(
            function(translatedString){
                if(translatedString == trusted){
                    //no key has been found so show a default
                    deferred.resolve($translate.instant(defaultKey));
                }
                else{
                    //the key has been found correclty
                    deferred.resolve(translatedString);
                }
            }
        );
        return deferred.promise;
    }

//called like this
  $scope.error = translateParam('keyFromURL','defaultKeyInTranslateFile');

You need to inject $q, $sce and $translate. For me this is the most secure and reliable solution.

Gurnard
  • 1,773
  • 22
  • 43