November 04, 2015

Protect Your Code While Using Webpack

by Juho Vepsäläinen

Protect Your Code While Using Webpack

It’s not an understatement to say that front-end tooling moves forward fast. For a while Grunt was the top task runner. Since then the community has adopted Gulp, a streaming alternative. Even though these tools are nice, you still have to work quite hard to maintain your build system. This is where bundlers, such as Browserify and Webpack come in.

Why to Use a Bundler?

Bundlers solve the fundamental problem of front-end development. They allow you to transform arbitrary assets to something a browser can consume. If you are using NPM, as you should, you can bundle all those packages you are using in your application so you can use them in the browser. But the chances are that you may want to do a lot of other things besides “bundling” so you will want to wire this up with some build automation tool like Grunt or Gulp. You may even skip a task runner and implement your tasks through package.json scripts section (if you are using NPM) to setup your Browserify transformations.

Understanding Webpack

You can achieve similar results with both Browserify and Webpack. Browserify is closer to the Unix philosophy. When using Browserify you are literally gluing separate small utilities together. As a result, Browserify is easy to pick up. But if you have a long list of transformations you want to apply to your code you better use a task runner like Grunt or Gulp to automate this process.

In the other hand, if you use Webpack you might not even need Grunt or Gulp. Webpack assumes that there are certain tasks that you always want to do. Sure you want to move files from a source directory to a build directory. Sure you’ll want to compile your source code using a (usually long) list of transformations (by the way, they are called loaders in Webpack). Sure you want to use libraries in different module formats like CommonJS, RequireJS or the new ES6 modules if you want. You might even want to deal with different file formats.

To give you a better example of what this means in practice, consider the example below:

style.css

body {  
    font - family: sans - serif;
}

index.js

// load style to the resulting bundle
require('./style.css');

// just print hello, normally we would do
// something more involved and start the
// application here
console.log('hello world');  

webpack.config.js

var webpack = require('webpack');

module.exports = {  
    // this is the top level file or set of files
    entry: './index.js',
    // it will bundle everything inside this output path
    output: {
        path: __dirname,
        filename: 'bundle.js'
    },
    module: {
        loaders: [{
            // 1. resolve @import and url() through css-loader
            // 2. load the result to bundle using style-loader
            // note that loaders are interpreted from right to left
            test: /.css$/, // A regexp to test the require path
            loaders: ['style', 'css']
        }]
    },
    plugins: [
        // minify output
        new webpack.optimize.UglifyJsPlugin()
    ]
};

Webpack allows you to load CSS like you load any other code with css-loader and style-loader.

Why would you want to require your CSS instead of the same old way we have been using CSS? Well, because Webpack is smart enough to inline your CSS when they are small enough to be inlined otherwise it will minify the file and give it an unique name for caching purposes. The same can be done with images using the url-loader.

If you ran Webpack against this setup, you would end up with a minified bundle.js that contains CSS inline. It might seem like a lot of effort to achieve a simple result like this. That’s beyond the point. Consider the following:

  • What if you wanted to use the new JavaScript features in your project? You would set up babel-loader.
  • What if you wanted to use compiled CSS? You would set up less-loader or sass-loader.
  • What if you wanted sourcemaps? You would set devtool option.
  • What if you wanted UMD output for your library? You would set output.libraryTarget.
  • What if you wanted a hot loading development server? You would set up webpack-dev-server or build one yourself based on Express as shown by react-transform-boilerplate. Hot loading is one of those features that sets Webpack apart as it updates your browser automatically while retaining application state.
  • What if you wanted multiple targets (development/production/test)? You would use a solution such as webpack-merge and connect your task runner with that.
  • What if you wanted to load some dependencies lazily? You would set up require.ensure. Webpack will generate separate bundles for split points and load them on demand.

In short, you can grow the configuration to various directions based on your needs. There’s definitely a learning curve and understanding all options will take time. That said, the approach is powerful once you get into it.

Connecting Webpack with Jscrambler

If you want to add Jscrambler to your build process and you are using Webpack there are some good news for you! Our Webpack plugin is out and it’s really easy to set up like the majority of Webpack loaders.

We will show you how easy it is to setup using the example above and adding it to our build process. We will also remove the UglifyJsPlugin since Jscrambler can do the same job.

webpack.config.js

var webpack = require('webpack');

module.exports = {  
    // this is the top level file or set of files
    entry: './index.js',
    // it will bundle everything inside this output path
    output: {
        path: __dirname,
        filename: 'bundle.js'
    },
    module: {
        loaders: [{
            // 1. resolve @import and url() through css-loader
            // 2. load the result to bundle using style-loader
            // note that loaders are interpreted from right to left
            test: /.css$/, // A regexp to test the require path
            loaders: ['style', 'css']
        }, {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'jscrambler-loader'
        }]
    }
};

You will also need to create a .jscramblerrc file with your API Credentials. You can find those in your Jscrambler Account Panel.

.jscramblerrc

{
    "keys": {
        "accessKey": "XXXXXX",
        "secretKey": "XXXXXX"
    },
    "params": {
        "self_defending": "%DEFAULT%"
            // there is a big set of transformations that you can use
            // check https://jscrambler.com/en/help/javascript_obfuscation
    }
}

That’s it! You are ready to deploy your protected code!

There are multiple other ways to connect Jscrambler to your build system. This depends on your task runner. I’ve assembled possible approaches below:

  • Grunt – grunt-jscrambler
  • Gulp – gulp-jscrambler
  • package.jsonJScrambler CLI tool. To make this work, create a separate script and then pass unminified version of your Webpack bundle through it. It is preferable to maintain a local version (npm i jscrambler --save-dev) of Jscrambler CLI tool within your project so everything works regardless of the environment.

Jscrambler has a set of tools to protect your code (obfuscation + code traps + Runtime Application Self-Protection or RASP) making it significantly harder to reverse engineer but it also has some code optimization features that you can take advantage of. You can even use it just for minification and compression.

You can learn more about Jscrambler in jscrambler.com.

Conclusion

Even though Webpack isn’t the easiest tool to pick up, I recommend checking it out. I go into great detail about the tool at my book SurviveJS – Webpack and React. Most of the content is freely available and will help you to understand both Webpack and React on a deeper level.