DEV Community

Cover image for Lazy-Loading as a Security Measure
bob.ts
bob.ts

Posted on

Lazy-Loading as a Security Measure

Introduction

Recently, an external red team (security) discovered a unique vulnerability in an application I maintain.

All other work on this project has stopped until we get it not just patched, but appropriately fixed.

The initial patch was simply a means of plugging the hole. It is intended to be temporary and something we can get in place quickly. While thinking about the patch, I had an odd thought.

My thought, "Can lazy loading be used to protect our sensitive code?"

Lazy loading has always been a means for me to make my code run faster and more efficiently. In most cases, I've skipped using it unless there was a specific need for a faster implementation.

With the thought of protecting sensitive code in mind, I began conducting some research...

Lazy Loading

Implementing Lazing Loading in Angular - GeeksForGeeks.

Server-Side Rendering

Applications that rely heavily on server-side rendering typically do not experience this issue.

The login page is rendered and shown. Once logged in, the server-side will present the sensitive pages as needed. If correctly designed, they will only load the content and endpoints required.

Angular

Angular lazy loading is a technique used to improve the performance of Angular applications by loading modules or components only when they are needed, rather than loading everything at the initial application startup.

This includes:

  • Module splitting
  • Route configuration
  • On-demand loading

The benefits are:

  • Faster initial load times
  • Improved performance
  • Reduced bandwidth usage

Implementing Angular Lazy Loading ...

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
  { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
Enter fullscreen mode Exit fullscreen mode

Angular (in newer versions) also allows for the lazy loading of individual components or parts of templates using the defer block. This offers even finer-grained control over when content is loaded, with options for triggers and placeholders.

React

React lazy loading, also known as code-splitting, is a performance optimization technique that allows you to defer the loading of specific components until they are actually needed. This significantly reduces the initial bundle size, improving the application's loading time and overall performance.

This includes ...

  • Reduced initial loading time
  • Improved performance
  • Optimized resource usage

The benefits are ...

  • Large or complex components
  • Route-based code splitting
  • Components with heavy dependencies

How to implement React lazy loading ...

React provides built-in features, React.lazy() and Suspense, for implementing lazy loading.

import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

Vue and more

Vue does something similar to React. I won't rehash that for simplicity's sake.

I was asked about lazy-loading JavaScript code that should be protected natively. This is an interesting side track that I haven't explored yet, but I might delve into it in a future article.

Safe Lazy Loading

My research was shocking ... to me.

Only one of the frameworks or libraries even hinted at using lazy loading for security reasons, "code splitting." Security was not mentioned.

In a more recent conversation, I realized that applications rendered server-side would be more protected by the implementation I'm discussing here.

The sensitive code isn't returned until proper authentication occurs. Having said this, we still need to be careful about how much of the application is loaded; are we exposing more code than is necessary, even to logged-in users?

Implementation

I've been exploring Microsoft's MSAL tooling and came across this code. There's nothing complicated here ...

function App() {
  const request = {
    loginHint: "name@example.com",
    scopes: ["User.Read"]
  };

  const { login, result, error } = useMsalAuthentication(InteractionType.Silent, request);

  useEffect(() => {
    if (error) {
      login(InteractionType.Popup, request);
    }
  }, [error]);

  const { accounts } = useMsal();

  return (
    <>
      <p>Anyone can see this paragraph.</p>
      <AuthenticatedTemplate>
        <p>Signed in as: {accounts[0]?.username}</p>
      </AuthenticatedTemplate>
      <UnauthenticatedTemplate>
        <p>No users are signed in!</p>
      </UnauthenticatedTemplate>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

... but ...

The code <p>Signed in as: {accounts[0]?.username}</p>, while not an issue as written, could just as easily be <complex-application /> that has tons of JavaScript available that could expose threat vectors.

In my opinion, this is the ideal scenario to utilize the <Suspense> element and React.lazy() functionality to protect the code.

Thoughts

There is definitely more that I need to consider. There is definitely more research that I need to do. There's a depth of authentication and authorization that needs to be taken into account to protect the client's code and data appropriately.

Having recently gone to a talk on "Data Validation and Sanitation by Claire Bourdon, I'm thinking of this as a "lifecycle" of security - maybe it's a "generational" handling. Whatever it's called, I'm realizing that there's more depth to protecting my code than I saw before.

Traditional SPA versus Lazy Loaded SPA

Examining the traditional SPA methodology, where everything is loaded at once, reveals "all code," including code that should be secure. Using LAZY LOADING allows us to protect that code in a much more meaningful way. This isn't a complete security solution, but it's a significant step that should be taken into account when developing frontend code.

Whenever code is loaded into the browser, it can be read or scanned. Sensitive information can be exposed unintentionally. We should limit exposure by using lazy loading and not solely relying on server-side security operations.

My thought now is, "Should lazy loading be used to protect our sensitive code, beyond what is done server-side?"

It absolutely should be used for improved code security.

Please let me know your thoughts.

Top comments (19)

Collapse
 
xwero profile image
david duymelinck

Examining the traditional SPA methodology, where everything is loaded at once

Who is still bundling all the frontend code in one file? With all main browsers supporting dynamic imports it is a antiquated best practice.

The sensitive code isn't returned until proper authentication occurs

Why have sensitive code in the frontend in the first place? When the code is sensitive it should be in the backend to have no exposure.

Sensitive information can be exposed unintentionally.

Lazy loading is not stopping that. API endpoints should have security measures in place to prevent that.

All javascript code send to the browser is public, so thinking it is possible to use it as a security measure sets you up for failure.

Collapse
 
rfornal profile image
bob.ts

David, bundling code has been the default for a long time. I'm not saying it's current practice (i.e. Angular implements lazy loading by default), but there's a LOT of legacy code out there that can be improved.

There has to be "some" sensitive code in the frontend to reach the backend - API URLs, potentially keys, and possibly more with poor designs. Not all security should be in the backend.

Lazy loading absolutely can protect information if done correctly. I think it's naive to think otherwise.

The ultimate idea I am proposing here is to limit what protected information is visible and when. Ignoring what has to be done in the frontend because the backend is "secure" would be my idea of setting yourself up for failure. It all should be protected end-to-end.

Thank you for the thoughts ...

Collapse
 
xwero profile image
david duymelinck • Edited

API URLs, potentially keys

As I mentioned before API urls should have security measurements, otherwise lazy loading code is not preventing misuse.
If there is a man in the browser attack all the API urls can be exposed, and when they see the endpoints don't have security measurements there can be a data breach.
But even without an attack the filename of the lazy loaded modules can be found in the initial code and can be downloaded to find urls.

That is my worst nightmare, a key that can be found in javascript code. This is one of the reasons there is a backend/frontend divide.
You don't want people to make request with your key, this can lead to not being able to use the API because bots are hammering it with your key.

Lazy loading absolutely can protect information if done correctly

Lazy loading on its own doesn't protect information. It can defer asking for information, but that is security by hiding. And that is one of the weakest security measurements.

It is the same boat as frontend validation. If it is not backed up by backend validation, it is only half of a security measure.

The ultimate idea I am proposing here is to limit what protected information is visible and when. Ignoring what has to be done in the frontend because the backend is "secure" would be my idea of setting yourself up for failure

I agree that the frontend can be responsible for a bit of the security tasks, but it should be the backend that evaluates when the situation is secure enough to push the information to the browser.
A website is loaded in many unsecured applications, so it is much easier to undo frontend security measures.

I think looking at in-browser behaviour as security without mentioning the backend side of the coin, is a dangerous way to promote security.

Thread Thread
 
rfornal profile image
bob.ts

I'm never promoting frontend only. Just showcasing a pattern I found that improves things on the frontend. As I said previously, it has to be a holistic approach to security and ignoring the frontend because the backend is solid is just as bad as a frontend only approach.

I'm agreeing, not disagreeing.

Collapse
 
lionelrowe profile image
lionel-rowe

I can sort of see this being valuable as part of defense in depth, but if that's the only thing preventing secrets being exposed then it's just security through obscurity, which is really no security at all. Secrets should never be included directly in code bundles, if they're present there then something's already gone badly wrong.

Collapse
 
rfornal profile image
bob.ts

I would always advocate for defense-in-depth. In fact, I use the word secrets lightly. I’d rather not have the API endpoints in code, but it’s a necessity. I wrote this article as another layer I realized that might not be used as effectively as it could.

Collapse
 
volixta profile image
Hovo

Great perspective!
It’s true that in large SPAs (especially with sensitive dashboards or admin modules), lazy loading can indirectly reduce the attack surface.

I’ve seen some WordPress-based apps using dynamic script injection for similar reasons — might be worth exploring cross-framework patterns like that.

Collapse
 
rfornal profile image
bob.ts

Yes. I think I’ve got a lot of research to do. Thank you for the thoughts.

Collapse
 
kushyarr7 profile image
Kushyar Rashidzadeh

Thanks for sharing these insightful article.

Collapse
 
rfornal profile image
bob.ts

You’re welcome!

Collapse
 
coral_zang profile image
Coral • Edited

This cover is amazing U w U

Collapse
 
rfornal profile image
bob.ts

Thank you. I can't take too much credit - dozens of attempts at various options (in Canva).

Collapse
 
richmirks profile image
Richard Mirks

Does lazy loading hide code, or just delay it until auth?

Collapse
 
rfornal profile image
bob.ts

It doesn’t “hide” code. The code is not loaded until proper auth takes place. So, yes, the code is visible and potentially accessible at some point. This is one step in a security fence.

Collapse
 
arvin_jay_romero profile image
Arvin Jay Romero

This is a very insightful exploration of using lazy loading beyond just performance optimization, extending it as a layer of security for frontend code.

Collapse
 
rfornal profile image
bob.ts

Thank you. I like how you phrased this!

Collapse
 
fardeen_khan_757b4992b2bc profile image
Fardeen Khan

Very insightful information

Collapse
 
rfornal profile image
bob.ts

Thank you. There's just enough content here for an article; not enough (yet) for a conference talk ...