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 { }
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>
);
}
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>
</>
);
}
... 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.
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 (1)
Does lazy loading hide code, or just delay it until auth?