In reality, it’s probably a bit far-fetched to think there are Node modules doing something this egregious with outbound requests — to me it seems like this would be too easy to detect.

But do you really want to go to all the trouble of creating a form that doesn’t contain any third-party code only to give third-party code the ability to modify it right before sending it to the user? That’s your call.

My suggestion is to serve these ‘secure’ files from a static file server, or don’t bother doing any of this.

2. Sending to a static file server

Yes that heading is both the step we’re up to and the name of a vulnerability.

I’m a big fan of Firebase for static hosting because it’s about as fast as you can get, and deployments are dead easy.

Just install the firebase-tools from npm and… oh no, I’m using an npm package to avoid npm packages.

OK, deep breath David, maybe it’s one of those beautiful zero-dependency packages.

Installing … installing …

Jezus Kanye, 640 packages!

OK I give up on making recommendations, you’re on your own. Just get your HTML files onto a server somehow. At some point we all need to trust code written by strangers.

Fun fact: it’s taken me a few weeks to write this post. I’m in a final draft and I just installed the Firebase tools again to check I got that number right…

I wonder what those seven new packages do? I wonder if the people that manage the Firebase tools wonder what those seven new packages do? I wonder if anyone knows what all the packages their package requires do?

3. Webpack

You may have noticed that I haven’t suggested that you incorporate your ‘secure’ HTML files in your build pipeline (for example, to share CSS), even though that would solve the duplication-of-code problem.

This is because any of the hundreds of packages involved in even the simplest Webpack build can potentially modify the output of the build process. Webpack on its own requires 367 packages. Something benign like a css-loader will add 246 more. The excellent html-webpack-plugin you might use to put the right CSS file name in your index file will add 156 packages on top of that.

Again, I think it’s highly unlikely that any of these will be injecting scripts into your minified output. But still, it seems wrong to go to so much effort to produce a pristine, tiny, hand-written, human-readable hamster-friendly HTML file only to process it with several hundred dobermans right before bedtime.

4. Attacks of ineptitude

The last thing to protect against is the most dangerous of all. Something that has access to modify any code you’ve written and take down any security barriers you have put up: the new kid that starts 6 months from now and doesn’t know what they’re doing.

This is actually one of the trickiest things to protect against. The only solution I can think of is a ‘unit test’ of sorts that ensures there’s no external scripts in any of these ‘secure’ files.

I’m allowing <script> tags with no source (so, inline code), but blocking script tags with a src attribute. I set jsdom to execute scripts so I can catch if someone is creating a new script element with a document.createElement() .

At least this way, the new kid would actually need to modify a unit test to add a script, and with any luck that would wake up a code reviewer enough to question the move.

It’s also a good idea to run checks of this nature on the published secure HTML file. You could then be more comfortable using things like Firebase tools and Webpack, knowing that alarm bells will sound in the extremely unlikely event that one of those 1,200 packages edits your output.

Wrapping up

Before I go, I want to address a sentiment I’ve heard quite a lot over the past few weeks — the suggestion that developers should use fewer npm packages.

I understand the emotional drive behind this: packages can be bad, less packages must be less bad.

But it’s a bad suggestion; if the security of your user’s data relies on you using fewer npm packages, your security isn’t any good.

It’s like leaving your hamster alone with fewer dobermans.