Modularity And Security In Composite JavaScript Apps

In one of my current apps for a client, I have an activity based security system that determines what the user is allowed to do. The trick to this system is that all of the authorization checks happen on the server, but the functionality that is being secured runs on the client, in JavaScript.

This is a bit of a problem. If I send all of the JavaScript that is uses to run any part of the system to the browser, then it’s possible that a clever user could enable it and do things they aren’t supposed to do. Of course, when they send a request back to the server, the server will verify they can do what they requested and block it… but the user should not be able to even try to do things they aren’t authorized to do, in the first place.

The solution that I’ve come up with, at a very high “functional area of the application” level, is simply not to send JavaScript to the browser if the user is not allowed to use it. This keeps me from having to do extra security checks in the browser, keeps the download for the user smaller and generally makes the app appear snappier because there is less code to run. More importantly, though, it keeps the user from being able to try things they can’t do.

Modules To The Rescue

I’m facilitating this through the use of modules in my JavaScript – both “module” as in the JavaScript module pattern, and “module” as in a packaged functional area of an application.

It’s worth noting, though, that I’m not using AMD (asynchronous module definitions), RequireJS, or any other “module” framework for JavaScript. The debate about the “right way” to do modules can rage on all it wants. I’m content with simple immediate functions and logical groupings of files while the more intelligent and invested people in the JS community figure that all out.

Each of the functional areas of my application is built in it’s own set of files. Each area has multiple files that it is composed within, but the files are grouped together for easy identification of what makes up a module. I also have an HTML template for each module which provides the needed <script> tags to include the code for that module.

Only Render What Is Needed

When I need a functional area of my site to be sent down to the user, I tell my server side template language to include the correct HTML file. For example, I’m doing this in an ASP.NET MVC application:

@{var user = (CustomPrincipal)User;}

@if (user.Can("locations/manage"))
{
    @Html.LocationManagementScripts()
}

@if (user.Can("locations/search"))
{
    @Html.LocationSearchScripts()
}

In this code, I’m checking to see if the current user is allowed to manage locations. If they are, the extension method “LocationManagementScripts” is called. This in turn renders the “LocationManagementScript.html” file at this point in my HTML layout. That file contains all of the <script> tags for the location management JavaScript app. In the same way, I’m checking to see if the user can search through locations, and running the same basic process if they can.

Self-Initializing Modules

When a functional module is included after passing one of these checks, it needs a way to get itself spun up and started so that it can do it’s magic. It may need to render something on to the screen. It may need to register itself with the application’s event aggregator, or any of a number of other things. This is where my Backbone.Marionette add-on comes in to play for my Backbone apps.

Marionette has an explicit concept of an “initializer” tied to it’s Application objects. When you create an instance of an Application object, you can call “app.addInitializer” and pass a callback function. The callback function represents everything that your module needs to do, to get itself up and running. All of these initializer functions – no matter how many you add – get fired when you call “app.start()”.

myApp = new Backbone.Marionette.Application();

myApp.addInitializer(function(options){
  var myView = new MyView({
    model: options.someModel
  });
  MyApp.mainRegion.show(myView);
});

myApp.addInitializer(function(options){
  new MyRouter();
  new SomeOtherView().render();
});

myApp.start();

Each functional area of my application has it’s own initializer function. When a functional area has been included in the rendered <script> tags, the initializer gets added and when the “start” method is called, the modules for that functional area are fired up and they do there thing.

A Composite App, And Sub-Apps

One of the tricks to making all of this work, is that I need to have a primary “app” object that all of my modules know about. In the above example, the “myApp” object is this. Each of the modules for each of the functional areas has direct knowledge of this object and can call public APIs on it – including the “addInitializer” method.

A better example of what a module definition and initializer might look like, would be this:

// --------------------
// app.js

myApp = new Backbone.Marionette.Application();

myApp.addRegions({
  mainRegion: "#main"
});


// --------------------
// locationSearch.js

(function(myApp, Backbone){
  var SearchView = Backbone.View.extend({
    // ...
  });

  myApp.addRegions({
    searchRegion: "#search"
  });

  myApp.addInitializer(function(){
    var view = new SearchView();
    view.render();
    myApp.searchRegion.show(view);
  });
})(myApp, Backbone);


// --------------------
// index.html

<script language="javascript">
  myApp.start();
</script>

In this example, I’m using the simple JavaScript module pattern to encapsulate my search functionality. I’m also providing an initializer for the module that instantiates a search view and shows it to the user using a region manager.

Each of these functional areas is basically a sub-application. Many sub-applications are used to compose a larger application and overall experience for the user. The composition of a larger application through various modules that are included / excluded based on some criteria are what really make this a composite application.

I also included the final call to “myApp.start()”, showing that I do this from my main HTML page and not from my JavaScript files. This provides a single point of entry for all of the registered modules, no matter which modules are registered. The “myApp” object really doesn’t care which modules are registered, honestly. It doesn’t need to care. It only needs to execute the initializers that happen to be present. If none are present because the user didn’t have permission to do anything, then nothing happens when this method is called and the user won’t see anything.

Security: Don’t Let Them See It If They Can’t Do It

If the security check to see if the user is allowed to use the location search feature fails, the rendered HTML won’t include the <script> tags for the “locationSearch.js” file. If this file is not sent down to the browser, then it will never register itself. If a module has not registered itself for initialization, it’s views won’t show up on the screen and the user won’t be able to try and use the feature. Further, the user won’t be able to “view source” on the page and find any stray JavaScript that they shouldn’t be able to use.

It’s Not Always That Easy

Of course there are other security concerns that are not this simple. When a functional area is closed off by authorization, it’s easy to keep things clean like this. We can compose the application at run time simply by including the right files and letting the code in those files register themselves for initialization. But when we have a functional area of the system that has finer grained authorization and permissions associated with it, things get a little more tricky.

