您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何提高Node.js應用程序的安全性”,在日常操作中,相信很多人在如何提高Node.js應用程序的安全性問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何提高Node.js應用程序的安全性”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
1. 數據驗證 - 永遠不要信任你的用戶
來自用戶輸入或其他系統的數據,你都必須要進行驗證。否則,這會對當前系統造成威脅,并導致不可想象的安全漏洞。現在,讓我們學習如何驗證Node.js中的傳入數據。你可以使用名為 validator 的模塊來執行數據驗證。 例如:
const validator = require('validator'); validator.isEmail('foo@bar.com'); //=> true validator.isEmail('bar.com'); //=> false
另外,你也可以使用 joi 模塊來進行數據和模型的驗證,例如:
const joi = require('joi'); try { const schema = joi.object().keys({ name: joi.string().min(3).max(45).required(), email: joi.string().email().required(), password: joi.string().min(6).max(20).required() }); const dataToValidate = { name: "Shahid", email: "abc.com", password: "123456", } const result = schema.validate(dataToValidate); if (result.error) { throw result.error.details[0].message; } } catch (e) { console.log(e); }
2. SQL注入攻擊
SQL注入可以讓惡意用戶通過傳遞非法參數,來篡改SQL語句。下面是一個例子,假設你寫了這樣一個SQL:
UPDATE users SET first_name="' + req.body.first_name + '" WHERE id=1332;
在正常情況下,你希望這次查詢應該是這樣的:
UPDATE users SET first_name = "John" WHERE id = 1332;
但是現在,如果有人將 first_name 的值按下面這種方式傳遞:
John", last_name="Wick"; --
這時,你的SQL語句就會變成這樣:
UPDATE users SET first_name="John", last_name="Wick"; --" WHERE id=1001;
你會看到, WHERE 條件被注釋掉了,這次更新會將整張表所有用戶的 first_name 改成 John , last_name 改成 Wick 。這下,你闖禍了!
如何避免SQL注入
避免SQL注入攻擊最有效的辦法就是將輸入數據進行過濾。你可以對每一個輸入數據逐一進行驗證,也可以用參數綁定的方式驗證。開發者們最常用的就是參數綁定的方式,因為它高效而且安全。
如果你在使用一些比較流行的ORM框架,例如sequelize、hibernate等等,那么框架中就已經提供了這種數據驗證和SQL注入保護機制。
如果你更喜歡依賴數據庫模塊,例如 mysql for Node ,那么你可以使用數據庫提供的過濾方法。下面代碼是使用 mysql for Node 的一個例子:
var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'me', password : 'secret', database : 'my_db' }); connection.connect(); connection.query( 'UPDATE users SET ?? = ? WHERE ?? = ?', ['first_name',req.body.first_name, ,'id',1001], function(err, result) { //... });
?? 的地方被字段名稱替換, ? 的地方被字段值替換,這樣就保證了輸入值的安全性。
你也可以使用存儲過程來提高安全級別,但是由于缺乏可維護性,開發人員傾向于避免使用存儲過程。
同時,你還應該執行服務器端的數據驗證。 但我不建議你手動驗證每個字段,可以使用 joi 等模塊來解決這個問題。
類型轉換
JavaScript是一種動態類型語言,即值可以是任何類型。你可以使用類型轉換方法來驗證數據的類型,這樣就能保證,只有指定類型的數據才可以進入數據庫。比如,用戶ID只能是數字類型,看下面的代碼:
var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'me', password : 'secret', database : 'my_db' }); connection.connect(); connection.query( 'UPDATE users SET ?? = ? WHERE ?? = ?', ['first_name',req.body.first_name, ,'id',Number(req.body.ID)], function(err, result) { //... });
你注意到變化了嗎?這里我們使用了 Number(req.body.ID) 方法來確保用戶ID必須為數字。
3. 應用程序認證和授權
敏感數據(例如密碼)應該以一種安全的方式存儲在系統中,這樣,惡意用戶就不會濫用敏感信息。在本節中,我們將學習如何存儲和管理通用的密碼,幾乎每個應用程序在其系統中都有不同的密碼存儲方式。
密碼哈希
哈希是一個將輸入值生成固定大小字符串的函數。哈希函數的輸出值是無法解密的,因此可以說是“單向的”。因此,像密碼這樣的數據,存儲在數據庫中的值必須是哈希值,而不是明文。
你也許想知道,既然哈希是一種不可逆的加密方式,那么攻擊者又是如何取得密碼訪問權限的呢?
正如我上面提到的那樣,哈希加密使用輸入字符串并生成固定長度的輸出值。因此,攻擊者采取了相反的方法,他們從常規密碼列表中生成哈希,然后將哈希與系統中的哈希進行比較以找到密碼。這種攻擊方式叫做查表法(Lookup Tables)
這就是為什么你作為系統架構師,絕不允許系統中使用簡單通用密碼的原因。為了避免攻擊,你也可以使用一種叫”salt"的東西,我們稱之為“哈希加鹽法”。將salt附加到密碼哈希中,從而使輸入值唯一。salt值必須是隨機的不可預測的。我們建議你使用的哈希算法是 BCrypt ,在Node.js中,你可以使用bcyrpt節點模塊執行哈希處理。
請參考下面例子中的代碼:
const bcrypt = require('bcrypt'); const saltRounds = 10; const password = "Some-Password@2020"; bcrypt.hash( password, saltRounds, (err, passwordHash) => { //we will just print it to the console for now //you should store it somewhere and never logs or print it console.log("Hashed Password:", passwordHash); });
SaltRounds 函數是哈希函數的成本,成本越高,生成的hash密碼越安全。你應該根據服務器的計算能力來確定salt值,密碼的hash值生成后,用戶輸入的密碼將會和存儲在數據庫中的hash值比對,參考代碼如下:
const bcrypt = require('bcrypt'); const incomingPassword = "Some-Password@2020"; const existingHash = "some-hash-previously-generated" bcrypt.compare( incomingPassword, existingHash, (err, res) => { if(res && res === true) { return console.log("Valid Password"); } //invalid password handling here else { console.log("Invalid Password"); } });
密碼存儲
無論你是用數據庫還是文件來存儲密碼,都不能使用明文存儲。我們在上一節已經學到,你可以將密碼進行哈希后存儲在數據庫中。我推薦密碼字段用 varchar(255) 數據類型,你也可以選擇不限長度的字段類型。如果你使用的是 bcrypt ,則可以使用 varchar(60) 字段類型,因為bcrypt會生成固定長度為60個字符的哈希。
認證和授權
一個擁有合適的角色權限系統,將會阻止一些惡意用戶在系統中做一些越權的事情。為了實現正確的授權過程,將合適的角色和權限分配給每個用戶,以便他們可以執行權限范圍內的某些任務。在Node.js中,你可以使用著名的ACL模塊,根據系統中的授權來開發訪問控制列表。
const ACL = require('acl2'); const acl = new ACL(new ACL.memoryBackend()); // guest is allowed to view blogs acl.allow('guest', 'blogs', 'view') // check if the permission is granted acl.isAllowed('joed', 'blogs', 'view', (err, res) => { if(res){ console.log("User joed is allowed to view blogs"); } });
請查閱acl2文檔以獲取更多信息和示例代碼。
4. 暴力攻擊防護
黑客經常會使用軟件反復使用不同的密碼嘗試獲得系統權限,直到找到有效密碼為止,這種攻擊方式叫做暴力攻擊。為了避免這種攻擊,一種簡單有效的辦法是“讓他等一會”,也就是說,當某人嘗試登錄系統并嘗試輸入無效密碼3次以上時,請讓他們等待60秒左右,然后再嘗試。這樣,攻擊者將大大提高時間成本,并且將使他們永遠無法破解密碼。
防止這種攻擊的另一種方法是屏蔽無效登錄請求的IP。系統在24小時內允許每個IP進行3次錯誤地登錄嘗試。如果有人嘗試進行暴力破解,則將其IP封鎖24小時。 許多公司已使用這種方法來防止暴力攻擊。 如果使用Express框架,則有一個中間件模塊可在傳入請求中啟用速率限制。 它稱為 express = brute 。
下面是一個例子。
安裝依賴項
npm install express-brute --save
在路由中啟用它
const ExpressBrute = require('express-brute'); const store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this in production const bruteforce = new ExpressBrute(store); app.post('/auth', bruteforce.prevent, // error 429 if we hit this route too often function (req, res, next) { res.send('Success!'); } ); //...
5. HTTPS安全傳輸
現在已經2021年了,你也應該使用HTTPS來向網絡中發送數據了。HTTPS是具有安全通信支持的HTTP協議的擴展。使用HTTPS,可以保證用戶在互聯網中發送的數據是被加密的,是安全的。
在這里我不打算詳細介紹HTTPS協議的工作原理,我們只討論如何使用它。這里我強烈推薦使用 LetsEncrypt 來為你的所有域名生成安全證書。
你可以在基于Apache和Nginx的Web服務器上使用LetsEncrypt。我強烈建議你在反向代理或網關層上使用HTTPS協議,因為它們有很多繁重的計算操作。
6. 會話劫持保護
會話(session)是任何動態Web應用程序最重要的部分,一個安全的會話對用戶和系統來說真的是非常必要的。會話是使用Cookie實現的,因此必須確保其安全以防止會話劫持。以下是可以為每個cookie設置的屬性列表以及它們的含義:
secure - 該屬性告訴瀏覽器僅在通過HTTPS發送請求時才發送cookie。
HttpOnly - 該屬性用于防止跨站點腳本攻擊,因為它不允許通過JavaScript訪問cookie。
domain - 該屬性用于與請求URL的服務器域名進行比較,如果域名匹配,或者是其子域,那么接下來就會檢查path屬性。
path - 除了domian之外,還可以指定cookie有效的URL路徑。 如果domain和路徑匹配,則可以在請求中發送cookie。
expires - 該屬性用于設置持久性的cookie,cookie只會在超過了設定的日期才會過期。
在Express框架中,你可以使用 express-session npm模塊來管理會話。
const express = require('express'); const session = require('express-session'); const app = express(); app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: true, cookie: { secure: true, path: '/'} }));
7. 跨站點請求偽造攻擊(CSRF)防護
跨站點請求偽造攻擊利用系統中受信任的用戶,對Web應用程序執行有害的惡意操作。在Node.js中,我們可以使用 csurf 模塊來緩解CSRF攻擊。該模塊需要首先初始化 express-session 或 cookie-parser ,你可以看看下面的示例代碼:
const express = require('express'); const cookieParser = require('cookie-parser'); const csrf = require('csurf'); const bodyParser = require('body-parser'); // setup route middlewares const csrfProtection = csrf({ cookie: true }); const parseForm = bodyParser.urlencoded({ extended: false }); // create express app const app = express(); // we need this because "cookie" is true in csrfProtection app.use(cookieParser()); app.get('/form', csrfProtection, function(req, res) { // pass the csrfToken to the view res.render('send', { csrfToken: req.csrfToken() }); }); app.post('/process', parseForm, csrfProtection, function(req, res) { res.send('data is being processed'); }); app.listen(3000);
在網頁上,你需要創建一個隱藏輸入域,將CSRF令牌保存在該輸入域中,例如:
<form action="/process" method="POST"> <input type="hidden" name="_csrf" value="{{csrfToken}}"> Favorite color: <input type="text" name="favoriteColor"> <button type="submit">Submit</button> </form>
如果使用的是AJAX請求,那么CSRF令牌可以通過請求頭(header)來傳遞。
var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); headers: { 'CSRF-Token': token }
8. 拒絕服務
拒絕服務或DOS攻擊,可以讓攻擊者通過破壞系統,使系統被迫關閉服務或用戶無法訪問服務。攻擊者通常會向系統發送大量的流量和請求,從而增加服務器CPU和內存負載,導致系統崩潰。為了緩解Node.js應用程序中的DOS攻擊,首先是要識別此類事件, 我強烈建議將這兩個模塊集成到系統中。
鴻蒙官方戰略合作共建——HarmonyOS技術社區
Account lockout - 在n次嘗試失敗后,將帳戶或IP地址鎖定一段時間(例如24小時?)
Rate limiting - 限制用戶在特定時間段內只能請求系統n次,例如,單個用戶每分鐘只能請求3次。
正則表達式拒絕服務攻擊(ReDOS)是DOS攻擊的一種,攻擊者利用系統中正則表達式的設計缺陷或計算復雜度來大量消耗服務器的系統資源,造成服務器的服務中斷或停止。
我們可以使用一些工具來檢查有風險的正則表達式,從而避免這些正則表達式的使用。例如這個工具:
https://github.com/davisjam/vuln-regex-detector
9. 依賴關系驗證
我們在項目中都使用了大量的依賴項。我們還需要檢查并驗證這些依賴關系,以確保整個項目的安全性。NPM已經具有這樣的審核功能來查找項目的漏洞。只需在源代碼目錄中運行下面的命令即可:
npm audit
要修復漏洞,可以運行此命令:
npm audit fix
您也可以先進行 dry run 來檢查修復程序,然后再將其應用到項目中。
npm audit fix --dry-run --json
10. HTTP安全頭信息
HTTP提供了一些安全頭信息,可以防止常見的攻擊。如果使用的是Express框架,則可以使用 helmet 模塊,1行代碼就可以啟用所有安全頭。
npm install helmet --save
下面來看看如何使用:
const express = require("express"); const helmet = require("helmet"); const app = express(); app.use(helmet()); //...
這將啟用以下HTTP頭:
Strict-Transport-Security
X-frame-Options
X-XSS-Protection
X-Content-Type-Protection
Content-Security-Policy
Cache-Control
Expect-CT
Disable X-Powered-By
這些HTTP頭可防止惡意用戶的各種攻擊,例如點擊劫持,跨站點腳本攻擊等。
到此,關于“如何提高Node.js應用程序的安全性”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。