Skip to content

Disable __proto__ #31951

Description

@mcollina

There have been quite a few CVE related to __proto__ in the last while. I think it would be good to have a flag to enable/disable it.

A quick example:

const payload = '{"__proto__": null}'
const a = {}
console.log("Before : " + a) // this works
Object.assign(a, JSON.parse(payload))
console.log("After : " + a) // this crashes

(It's not strictly related to JSON, as it can also apply to multipart data or other serialization format).

Some vulnerabilities:

I don't know if this is fixable / manageable on our side (vs V8), but __proto__ still causes significant vulnerabilities.


Note that there are some modules to help with this, including https://github.com/hapijs/bourne.

Activity

  1. mcollina commented on Feb 25, 2020

    @mcollina
    SponsorMemberAuthor

    cc @nodejs/tsc @nodejs/security @nodejs/v8

  2. hueniverse commented on Feb 25, 2020

    @hueniverse

    From a security / dev perspective, there really is no reason not to disable the getter/setter for __proro__ on Object. If this is done in a major breaking release, modules that use it can easily move to better ways of manipulating the prototype.

    The core issue here is that even the most security-aware developers still get caught not thinking about it when using external inputs. It's impossible to completely seal internal logic from some external input coming in that contains __proto__ directly or after some string manipulation.

    I am seriously considering deleting the getter/setter when hapi loads and being done with it, letting any other non-hapi module that requires it to fail and get people to fix it. But this would be much better dealt with for everyone at the node core level (or v8). It's just a getter/setter on the Object prototype.

  3. devsnek commented on Feb 25, 2020

    @devsnek
    Member

    There are two parts to __proto__:

    • Object.prototype.__proto__
    • Object literals with __proto__

    The second one is extremely popular for creating object literals with a null prototype, so I doubt we could disable it. I don't think the Object.prototype.__proto__ has anywhere near as much usage, but I would guess it is still a lot more than we could deal with.

  4. jasnell commented on Feb 25, 2020

    @jasnell
    Member

    I'm not convinced there's anything reasonable we can do at the Node.js level here by default without causing massive backwards compat issues. I wouldn't be opposed to running an experiment tho so see if that's wrong.

  5. ljharb commented on Feb 25, 2020

    @ljharb
    SponsorMember

    It's part of JS; I don't think node dot JS should ever be in the position of disabling parts of the language, even with a flag.

    (also most of these vulnerabilities are due to doing dangerous things with unsanitized user input, which is a bad practice that causes problems with or without __proto__)

  6. bakkot commented on Feb 25, 2020

    @bakkot
    Contributor

    Drive-by comment:

    I don't know if this is fixable / manageable on our side

    For the vulnerabilities listed, which are about the setter on Object.prototype, I think it would be straightforward for Node to just delete that setter during startup when the flag was set. (Possibly it would be a little more complicated to propagate that behavior to new contexts; I lack the relevant knowledge of the internals.)

    Object.defineProperty(Object.prototype, '__proto__', { set: void 0 });
  7. mscdex commented on Feb 25, 2020

    @mscdex
    Contributor

    IMO there isn't anything node can/should do about this. Developers just need to implement better patterns. The typical scenario I've seen that relates to this kind of issue is people using {} as a data storage mechanism instead of a Map or Object.create(null), both of which have been available for quite some time now and are more suitable for storing arbitrary data.

    Additionally, I'm not entirely sure why someone would be attempting the kind of operation in the OP ('string' + nullProtoObj). Outside of edge cases like that, objects with null prototypes work just fine, even when calling JSON.stringify():

    > JSON.stringify({ foo: Object.create(null) })
    '{"foo":{}}'
    
  8. vdeturckheim commented on Feb 25, 2020

    @vdeturckheim
    Member

    For context, here is a real life RCE in Kibana using prototype pollution: https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/

  9. bmeck commented on Feb 25, 2020

    @bmeck
    Member

    I know we have --frozen-intrinsics and --experimental-policies, perhaps we could make a more cohesive single set of defaults instead of a flag for each. This would be similar to the difficulty TypeScript faces with all its flags, and strict mode for TS opts into the desired but breaking defaults.

  10. kanongil commented on Feb 26, 2020

    @kanongil
    Contributor

    I recently looked into the feasibility of doing a delete Object.prototype.__proto__ for my serverside projects. It can work, but it is definitely still in use. This was evidenced when I tried to benchmark if it caused any changes in performance, and the benchmark tool failed.

    It's part of JS; I don't think node dot JS should ever be in the position of disabling parts of the language, even with a flag.

    It's not really, though. At least, as it is currently specced in the optional for non-browsers section.

    There are two parts to __proto__:

    • Object.prototype.__proto__
    • Object literals with __proto__

    The second one is extremely popular for creating object literals with a null prototype, so I doubt we could disable it. I don't think the Object.prototype.__proto__ has anywhere near as much usage, but I would guess it is still a lot more than we could deal with.

    The second form could still be valid, and is not a security concern, as you can't accidentally get user input into a literal.

  11. guybedford commented on Feb 26, 2020

    @guybedford
    Contributor

    Note that the specific JSON issue quoted in the issue is blocked when enabling the --frozen-intrinsics flag, as it is not possible to write to any builtin __proto__ properties with this.

    Edit: it's just --frozen-intrinsics

  12. Jack-Works commented on Feb 26, 2020

    @Jack-Works

    It's part of JS; I don't think node dot JS should ever be in the position of disabling parts of the language, even with a flag.

    (also most of these vulnerabilities are due to doing dangerous things with unsanitized user input, which is a bad practice that causes problems with or without __proto__)

    It's not part of JS. __proto__ is defined in section B and Node.JS is not a browser so it's safe even __proto__ is not implemented🤔

    When the ECMAScript host is a web browser the following additional properties of the standard built-in objects are defined. (section B)

  13. kanongil commented on Feb 26, 2020

    @kanongil
    Contributor

    The --frozen-intrinsics flag doesn't really work. It does prevent the more serious issue, where the global prototype is modified. It does not prevent changing an existing object prototype through assignment to the obj.__proto__ property itself.

  14. ljharb commented on Feb 26, 2020

    @ljharb
    SponsorMember

    In practice, most of Annex B is not really optional if you want to write or use portable code, but yes, that would be the loophole that would allow node to remove it, using the method described upthread.

  15. 24 remaining items

  16. added a commit that references this issue on Mar 18, 2020
    7a742ec
  17. added 2 commits that reference this issue on Mar 19, 2020
    7a2400d
    36ba54e
  18. added 2 commits that reference this issue on Apr 25, 2020
    dafa9c7
    b598321
  19. GrosSacASac commented on Jan 11, 2021

    @GrosSacASac
    Contributor

    What is the state of this in v15.5.1 Current. As I understand the setter has been disabled ?

  20. bmeck commented on Jan 11, 2021

    @bmeck
    Member

    @GrosSacASac it is enabled by default but can be disabled via CLI flag

  21. ChALkeR commented on Jan 26, 2021

    @ChALkeR
    Member

    I believe babel/babel#12693 should fix the babel side, that was the last part in babel codebase that generated __proto__-first code afaik.

  22. GrosSacASac commented on Jan 26, 2021

    @GrosSacASac
    Contributor

    Since what version ? I could not find it in the changelogs ?

  23. ChALkeR commented on Jan 26, 2021

    @ChALkeR
    Member

    @GrosSacASac If you are speaking about Babel fix — that PR I linked is not merged yet.

    That is, that PR only affects loose: true class transforms.
    Without loose mode, Babel was already fine since babel/babel#7675 (which was landed in v7.0.0) afaik.

  24. GrosSacASac commented on Jan 26, 2021

    @GrosSacASac
    Contributor

    Sorry, I was asking about Node

  25. targos commented on Jan 26, 2021

    @targos
    Member

    --disable-proto was added in v13.12.0 and backported to v12.17.0: https://nodejs.org/dist/latest-v15.x/docs/api/cli.html#cli_disable_proto_mode

  26. added a commit that references this issue on Jan 26, 2021
  27. ChALkeR commented on Feb 3, 2021

    @ChALkeR
    Member

    Babel v7.12.13 should be fine even in loose mode.
    Outside of loose mode, anything from v7.0.0 onwards appears to fine afaik.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    securityIssues and PRs related to security.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions