node中的密碼安全
阿新 • • 發佈:2018-12-10
本文將講解對於前後端分離的專案,前端註冊或登入時如何保證使用者密碼安全傳輸到server端,最終存入資料庫
為什麼需要加密
加密真的有必要嗎?我們先來看一看前端發起的ajax請求中,如果不對密碼進行加密,會發生什麼。f12開啟chrome開發者工具,找到請求,檢視請求引數如下:
如果你的協議是http,那麼前端傳給後端的密碼差不多裸奔狀態,因為http傳輸的是明文,很可能在傳輸過程中被竊聽,偽裝或篡改。那麼,弄個https不就好了嗎?https的確能夠極大增加網站的安全性,但是用https得先買證書(也有免費的),對於個人站點或者不想弄證書的情況下,那最起碼也得對使用者密碼進行一下加密吧。
流程圖
先看一下大體流程圖,首先,我們用工具生成公鑰和私鑰,將其放入server端,前端發起請求獲取公鑰,拿到公鑰後對密碼進行加密,然後將加密後的密碼傳送到server端,server端將用金鑰解密,最後再用sha1加密密碼,存入資料庫。
生成RSA公鑰和金鑰
既然要加密,那麼首先得有生成加密的工具,常見的有openssl,但這裡不介紹,感興趣的請自行查閱,對於node而言,我介紹一個不錯的庫Node-RSA,我們將用它來生成RSA公鑰和金鑰。
RSA是一種非對稱加密演算法,即由一個金鑰
和一個公鑰
構成的金鑰對,通過金鑰加密,公鑰解密,或者通過公鑰加密,金鑰解密。其中,公鑰可以公開,金鑰必須保密
。
用Node-RSA生成的公鑰和金鑰程式碼如下:
const NodeRSA = require('node-rsa') const fs = require('fs') // Generate new 512bit-length key var key = new NodeRSA({b: 512}) key.setOptions({encryptionScheme: 'pkcs1'}) var privatePem = key.exportKey('pkcs1-private-pem') var publicDer = key.exportKey('pkcs8-public-der') var publicDerStr = publicDer.toString('base64') // 儲存返回到前端的公鑰 fs.writeFile('./pem/public.pem', publicDerStr, (err) => { if (err) throw err console.log('公鑰已儲存!') }) // 儲存私鑰 fs.writeFile('./pem/private.pem', privatePem, (err) => { if (err) throw err console.log('私鑰已儲存!') })
執行完成後,我們將在根目錄下得到公鑰和私鑰檔案:
前端加密
核心程式碼如下:
<script src="https://cdn.bootcss.com/jsencrypt/2.3.1/jsencrypt.min.js"></script>
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
<script>
function reg() {
axios({
method: 'post',
url: 'http://127.0.0.1:3000/getPublicKey'
})
.then(res => {
let result = res.data
// 從後端獲取的公鑰 String
var publicPem = result
// 用JSEncrypt對密碼進行加密
var encrypt = new JSEncrypt()
encrypt.setPublicKey(publicPem)
var password = 'abc123'
password = encrypt.encrypt(password)
axios({
method: 'post',
url: 'http://127.0.0.1:3000/reg',
data: {
password: password
}
})
.then(res => {
let result = res.data
console.log(result)
})
.catch(error => {
console.log(error)
})
})
}
</script>
前端將用到jsencrypt對其進行加密,詳細用法請參考github。
後端解密
後端核心程式碼:
const express = require('express');
const crypto = require('crypto');
const fs = require('fs');
var privatePem = fs.readFileSync('./pem/private.pem');
var app = express();
app.use(express.json());
// CORS 注意:要放在處理路由前
function crossDomain(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
app.use(crossDomain)
app.use(function (req, res, next) {
// 不加會報錯
if (req.method === 'OPTIONS') {
res.end('ok')
return
}
switch (req.url) {
case '/getPublicKey':
let publicPem = fs.readFileSync('./pem/public.pem', 'utf-8')
res.json(publicPem)
break
case '/reg':
// 解密
var privateKey = fs.readFileSync('./pem/private.pem', 'utf8')
var password = req.body.password
var buffer2 = Buffer.from(password, 'base64')
var decrypted = crypto.privateDecrypt(
{
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PADDING // 注意這裡的常量值要設定為RSA_PKCS1_PADDING
},
buffer2
)
console.log(decrypted.toString('utf8'))
// sha1加密
var sha1 = crypto.createHash('sha1');
var password = sha1.update(decrypted).digest('hex');
console.log('輸入到資料庫中的密碼是: ', password)
// 存入資料庫中
// store to db...
res.end('reg ok')
break
}
})
app.listen(3000, '127.0.0.1')
這裡,我是用node自帶模組crpto進行解密,當然,你也可以用Node-RSA的方法進行解密。
最後
當然,關於網路安全是一個大話題,本篇只是對其中的一小部分進行介紹,歡迎留言討論,希望對您有幫助。