I’m still learning and exploring this space. I have some ideas and am going to be implementing some of them soon. If anyone out there has any experience in handling finer grained security needs in JavaScript apps, I’d love to hear about it. Post links to your favorite resources for this, in the comments.

 


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, Composite Apps, Javascript, Marionette, Security. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Anonymous

    _test

  • Yusuf Motara

    What if a user with lower privileges gets to view-source (and save-source) on a session belonging to a user with higher privileges?  All the “security benefit” (and I don’t think there is much; this is basically security through obscurity) is removed.

    • http://mutedsolutions.com Derick Bailey

      if a user with lower privileges is viewing the source from a user with higher privileges, you’re not going to be able to do anything. that would basically mean, “Joe” – an order entry person walked over to the computer of “Tim” – an administrator, and started using Tim’s computer. You’ve got bigger problems to solve, related to the people that you hire.

      This is not security through obscurity. I’m not obscuring anything. Security through obscurity means that the code is sitting right there in front of the person, but it’s so obscure that they can’t figure out how to get around it.

      This is one level of security by not giving the user the option to even try to do something they can’t do. As I said in my post, I make actual authorization calls on the back-end of my system, to ensure nothing gets through that shouldn’t.

      • Yusuf Motara

        If you trust Joe to not look at Tim’s stuff, then why not simply trust him to click on only his stuff?  Or, more simply, send him all of the JS, but check on the clientside what you should display and only display that.  Sure, you’re trusting him to NOT do the view-source … but then again, you trust him, right?

        Security that only works in the presence of a trusted consumer isn’t security.  Security needs to work against the most determined attacker, who is malicious.  The term “security through obscurity” refers to NOT revealing security controls, in the hope that an attacker won’t guess what they are; it doesn’t usually refer to obscuring code.

        If you make actual authorization calls on the back-end, when why not send the JS across?  It doesn’t reduce security at all, though it might reduce efficiency (more stuff being transferred & parsed).

        • http://mutedsolutions.com Derick Bailey

          You’re right about the obscurity bit. This isn’t obscurity, though. I’m not hiding something in the hope they won’t find it. I’m using actual authorization checks to determine what they can and cannot do. It’s a multi-part security system, where I don’t give them the option to even try something they can’t do, and I also check everything they do against what they can do, to make sure it’s all valid. It’s not obscurity. It’s security.

          “If you make actual authorization calls on the back-end, when why not send the JS across?  It doesn’t reduce security at all, though it might reduce efficiency (more stuff being transferred & parsed).”

          It reduces the user experience by causing confusion and the perception of errors. 

          Why should a user be able to see a bunch of stuff on the screen if they aren’t allowed to use it? If I allow my users to see the search features, select the options that they want, and then they get an error message saying that they can’t use this feature, the user will likely perceive this as a problem with the system. I’ve run into this before. I’m not postulating or assuming, here, but basing this on actual experience. I’ve had users tell me “if I’m not allowed to do X, then why is X on my screen?”

          • Yusuf Motara

            Then the primary benefit is reduced user confusion?  I absolutely agree with you about the importance of that (“check on the clientside what you should display and only display that”).

            Speaking as a pentester (in a past life) and current security researcher, “multi-part” security systems are a treat to explore.  A variation of Hoare’s maxim comes to mind: “There are two ways of developing secure systems: One way is to make it so simple that there are obviously no holes, and the other way is to make it so complicated that there are no obvious holes. The first method is far more difficult.” :).

          • http://mutedsolutions.com Derick Bailey

            right – so, we have the same goals, then, we just have 2 slightly different ways of getting there :)

            i like that quote/variation. simple is definitely more difficult.

    • http://mutedsolutions.com Derick Bailey

      thinking about this a bit more… i think i can see your point about this being obscurity. but i would say checking permissions on the client side is also just a form of obscurity.

      in my version of this, i’m not sending the javascript down to the browser, so there’s no way the user can even see it. i’ve obscured the functionality from their sight. 

      if we did client side checking of the permissions, the user would still have the functionality downloaded to their browser. the checks would prevent it from being displayed, though. again, obscuring it from their sight.

      i don’t think obscurity in this case is bad. but it’s not “complete”, either. it’s only a part of the over all security of a web app. we have to do the authorization checks on the back-end, still, because javascript running in the browser is inherently less secure than the code running on the server.

  • Singh

    According to you, should a single page application have a typical html form submit based login or should it be handled using backbone.?

    • http://mutedsolutions.com Derick Bailey

      I see no advantage to building a login page with backbone. it creates more work and more difficulty in being able to reset the rest of the application to use the authentication and authorization information of the newly logged in user. it’s far more simple and effective, in my experience, to have the login page be a full server page refresh.

      That being said, I often use Backbone on the login page. It helps to organize the jQuery code that I use to show error messages and validate input. But once the authentication has been verified, I almost always do a full redirect to a new page from the server, to start the real application.

      • http://web-scents.blogspot.com/ Marhamah

        “almost always do a full redirect to a new page from the server”

        what do you mean almost? from my experience, i just store the auth token into server session. Then pass it back to client and set xhrHeader with it. And straight instruct backbone to navigate to new page. Is that a bad practise?

  • Priya

    According to you, should a single page application have a typical form submit based login or a backbone login ?

  • rodriguezartav

    Hey great article! I also came up with a similar solution. I am sending a platform.js file with all the necesary stuff at first request. And then after login compiling on the server another js file that only includes the modules for the logged in user.

    I even went as far as only including content depending on screen size! It’s great that you are doing something similar since it validates the concept,

    To do this I do have to use CommonJS, check out how HEM server from SPINEJS do it.

    Thanks for the post.