I’ve been part of a recent “storm” in the Node.js world after providing an example of Promise.prototype.then
pollution that would allow any malicious code to intercept anything out there, especially crypto
and its subtle
related operations, which is (imho) a security no-go
Background
Native prototype pollution has been essential to move the Web forward and it’s a more than welcome feature, but in recent years most Web related APIs are Promise
based and I feel like:
- the
Promise
original API is frozen in time, as in … nobody expectsthen
orcatch
to change anytime soon in the next thousand years - the
crypto
and itssubtle
entries are mostly based onPromise
API: one leak/evil hook at that prototype, we’re all doomed - everything that travels asynchronously, including
fetch
operations, can be intercepted, instrumented, and poisoned in a way or another or, worst part, leak in the wild
It’s true that Promise
is likely my only target for this proposal, but at the same time it’s the most compelling use case for such proposal.
Proposal
Find out what native API is sealed in time and widely available (close to 100% if not just 100% across vendors/engines) and decide that, if every environment would never ever need a specific patch for such API, as it’s legacy or not inherently leaking data, the descriptor signature out of that API is writable: false
and configurable: false
, covering also all accessors in the making (yes, in my report I’ve mentioned even TypedArray buffer access could be poisoned by reaching Uint8Array.__proto__.prototype
and its buffer
descriptor).
Goal
Allow developers to trust that if they await anything
that anything
resolved value can’t ever be intercepted by Promise.prototype.then
pollution, because that pollution in the first place would throw an error out of the box.
This would increase security concerns about most asynchronous APIs already and make the Web, or in general JS as a language, a place to trust more, as opposite of being victim of any npm module out there that got hacked before the next production deployment.
Risk
There might be modules with honest intents able to provide metrics or whatnot that need to be able to wrap Promise#then
or any other introspection Object
or Reflect
based method around, although that should not be the default allowed to any script, rather an ad-hoc environment (with its flags, like via playwright or puppeteer) so that the “production Web code” can run in a safer way that doesn’t need ugly/paranoia workaround to maintain.
Compromise
I think having crypto
and its subtle
impossible to hack around would be already a huge step forward, if asking to have some historical prototype sealed by any mean feels like too much, but when it comes to Promise
I am not asking to seal the whole thing, imagine polyfills impossible to provide a finally
hook, as example, although I think Promise.prototype.then
and why not, catch
, should not be defined just like everything else, as it’s clear the entirety of the Web is moving toward more asynchronous APIs, so that the risk somebody can poison the env at any time and intercept values out of it, or their classes such as CryptoKey
, would be mitigated out of the box.
Personal Opinion
I am not sure I know any other scripting language (Ruby, Python, PHP, others) that can poison or pollute, beside being even Server Side targeting PLs, primitives offered by the language itself (stdClass
in PHP, dict
, list
or tuple
in Python and so on) so that keeping the dynamic nature of JS, as it is, should be a goal, but at the same time “something gotta be safe out there” and that something is Promise.prototype
land together with (at least?) crypto
and its subtle
offer.
Thanks in advance for anyone willing to at least open a discussion around this topic.