Application Security

How To Protect Your Angular.js Application With Jscrambler

January 15th, 2018 | By Carlos Matias | 6 min read

Are you seeking solutions to ensure your Angular.js app is safe? See how to protect your Angular.js Application with Jscrambler.

Although AngularJs, Angular’s first version, is a few years old, it is still responsible for powering a lot of enterprise applications. It helps you build solid MVC applications by leveraging dependency injection and single-responsibility patterns.

There are a lot of tutorials and resources out there to help you get started with Angular, but it is usually better to start with the official documentation.

Today, we go through the steps required to protect your AngularJs application, including setup, build process, and compatibility with Jscrambler.

Integrating Jscrambler with an AngularJs app

The sample application we are using is the Real World example app. Now, clone the app and install dependencies:

git clone https://github.com/JscramblerBlog/angularjs-realworld-example-app.git
cd angularjs-realworld-example-app
npm install


Jscrambler Configuration

If you haven't created a Jscrambler account yet, be sure to do so before moving forward.

The first step to protect our application is setting up our Jscrambler configuration. If you haven't used Jscrambler before, please consider reading the Code Integrity Getting Started guide, which will walk you through the steps to protect your app.

Reading the guide will teach you the core concepts of how to interact with Jscrambler and will make some parts of this article easier to reason about.

To protect our application, we will be using the following configuration, which is based on Jscrambler's Obfuscation default template. Create a .jscramblerrc file at the root of your project and paste your configuration.

{
  "keys": {
    "accessKey": "<YOUR_ACCESS_KEY>",
    "secretKey": "<YOUR_API_KEY>"
  },
  "applicationId": "<YOUR_APP_ID>",
  "params": [
    {
      "name": "whitespaceRemoval"
    },
    {
      "name": "identifiersRenaming",
      "options": {
        "mode": "SAFEST"
      }
    },
    {
      "name": "dotToBracketNotation"
    },
    {
      "name": "deadCodeInjection"
    },
    {
      "name": "stringConcealing"
    },
    {
      "name": "functionReordering"
    },
    {
      "name": "functionOutlining",
      "options": {
        "freq": 1,
        "features": [
          "opaqueFunctions"
        ]
      }
    },
    {
      "name": "propertyKeysObfuscation"
    },
    {
      "name": "regexObfuscation"
    },
    {
      "name": "booleanToAnything"
    }
  ],
  "areSubscribersOrdered": false,
  "applicationTypes": {
    "webBrowserApp": true,
    "desktopApp": false,
    "serverApp": false,
    "hybridMobileApp": false,
    "javascriptNativeApp": false,
    "html5GameApp": false
  },
  "languageSpecifications": {
    "es5": true,
    "es6": false,
    "es7": false
  },
  "useRecommendedOrder": true,
  "jscramblerVersion": "<JSCRAMLBER_VERSION>",
  "sourceMaps": false
}

Don't forget to fill out which Jscrambler version you wish to use, as well as your application's ID and API keys.

Protecting Our App

You should be able to verify that our app has a gulpfile.js which is responsible for our app's build process. Jscrambler offers a gulp plugin that we can leverage in order to integrate Jscrambler into our app's build process.

You can install the plugin by running the following command:

npm install --save-dev gulp-jscrambler


Upon inspection, we can notice that our package.json file doesn't include any scripts. Npm scripts are an easy way of interacting with our application, so let's create two scripts, a dev and a build script, which will allow us to run our app in development mode and build our production assets, respectively.

"scripts": {
  "dev": "gulp",
  "build": "gulp build"
}


The dev script will run our default gulp task, which triggers a build of the app and watches for changes. The build script will build our assets for production and place them in the dist directory.

To protect our application, we're going to have to make some changes to our gulpfile.

We can observe that our build task pipes the contents of the build directory, uglifies them, and places them in the dist directory.

Since Jscrambler already offers minification features in the form of the identifiersRenaming and whitespaceRemoval transformations, we don't need to uglify our main.js file.

So we can replace the call to the uglify gulp plugin for the Jscrambler plugin and remove the UglifyJS plugin from our project's dependencies.

//...
// var uglify = require('gulp-uglify'); <- Remove
var jscrambler = require('gulp-jscrambler');
//...
gulp.task("build", ["html", "browserify"], function() {
  var html = gulp.src("build/index.html").pipe(gulp.dest("./dist/"));
  var js = gulp
    .src("build/main.js")
    //.pipe(uglify()) <- Remove
    .pipe(jscrambler())
    .pipe(gulp.dest("./dist/"));
  return merge(html, js);
});


We don't need to specify any parameters on the plugin since it will use the .jscramblerrc file. The .jscramblerrc file shouldn't be committed to your repository since it contains your Jscrambler API keys and application ID.

You can, however, leave the keys and the application Id on the .jscramblerrc file and move the Jscrambler parameterization to the gulpfile, so that every one which clones the repo has access to the same protection configuration:

gulp.task("build", ["html", "browserify"], function() {
  var html = gulp.src("build/index.html").pipe(gulp.dest("./dist/"));
  var js = gulp
    .src("build/main.js")
    .pipe(
      jscrambler({
        params: [
          {
            name: "dotToBracketNotation"
          },
          {
            name: "stringConcealing"
          },
          {
            name: "functionReordering"
          },
          {
            name: "identifiersRenaming",
            options: {
              mode: "SAFEST"
            }
          },
          {
            name: "deadCodeInjection"
          },
          {
            name: "functionOutlining",
            options: {
              freq: 1,
              features: [
                "opaqueFunctions"
              ]
            }
          },
          {
            name: "propertyKeysObfuscation"
          },
          {
            name: "regexObfuscation"
          },
          {
            name: "booleanToAnything"
          }
        ],
        areSubscribersOrdered: false,
        applicationTypes: {
          webBrowserApp: true,
          desktopApp: false,
          serverApp: false,
          hybridMobileApp: false,
          javascriptNativeApp: false,
          html5GameApp: false
        },
        languageSpecifications: {
          es5: true,
          es6: false,
          es7: false
        },
        useRecommendedOrder: true,
        jscramblerVersion: "5.1",
        sourceMaps: false
      })
    )
    .pipe(gulp.dest("./dist/"));
  return merge(html, js);
});


Since Jscrambler reproduces the file structure of the input directories, it will place the build/main.js file on dist/build/main.js. Our index.html file requires the dist/main.js so our application will be broken.

Here, we can take one of two approaches, either we also protect our index.html file which currently doesn't have any JavaScript, or we move the contents to their correct location after the protection. This could be done either in the gulpfile or in the npm script. For simplicity's sake, let's do it in the build script.

"build": "gulp build && mv dist/build/main.js dist/ && rm -r dist/build"

Our protected JavaScript will be inside dist/main.js.

Now, in order to run our application, we need an http-server. Let's install a package that lets us create one and add a script to run our application. Our npm scripts should look like this:

"scripts": {
  "dev": "gulp",
  "build": "gulp build && mv dist/build/main.js dist/ && rm -r dist/build",
  "start": "cd dist/ && http-server -o"
}


Then we just need to run the following commands and we should be able to see our protected app working:

npm i --save-dev http-server
npm start


Known Compatibility Issues

Identifiers Renaming

Your AngularJs app may break when using the identifiersRenaming transformation since the transformation will rename some of the identifiers that begin with the $ character such as $scope or $stateProvider. To work around this issue we have two options.

Either we exclude all of those names from being renamed, by using the excludeList option:

'params': [  
  {
    "name": "identifiersRenaming",
    "options": {
      "mode": "SAFEST",
      "excludeList": [
        "$*"
      ]
    }
  }
]


Or we modify our controllers to inject the dependencies in an alternative fashion, which will not break the framework's dependency injection system:

// Before
angular.module('starter.controllers', [])
  .controller('ChatsCtrl', function($scope, Chats) {
    $scope.chats = Chats.all();
    $scope.remove = function(chat) {
      Chats.remove(chat);
    };
  });
// After
angular.module('starter.controllers', []);
// Create a function for the controller
function ChatsCtrl($scope, Chats){  
  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
}
// Create a $inject property with the dependencies
ChatsCtrl.$inject = ['$scope', 'Chats'];
// Assign the controller to the module
angular.module('starter.controllers').controller('ChatsCtrl', ChatsCtrl);  


By doing this refactor, we don't need to use an excludeList.

Function Reordering and Self Defending

Jscrambler's selfDefending transformation converts function declarations into variable declarations. The value of the newly created variable is a function expression:

// Function declaration
function foo() {  
    return 'foo!';
}
// Variable with a function expression
var bar = function() {  
    return 'bar!';
}


The usage of both selfDefending and functionReordering may lead to cases where we are assigning our controllers dependencies before we have actually declared it:

// Protected code resulting of applying selfDefending with functionReordering
// Will break the app
// Even though the ChatsCtrl variable is declared (hoisted)
// it's not assigned to an object
// Results in the error: Unable to set property $inject of undefined
ChatsCtrl.$inject = ['$scope', 'Chats']; // Error
// Var declaration resulting of the `selfDefending` transformation
// New variable is created with the previously declared function
var ChatsCtrl = function () {  
  /* protected code */
}
angular.module('starter.controllers').controller('ChatsCtrl', ChatsCtrl); 


To work around this issue, all we need to do is convert our function declarations into variable declarations with function expressions.

// Instead of `function ChatsCtrl($scope, Chats){...}`
var ChatsCtrl = function ($scope, Chats){  
  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
}
ChatsCtrl.$inject = ['$scope', 'Chats'];
angular.module('starter.controllers').controller('ChatsCtrl', ChatsCtrl);


Final Remarks

Now you know how to protect your AngularJs application by integrating Jscrambler into its build process which will make your application safe and secure.

You've also learned how to work around issues that you may encounter when using Jscrambler with Angular.

Jscrambler's Blog offers a lot of Angular content which can help you build Angular applications.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Application Security

How To Protect Next.js Apps with Jscrambler

In this article, we’ll look at how you can integrate Jscrambler into your Next.js app development workflow.

September 1, 2021 | By Jscrambler | 6 min read

Section Divider

Subscribe to Our Newsletter