Hi,大家好,昨天我們完成了使用者登入的雛形,今天來將這個雛形轉變為符合 ISO27001資通安全規則之使用者登入機制,那麼,就讓我們開始吧。
這幾年若是有接觸到公家專案或是金融機構專案的人,應該對ISO27001不陌生,行政院在近幾年非常注重資訊安全,因此有訂定出普、中、高三個等級的防護基準,並且每年進行稽核。在防護基準中,針對使用者管理有幾個要求項目,簡單說起來有下項目:
以下是這次 side project 中,我所規畫之使用者帳號密碼表
CREATE TABLE SYSUSER (
USERID VARCHAR(20), -- 使用者帳號
PWD VARCHAR(512), -- 密碼密文
USERNAME VARCHAR(40), -- 使用者姓名
EMAIL VARCHAR(50), -- e-mail
USERSDT DATE, -- 帳號起始日期
USEREDT DATE, -- 帳號到期日
USERED VARCHAR(1) DEFAULT 'N', -- 是否可用
PWDSLIST TEXT, -- 前3代密碼
PWDCHDATE DATE, -- 密碼異動日期
GUID VARCHAR(40) default UUID_GENERATE_V4(), -- 資料唯一編碼
CONSTRAINT SYSUSER_PK PRIMARY KEY (GUID)
);
說明:
PWDSLIST密碼會直接存在 json 陣列,元素只有 3個,每次變更密碼時進行檢核,小於3個的話直接附加進陣列中,大於3個的情形則刪除第0個元素後,將修改前的密碼附加進入陣列中
修改系統登入router
/**
* 系統登入
*/
router.post("/login", async (req, res, next) => {
try {
let uid = req.body.uid
let pwd = req.body.pwd
let userinfo = await userdao.chkuser(uid, pwd)
if(!userinfo) {
res.render("loginform", {
breads: createBreadcrumb("系統登入"),
errmsg: "登入失敗"
})
return false;
} else {
//驗證通過
req.session.user = userinfo
res.redirect("/saf/mgr")
}
} catch(err) {
if(err === "密碼已逾期") {
//密碼逾期後,紀錄目前登入之id後,導向至變更密碼畫面
req.session.chgpwduser = uid
res.redirect("/saf/chgpwd")
} else {
//驗證不通過,送回登入頁,並將錯誤訊息一併送回
res.render("loginform", {
breads: createBreadcrumb("系統登入"),
errmsg: err
})
}
}
})
說明:
接收表單傳入之帳號密碼後,呼叫 userdao.chkuser 進行驗證,若有回傳使用者資訊,則認定驗證通過,紀錄 session後重新導向至登入人員之系統主頁,驗證時若接收到「密碼已逾期」之錯誤,則重導至變更密碼畫面,否則回到系統登入表單
修改使用者驗證之功能
'use strict';
const sysuser = require("./db/sysuser")
const crypto = require('crypto');
/**
* 密碼以sha 512 進行加密
* @param {string} src 密碼原文
*/
function sha512Str(src) {
let hash = crypto.createHash('sha512');
let data = hash.update(src, 'utf-8');
let gen_hash = data.digest('hex');
return gen_hash
}
/**
* 以現在時間進行日期檢測
* @param {string} dat 檢測日期字串,需為 yyyy-mm-dd格式
* @returns 大於 0 - 比現在晚,小於 0 - 比現在早、 等於 0 - 和現在一樣
*/
function chdate(dat) {
let datval = new Date(dat).getTime()
let nowval = new Date().getTime()
return datval - nowval
}
/**
* 檢查密碼更換紀錄
* @param {*} obj
*/
function chkPwdTime(obj) {
let pwddat = obj.usersdt
if(obj.pwdchdate) {
pwddat = obj.pwdchdate
}
let dv = chdate(pwddat)
if(dv / 24 / 60 / 60 / 1000 < -90){
return true
} else {
return false
}
}
module.exports = {
/**
* 使用者驗證
* @param {string} uid 系統帳號
* @param {string} pwd 系統密碼
*/
chkuser: async(uid, pwd) =>{
return new Promise((resolve, reject) => {
let shapwd = sha512Str(pwd)
sysuser.findOne({
where: {
userid: uid,
pwd: shapwd
}
}).then((data) => {
if(!data) {
reject("帳號密碼錯誤")
return false;
}
let obj = data.dataValues
if(obj.usered === "N") {
reject("帳號已停用")
return false;
}
if(obj.usersdt) {
if(chdate(obj.usersdt) > 0) {
reject("帳號尚未啟用")
return false;
}
}
if(obj.useredt) {
if(chdate(obj.useredt) < 0) {
reject("帳號已逾期")
return false;
}
}
if(chkPwdTime(obj)) {
reject("密碼已逾期")
return false;
}
resolve(obj)
}).catch((err) => {
reject(err.message)
})
})
}
}
說明:
接收到帳號密碼後,先將密碼進行 sha512 之加密,並送入資料庫比對,若沒找到符合之資料,回傳帳號密碼錯誤之訊息,接下來依序檢查 1. 帳號是否停用、2. 若有設定啟用日的話,是否已經超過啟用日期了、3. 若有設定停用日期的話,是否已超過停用日期了、4. 密碼是否逾期,全部通過才認定通過使用者驗證作業
近幾年相關資安要求的力度很大,因此使用者驗證的作法需要比較複雜一些,今天的部份完成了使用者驗證的機制,但是還沒結束,明天會再完成密碼變更之機制,此時才是一個完成的使用者驗證含密碼管理的功能,那麼,我們明天再繼續吧