· 6 min read

Node.js 26.9.0 Turned On FFI by Default. That's a Feature and a New Risk for Anyone Installing Random Packages

Node.js 26.9.0 shipped on September 16 and quietly flipped a switch that used to require an experimental flag: Foreign Function Interface support, or FFI, is now enabled by default. In plain terms, JavaScript can call directly into a native shared library, a .so, .dylib, or .dll file, without writing a C++ addon or wrestling with node-gyp and a compiled build step. You describe the memory layout, Node handles the marshalling. I've wanted this for years on a couple of small projects that needed native speed for one function and weren't worth a full native addon. I also think it's the kind of change a solo developer should actually read before shrugging and running npm install on the next update.

What changed, specifically

FFI existed in Node before this release, but it lived behind an --experimental-ffi flag, and if you had the Permission Model turned on, you additionally needed --allow-ffi to use it. As of 26.9.0, it's on by default without the experimental flag, though the Permission Model gate still applies when that feature is active, so a locked-down process still has to explicitly opt in. The same release added FFI validation for dynamic library getter receivers, a smaller hardening fix bundled into the same version, plus other unrelated additions like a node:bench benchmarking module and first-class Web Workers in thread contexts.

Why this is genuinely useful

If you've ever needed one native-speed function, an image transform, a cryptographic primitive, a fast parser, and didn't want to maintain a compiled native addon across Node versions and platforms, this removes a real barrier. No build toolchain, no platform-specific binary distribution problem, no node-gyp failures on a contributor's machine because they're missing a system compiler. For a solo developer shipping a CLI tool or a small service, that's hours saved per project, not a one-time convenience.

Why it's also a new attack surface

Here's the part that doesn't show up in the celebratory release notes coverage. Native code execution has always been possible in Node through compiled addons, but it required a deliberate build step that left a trail: a binding.gyp file, a compile phase, something a maintainer or a security scanner could notice. FFI lets a package reach native code paths directly from plain JavaScript, no compile step, no obvious artifact to flag in a quick dependency review. After a year of supply chain attacks against popular npm packages, most of which worked through compromised maintainer accounts pushing malicious JavaScript, a lower-friction path to native code execution inside a dependency is worth taking seriously, even with the Permission Model gate as a mitigating factor for anyone who's actually turned it on.

Most solo developers, myself included until I checked this week, are not running Node with the Permission Model enabled on hobby projects or even production services. Without it, FFI-by-default just works, for legitimate use cases and for anything a compromised package wants to do with it.

What actually changes in practice

I don't think this means treating every dependency update as hostile. It means two concrete habits are worth adopting now rather than after something goes wrong. First, actually look at what a new or updated package does before installing it on anything that touches money, credentials, or user data, not just check the download count and move on. Second, consider turning on the Permission Model for anything production-facing, since it now gates a capability that's meaningfully more dangerous than it was a release ago. Neither of these is a big lift. Both are easy to skip when you're moving fast, which is exactly when they matter most.

What I'd actually do

I'm going to start using FFI for the one project where I actually want it, a small CLI tool that currently shells out to a native binary in an ugly way, because the upgrade genuinely solves a real problem I have. At the same time, I'm turning on the Permission Model for my production API service this month specifically because of this change, not because I think an attack is imminent, but because the cost of doing it now is an afternoon and the cost of doing it after a compromised dependency is found is much higher.

Where I could be wrong: the Permission Model gate might turn out to be enough of a mitigation that this risk stays mostly theoretical, the way a lot of "this changes everything" security takes on new language features do. Native code execution through a malicious dependency was already possible before this release for anyone willing to ship a compiled addon; FFI mostly lowers the friction rather than creating an entirely new category of risk. I'd rather spend the afternoon and be wrong about the urgency than skip it and be wrong about the risk.

Author

Sources

Stay in the Loop

Get new posts delivered to your inbox. No spam, unsubscribe anytime.

Newsletter coming soon. Set PUBLIC_CONVERTKIT_FORM_ID in .env to activate.

Related Posts