Session原理、安全以及最基本的Express和Redis實現

基礎概念

Session管理是Web Application的基礎也是一個老生常談的話題。爲了方便後文的展開,更重要的是確認本身清晰的理解了整個Session管理的概念,我在此仍是決定贅述的整個流程。若是你已經對於Session概念很是清晰的話,能夠跳過本節不影響對於後文的理解。git

HTTP協議在設計的時候是無狀態的。這是一個很關鍵的概念,意味着服務器在處理請求的時候,並不關注這個請求是誰發來的。這對於以提供內容爲核心的Web1.0,例如門戶網站,很是適合。然而對於以應用服務爲核心的Web 2.0而言,服務器端必須有能力從請求中提取出請求者的身份信息,以在請求者不用反覆輸入身份的狀況下,提供連續的服務。github

實現請求身份驗證的方式不少,其中一種普遍接受的方式是使用服務器端產生的Session ID結合瀏覽器的Cookie實現對Session的管理,通常來講包括如下4個步驟:redis

  1. 服務器端的產生Session ID
  2. 服務器端和客戶端存儲Session ID
  3. 從HTTP Header中提取Session ID
  4. 根據Session ID從服務器端的Hash中獲取請求者身份信息

圖片描述

上圖是一個使用Redis Cluster來實現對Session管理的流程,但本質上除了redisMatrix.get和redisMatrix.set之外,和通常的Session管理流程是一致的。數據庫

簡單來講,一個請求到達的時候,服務器會先判斷是否帶有Session信息。若是有,則根據Session ID去數據庫中查找是否具備對應的用戶身份信息。此處可能會出現Session失效、非法的Session信息等可能性,那麼服務器視同無Ssession信息的狀況,從新的產生一個隨機的字符串,而且在Http返回頭中寫入新的Session ID信息。另外一者,若是服務器成功獲取了用戶的身份信息則以該身份爲請求者提供服務。express

使用Express和Redis對Session管理的實現

Redis是一個很是適合用於Session管理的數據庫。第一,它的結構簡單,key-value的形式很是符合SessionID-UserID的存儲;第二,讀寫速度很是快;第三,自身支持數據自動過時和清除;第四,語法、部署很是簡單。基於以上緣由,不少Session管理都是基於Redis實現的。
Express已經將Session管理的整個實現過程簡化到僅僅幾行代碼的配置的地步了,你徹底不用理解整個session產生、存儲、返回、過時、再頒發的結構,使用Express和Redis實現Session管理,只要兩個中間件就足夠了:segmentfault

廢話很少說仍是上代碼:瀏覽器

var express = require('express');
var session = require('express-session');
var RedisStore = require('connect-redis')(session);

var app = express();
var options = {
     "host": "127.0.0.1",
     "port": "6379",
     "ttl": 60 * 60 * 24 * 30,   //Session的有效期爲30天
};

// 此時req對象尚未session這個屬性
app.use(session({
     store: new RedisStore(options),
     secret: 'express is powerful'
}));
// 通過中間件處理後,能夠經過req.session訪問session object。好比若是你在session中保存了session.userId就能夠根據userId查找用戶的信息了。

req在通過session中間件的時候就會自動完成session的有效性驗證、延期/從新頒發、以及對session中數據的獲取了。安全

上述代碼只是對於請求的Session靜態處理,整個用戶管理的另外一個方面則是狀態的切換(用戶的登錄、登出)以及用戶數據的獲取。服務器

exports.signin = function(params, req, res){
    var username = params.username;
    var password = params.password;

    //查找用戶信息,看是否知足登錄條件
    var user = findUser(username, password);
    if(user){
        //成功獲取用戶對象
        req.session.regenerate(function(){
            req.user = user;
            req.session.userId = user.id;
            req.session.save();  //保存一下修改後的Session

            res.redirect('/account');
        });  
    }
    else{
        //用戶信息不符合,登錄失敗
    }
}

exports.signout = function(req, res){
    req.clearCookie('connect.sid');
    req.user = null;

    req.session.regenerate(function(){
        //從新生成session以後後續的處理
        res.redirect('/signin');
    })
}

exports.persist = function(req, res, next){
    var userId = req.session.userId;

    //經過user id去數據庫裏面查找User對象
    var user = findUserById(userId);

    if(user){
        req.user = user;
        next();
    }
    else{
        //該用戶不存在
    }
}

Session的安全問題

SessionId就如同請求者的身份證,一旦被攻擊者惡意得到,攻擊者即可以假裝成請求者對服務器發起請求,也就是咱們常常聽到的會話劫持(Session/Cookie Hijack)
關於會話劫持的原理推薦你們去看這篇文章session

基於上述實現方法的Session管理,我認爲基本上能夠排除

  • 暴力破解SessionId
  • 惡意植入固定SessionId

兩種可能,由於uid的庫基本上能夠保證SessionId的隨機性;而傳遞SessionId則依賴HTTP請求頭中的Cookie信息而非URL,同時在用戶登陸馬上更換SessionId。

惟一的可能性來源於Session的監聽,而對於這種攻擊有效的兩種防止辦法是:

  • Https
    不少網站僅僅在Login的階段使用Https防止用戶的用戶名、密碼信息被監聽者獲取,可是隨後的SessionId一樣有可能被監聽者獲取而僞造登陸者的身份信息。所以更加推薦的方式是全部的信息傳遞所有使用Https實現,這樣即便監聽着截獲了信息也沒法破解其中的內容。關於如何使用NodeJS創建一個HTTPS的server能夠參考《HTTPS的原理和NodeJS的實現》 這篇文章
  • httpOnly
    Express在options中提供了httpOnly的屬性,此屬性默認值爲true,這個屬性保證了Cookie的信息不可以經過JavaScript腳本獲取。
相關文章
相關標籤/搜索