Tuesday, 16 January, 2018 UTC


Summary

Introduction
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 the 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's usually better to start with the official documentation.
In this blog post, we're going to go through all 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 will be using is the Real World's example app. Here's the demo of what we will be protecting.
Before anything else, let's clone the app and install dependencies:
git clone [email protected]:gothinkster/angularjs-realworld-example-app.git  
cd angularjs-realworld-example-app  
npm install  

Jscrambler Configuration

The first step in order to start protecting our application is setting up our Jscrambler configuration. If you haven't used Jscrambler before please consider reading the getting started guide which will walk you through the steps on how to protect your application. 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 which we can leverage in order to integrate Jscrambler in 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 everyone 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 is requiring 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 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 which 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

You AngularJs app may break when using the identifiersRenaming transformation since the transformation will rename some of the identifiers which begin with the $ character such as $scope or $stateProvider. To workaround 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 to 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
We've reached the end of this guide. 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 learnt how to work around issues which you may encounter when using Jscrambler with Angular.
Jscrambler's Blog offers a lot of Angular content which can help you build Angular applications. Follow this link to check out more of Angular related articles.