Skip to content

Instantly share code, notes, and snippets.

@laughinghan
Last active July 12, 2021 05:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laughinghan/b36b26eeb25d1253507c909aca6ee8e5 to your computer and use it in GitHub Desktop.
Save laughinghan/b36b26eeb25d1253507c909aca6ee8e5 to your computer and use it in GitHub Desktop.

PackSec: Package-level capability-based security

What: A capability-secure version of Node.js, and an ecosystem of capability-secure repackaged versions of existing NPM packages, community-contributed and hosted on GitHub like Homebrew & DefinitelyTyped.

Why: Immediately, this provides strong defense against malicious dependencies (supply chain attacks) like event-stream, electron-native-notify, typosquatting like crossenv, and thousands more; as well as vulnerable dependencies like JS-YAML, express-fileupload, and more.

More broadly, if you break down computer security into authentication and authorization, a huge swath of authorization problems—privilege escalation, directory traversal, injection attacks, etc—can be addressed by thorough application of capability-based security principles. (Authorization problems not addressed by capsec are primarily low-level attacks like race conditions, memory safety, or side-channel attacks.) PackSec alone can't address all those problems, but it's a foot-in-the-door to immediately begin applying capsec principles towards solving real-world problems.

How: For new projects, use npsx instead of node to run your scripts, and use npsm instead of npm to install dependencies. If a repackaged capabilities-secure version of the package you want isn't available, contribute one (TODO: link to tutorial)! :) Of course, an escape hatch will also be available to use capabilities-insecure dependencies without having to repackage them.

For existing projects, we will have tools to automatically convert individual files to use capabilities-secure dependencies where possible, and "capabilities coverage" tools to report on your progress converting an overall project to be capabilities-secure (and perhaps complain when new capabilities-insecure files are introduced).

Who: You! Me! Together, we can end software supply chain attacks and authorization problems of many kinds.

When: The state of computer security is unsustainable, help fix it today!

What's Next: More languages, platforms, and package ecosystems: PyPI, RubyGems, Cargo/Rust, WASI, Flatpak, apt-get, Homebrew?

Sketch

  • Capabilities-secure packages should have similar security properties as WASM modules: most importantly no side-effects, also no intrinsic non-determinism nor capability to measure time (either/both can be provided by the library consumer, ofc). This is enforced by running them in a sandbox where we mock out:
    • globals allowing side-effects like process
    • built-in modules that can have side-effects, like fs, http, os, child_process etc
    • C++ addon modules
  • How to repackage existing NPM packages?
    • Hopefully many are like sharp, which can already take in either a filename string (bad, capabilities-insecure) or pipe in a file (good, capabilities-secure). I think for those we can just stub out fs or whatever, which should just disable features like opening a file by name without breaking anything else
    • Otherwise, repackaging means a new API is presented to the library consumer, and we provide an API to the shim code to mock out side-effectful globals/built-in modules
    • C++ addons have to be imported at the top level (ie by the app itself) and passed to libraries that want to use them, possibly transitively
  • Escape hatch to insecurely import modules
  • Incremental transition
  • Processes for submitting & reviewing repackaged libraries
  • Homebrew & DefinitelyTyped show that a community-contributed package management ecosystem, where code is repackaged by third-parties unaffiliated with the authors and submitted to the package repository, can be workable
  • Choosing NPM to start because:
    • JS is designed to run in a sandbox, only require()/import needs to be overridden to contain it; Ruby has a built-in syntax for shelling out, Python has .__closure__, etc
    • I quickly found vm2 & isolate-vm sandboxing options
    • NPM has had prominent in-the-wild supply-chain-attacks like electron-native-notify and event-stream
  • The 2 main options for secure containment are vm2 and isolate-vm: https://github.com/laverdet/isolated-vm#alternatives
    • If vm2 allows dynamically intercepting require() that would be easiest, but it's not clear if that's possible
    • isolate-vm has a more secure design (starting with a secure underlying primitive (V8 Isolate) and adding functionality, rather than starting with an insecure underlying primitive (Node vm) and plugging holes), and it seems more official in terms of who uses it and how its run like having actual security advisories and stuff, but its advertised advantages over vm2 like threading and memory limits and stuff we don't really need, and it seems like we'd have to re-implement require() ourselves
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment