Sudah banyak yang dikatakan tentang popularitas NodeJS. Peningkatan jumlah aplikasi jelas - NodeJS cukup mudah dipelajari, memiliki sejumlah besar perpustakaan, serta ekosistem yang berkembang secara dinamis.Kami telah menyiapkan rekomendasi untuk pengembang NodeJS berdasarkan OWASP Cheat Sheets untuk membantu Anda mengantisipasi masalah keamanan saat mengembangkan aplikasi.Rekomendasi keamanan untuk aplikasi NodeJS dapat dibagi ke dalam kategori berikut:- Keamanan selama pengembangan aplikasi;
- Keamanan server;
- Keamanan platform;
Keamanan Pengembangan Aplikasi
Menghindari panggilan balikmenggunakan fungsi panggilan balik (callback) adalah salah satu kekuatan terbesar NodeJS, namun ketika bersarang panggilan balik, Anda dapat dengan mudah lupa untuk menangani kesalahan di salah satu fungsi. Salah satu cara untuk menghindari panggilan balik neraka adalah dengan menggunakan janji. Bahkan jika modul yang Anda gunakan tidak mendukung bekerja dengan janji-janji, Anda selalu dapat menggunakan Promise.promisifyAll (). Namun meski menggunakan janji, perlu diperhatikan untuk bersarang. Untuk benar-benar menghindari kesalahan panggilan balik neraka, patuhi rantai janji βdatarβ.Contoh panggilan balik neraka:function func1(name, callback) {
setTimeout(function() {
}, 500);
}
function func2(name, callback) {
setTimeout(function() {
}, 100);
}
function func3(name, callback) {
setTimeout(function() {
}, 900);
}
function func4(name, callback) {
setTimeout(function() {
}, 3000);
}
func1("input1", function(err, result1){
if(err){
}
else {
func2("input2", function(err, result2){
if(err){
}
else{
func3("input3", function(err, result3){
if(err){
}
else{
func4("input 4", function(err, result4){
if(err){
}
else {
}
});
}
});
}
});
}
});
Kode yang sama menggunakan janji rantai datar:function func1(name, callback) {
setTimeout(function() {
}, 500);
}
function func2(name, callback) {
setTimeout(function() {
}, 100);
}
function func3(name, callback) {
setTimeout(function() {
}, 900);
}
function func4(name, callback) {
setTimeout(function() {
}, 3000);
}
func1("input1")
.then(function (result){
return func2("input2");
})
.then(function (result){
return func3("input3");
})
.then(function (result){
return func4("input4");
})
.catch(function (error) {
});
Batasi ukuran permintaan.Mem-parsing badan permintaan bisa menjadi sumber daya yang cukup intensif. Jika Anda tidak membatasi ukuran permintaan, penyerang akan dapat mengirim permintaan yang cukup besar yang dapat mengisi semua ruang disk atau menghabiskan semua sumber daya server, tetapi pada saat yang sama, membatasi ukuran permintaan untuk semua kasus mungkin salah, karena ada permintaan, seperti mengunduh file. Karena itu, disarankan untuk menetapkan batas untuk berbagai jenis konten. Misalnya, menggunakan kerangka kerja ekspres, ini dapat diimplementasikan sebagai berikut:app.use(express.urlencoded({ limit: "1kb" }));
app.use(express.json({ limit: "1kb" }));
app.use(express.multipart({ limit:"10mb" }));
Perlu dicatat bahwa penyerang dapat mengubah jenis konten permintaan dan menghindari batasan, oleh karena itu, perlu untuk memeriksa apakah konten permintaan cocok dengan jenis konten yang ditentukan dalam header permintaan. Jika memeriksa tipe konten memengaruhi kinerja, Anda hanya dapat memeriksa tipe atau kueri tertentu yang lebih besar dari ukuran tertentu.Jangan memblokir loop acara. Komponen penting dari bahasa ini adalah loop acara, yang hanya memungkinkan Anda untuk mengubah konteks eksekusi tanpa menunggu operasi selesai. Namun, ada operasi pemblokiran yang penyelesaiannya NodeJS harus menunggu sebelum melanjutkan dengan kode. Misalnya, sebagian besar metode sinkron memblokir:const fs = require('fs');
fs.unlinkSync('/file.txt');
Disarankan untuk melakukan operasi seperti itu secara serempak:const fs = require('fs');
fs.unlink('/file.txt', (err) => {
if (err) throw err;
});
Pada saat yang sama, jangan lupa bahwa kode yang berdiri setelah panggilan asinkron akan dieksekusi tanpa menunggu selesainya operasi sebelumnya.Misalnya, dalam kode di bawah ini, file akan dihapus sebelum dibaca, yang dapat menyebabkan kondisi balapan.const fs = require('fs');
fs.readFile('/file.txt', (err, data) => {
});
fs.unlinkSync('/file.txt');
Untuk menghindari ini, Anda dapat menulis semua operasi dalam fungsi yang tidak menghalangi:const fs = require('fs');
fs.readFile('/file.txt', (err, data) => {
fs.unlink('/file.txt', (err) => {
if (err) throw err;
});
});
Periksa bidang input.Memeriksa bidang input adalah bagian penting dari keamanan aplikasi apa pun. Kesalahan validasi dapat menyebabkan aplikasi Anda menjadi rentan segera terhadap berbagai jenis serangan: injeksi sql, xss, injeksi perintah, dan lainnya. Untuk menyederhanakan validasi formulir, Anda dapat menggunakan paket validator, mongo-express-sanitize.Escape data penggunaSalah satu aturan yang akan membantu Anda melindungi diri dari serangan xss adalah untuk melindungi data pengguna. Anda dapat menggunakan perpustakaan escape-html atau node-esapi untuk ini.Simpan logSelain itu, ini akan membantu dalam kesalahan debugging, logging dapat digunakan untuk menanggapi insiden. Anda dapat membaca lebih lanjut tentang perlunya login di sini.. Salah satu paket logging NodeJS yang paling populer adalah Winston dan Bunyan. Contoh di bawah ini menunjukkan cara menggunakan Winston untuk membuat log ke konsol dan file:var logger = new (Winston.Logger) ({
transports: [
new (winston.transports.Console)(),
new (winston.transports.File)({ filename: 'application.log' })
],
level: 'verbose'
});
Mengontrol siklus peristiwaJika server Anda berada dalam kondisi lalu lintas jaringan yang intensif, pengguna mungkin mengalami kesulitan dengan ketersediaan layanan Anda. Ini pada dasarnya adalah serangan DoS. Dalam hal ini, Anda dapat melacak waktu respons dan, jika melebihi waktu yang ditentukan, mengirim pesan ke 503 Server Terlalu Sibuk. Modul toobusy-js dapat membantu.Contoh menggunakan modul:var toobusy = require('toobusy-js');
var express = require('express');
var app = express();
app.use(function(req, res, next) {
if (toobusy()) {
res.send(503, "Server Too Busy");
} else {
next();
}
});
Ambil tindakan pencegahan terhadap kekuatan brutal, sekali lagi, modul datang untuk menyelamatkan. Misalnya, express-brute atau express-bouncer. Contoh penggunaan:var bouncer = require('express-bouncer');
bouncer.whitelist.push('127.0.0.1');
bouncer.blocked = function (req, res, next, remaining) {
res.send(429, "Too many requests have been made. Please wait " + remaining/1000 + " seconds.");
};
app.post("/login", bouncer.block, function(req, res) {
if (LoginFailed){ }
else {
bouncer.reset( req );
}
});
Menggunakan CAPTCHA adalah penanggulangan brute force umum lainnya. Modul yang sering digunakan untuk membantu mengimplementasikan CAPTCHA adalah svg-captcha.Menggunakan Token CSRFSalah satu cara yang paling dapat diandalkan untuk melindungi terhadap serangan CSRF adalah dengan menggunakan token CSRF. Token harus dibuat dengan entropi tinggi, diperiksa ketat dan diikat ke sesi pengguna. Untuk memastikan pengoperasian token CSRF, Anda dapat menggunakan modul csurf.Contoh penggunaan:var csrf = require('csurf');
csrfProtection = csrf({ cookie: true });
app.get('/form', csrfProtection, function(req, res) {
res.render('send', { csrfToken: req.csrfToken() })
})
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('data is being processed');
});
Jangan lupa menambahkan token ke bidang tersembunyi di halaman:<input type="hidden" name="_csrf" value="{{ csrfToken }}">
Anda dapat membaca lebih lanjut tentang token CSRF di artikel kami .Hapus rute yang tidak perlu.Aplikasi web tidak boleh berisi halaman yang tidak digunakan oleh pengguna, karena ini dapat meningkatkan permukaan serangan. Karena itu, semua rute API yang tidak digunakan harus dinonaktifkan. Anda terutama harus memperhatikan pertanyaan ini jika Anda menggunakan kerangka kerja Sails atau Feathers, karena mereka secara otomatis menghasilkan titik akhir API.Lindungi diri Anda dari HPP (Polusi Parameter HTTP)Secara default, express menambahkan semua parameter dari permintaan ke array. OWASP merekomendasikan menggunakan modul hpp, yang mengabaikan semua nilai parameter dari req.query dan / atau req.body dan hanya memilih nilai terakhir dari yang duplikat.var hpp = require('hpp');
app.use(hpp());
Pantau nilai yang dikembalikan.Misalnya, tabel pengguna dapat menyimpan data penting: kata sandi, alamat email, tanggal lahir, dll. Karena itu, penting untuk hanya mengembalikan data yang diperlukan.Contohnya: exports.sanitizeUser = function(user) {
return {
id: user.id,
username: user.username,
fullName: user.fullName
};
};
Gunakan deskriptorGunakan deskriptor untuk menggambarkan perilaku properti untuk berbagai operasi: dapat ditulis - apakah mungkin untuk mengubah nilai properti, dapat dihitung - apakah mungkin untuk menggunakan properti dalam for..in loop, dapat dikonfigurasi - apakah mungkin untuk menimpa properti. Dianjurkan untuk memperhatikan properti yang terdaftar, karena ketika mendefinisikan properti suatu objek, semua atribut ini disetel ke true secara default. Anda dapat mengubah nilai properti sebagai berikut:var o = {};
Object.defineProperty(o, "a", {
writable: true,
enumerable: true,
configurable: true,
value: "A"
});
Gunakan ACLAcl dapat membantu membedakan akses data berdasarkan peran. Misalnya, menambahkan izin terlihat seperti ini:
acl.allow('guest', 'blogs', 'view')
acl.allow('member', 'blogs', ['edit', 'view', 'delete'])
Tangkap uncaughtExceptionSecara default, jika ada pengecualian yang tidak tertangkap, NodeJS akan membuang jejak stack saat ini dan mengakhiri utas eksekusi. Namun, NodeJS memungkinkan Anda untuk menyesuaikan perilaku ini. Jika ada pengecualian yang tidak tertangkap, peristiwa uncaughtException dimunculkan, yang dapat ditangkap menggunakan objek proses:process.on("uncaughtException", function(err) {
process.exit();
});
Perlu diingat bahwa ketika terjadi uncaughtException, perlu untuk menghapus semua sumber daya yang dialokasikan (misalnya, deskriptor file dan penangan) sebelum menyelesaikan proses Z untuk menghindari kesalahan yang tidak terduga. Sangat tidak disarankan bahwa program terus berjalan jika uncaughtException terjadi.Juga, saat menampilkan pesan kesalahan, pengguna tidak boleh mengungkapkan informasi kesalahan terperinci, seperti tumpukan jejak.Keamanan server
Mengatur flag untuk header ketika bekerja dengan cookie.Ada beberapa flag yang dapat membantu melindungi terhadap serangan seperti xss dan csrf: httpOnly, yang mencegah akses ke cookie melalui javascript; Aman - memungkinkan pengiriman cookie hanya melalui HTTPS dan SameSite, yang menentukan kemampuan untuk mentransfer cookie ke sumber daya pihak ketiga.Contoh penggunaan:var session = require('express-session');
app.use(session({
secret: 'your-secret-key',
key: 'cookieName',
cookie: { secure: true, httpOnly: true, path: '/user', sameSite: true}
}));
Menyetel tajuk HTTP untuk keamananBerikut ini tajuk dan contoh cara menghubungkannya untuk membantu Anda melindungi diri dari sejumlah serangan umum. Header diatur menggunakan modul helmβ’ Strict-Transport-Security: HTTP Strict Transport Security (HSTS) memberi tahu browser bahwa aplikasi hanya dapat diakses melalui HTTPSapp.use(helmet.hsts());
app.use(helmet.hsts("<max-age>", "<includeSubdomains>"));
β’ X-Frame-Options: menentukan apakah halaman dapat digunakan dalam bingkai, iframe, embed atau objekapp.use(hemlet.xframe());
helmet.xframe('sameorigin');
helmet.xframe('allow-from', 'http://alloweduri.com');
β’ X-XSS-Protection: memungkinkan browser untuk berhenti memuat halaman jika mendeteksi serangan XSS yang dipantulkan.var xssFilter = require('x-xss-protection');
app.use(xssFilter());
β’ X-Content-Type-Options: digunakan untuk mencegah serangan menggunakan tipe MIMEapp.use(helmet.noSniff());
β’ Kebijakan Konten-Keamanan-: Mencegah serangan seperti XSS dan serangan injeksi dataconst csp = require('helmet-csp')
app.use(csp({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
frameAncestors: ["'none'"],
imgSrc: ["'self'", "'http://imgexample.com'"],
styleSrc: ["'none'"]
}
}))
β’ Kontrol Cache dan Pragma: untuk mengelola caching, terutama tajuk ini dapat berguna untuk halaman yang berisi data sensitif. Namun, ingat bahwa menonaktifkan caching di semua halaman dapat memengaruhi kinerja.app.use(helmet.noCache());
β’ Opsi X-Unduh: header mencegah Inter Explorer dari menjalankan file yang diunduhapp.use(helmet.ieNoOpen());
β’ Expect-CT: Certificate Transparansi - mekanisme yang dibuat untuk menyelesaikan beberapa masalah dengan infrastruktur sertifikat SSL, header ini memberi tahu browser tentang perlunya verifikasi sertifikat tambahan dalam log CTvar expectCt = require('expect-ct');
app.use(expectCt({ maxAge: 123 }));
app.use(expectCt({ enforce: true, maxAge: 123 }));
app.use(expectCt({ enforce: true, maxAge: 123, reportUri: 'http://example.com'}));
β’ X-Powered-By: Header opsional yang digunakan untuk menunjukkan teknologi yang digunakan di server. Anda dapat menyembunyikan tajuk ini sebagai berikut:app.use(helmet.hidePoweredBy());
Selain itu, Anda dapat mengubah nilai untuk menyembunyikan informasi nyata tentang teknologi yang Anda gunakan:app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }));
Keamanan platform
Perbarui paket AndaKeamanan aplikasi Anda bergantung pada keamanan paket yang Anda gunakan, jadi penting untuk menggunakan versi terbaru paket. Untuk memastikan bahwa paket yang Anda gunakan tidak mengandung kerentanan yang diketahui, Anda dapat menggunakan daftar OWASP khusus . Anda juga dapat menggunakan pustaka yang memeriksa paket untuk kerentanan yang diketahui Retire.js.Jangan gunakan fungsi yang tidak aman.Ada beberapa fungsi yang direkomendasikan untuk dibuang jika memungkinkan. Di antara fungsi-fungsi ini adalah eval (), yang mengeksekusi string yang diambil sebagai argumen. Dalam kombinasi dengan input pengguna, menggunakan fungsi ini dapat menyebabkan kerentanan dalam eksekusi kode jarak jauh, karena untuk alasan yang sama, menggunakan child_process.exec juga tidak aman, karena fungsi meneruskan argumen yang diterima ke bin / sh.Selain itu, ada sejumlah modul yang harus Anda gunakan dengan hati-hati. Misalnya, modul fs untuk bekerja dengan file. Jika dengan cara tertentu input pengguna yang dihasilkan dilewatkan ke fungsi, maka aplikasi Anda mungkin menjadi rentan untuk memasukkan file lokal dan direktori traversal.Modul vm, yang menyediakan API untuk mengkompilasi dan menjalankan kode pada mesin virtual V8, harus digunakan hanya di kotak pasir.Di sini Anda dapat membiasakan diri dengan fungsi-fungsi lain yang dapat membuat aplikasi Anda tidak aman.Hati-hati menggunakan ekspresi reguler. Ekspresireguler dapat ditulis sehingga Anda dapat mencapai situasi di mana ekspresi akan tumbuh secara eksponensial, yang dapat menyebabkan penolakan layanan. Serangan semacam itu disebut ReDoS. Ada beberapa alat untuk memeriksa apakah ekspresi reguler aman, salah satunya adalah vuln-regex-detector.Jalankan linter secara berkalaSelama pengembangan, sulit untuk mengingat semua rekomendasi keselamatan, dan ketika menyangkut pengembangan tim, tidaklah mudah untuk mencapai kepatuhan dengan aturan oleh semua anggota tim. Untuk tujuan tersebut, ada alat untuk analisis keamanan statis. Alat semacam itu, tanpa mengeksekusi kode Anda, mencari kerentanan di dalamnya. Selain itu, linter memungkinkan Anda untuk menambahkan aturan khusus untuk menemukan tempat dalam kode yang mungkin rentan. Linter yang paling umum digunakan adalah ESLint dan JSHint.Gunakan mode ketat.Javascript memiliki sejumlah fungsi tidak aman dan usang yang tidak boleh digunakan. Untuk mengecualikan kemungkinan menggunakan fungsi-fungsi ini, mode ketat juga disediakan.Patuhi prinsip keselamatan umumRekomendasi tersebut menjelaskan fokus pada NodeJS, tetapi jangan lupa tentang prinsip keamanan umum yang harus diperhatikan terlepas dari platform yang digunakan.