【編者按】本文做者爲 Florian Heinemann 與 Robert Nyman。javascript
Florian 來自 MIT 系統設計與管理學院,專一於複雜的社交技術系統。此前曾在企業軟件領域的多家初創公司工做,以後加入 Airbus,擔任知識與創新管理經理一職。Robert 是 Mozilla Hacks 技術傳道師及編輯。曾就 HTML5,JavaScript 以及 Open Web 發表過屢次談話與博文。Robert 堅決地看好 HTML5 與 Open Web。自1995年就開始在 Front End 開發部門研究 Web 技術。php
本文系 OneAPM project師編譯呈現,下面爲正文。html
Passwordless(無密碼)是用於 Node.js 程序的一種身份驗證中間件。能提升用戶安全水平。同一時候具有部署簡單、高速的特色。java
過去幾個月。對熱衷於 Web 安全與保密性的人來講。着實激動人心:出現了不少了不得的文章、討論。還有不少事件,都在提升人們的安全意識。node
然而,大多數站點仍在使用最先期的 web 身份驗證方式:username和password。git
雖然username密碼這樣的身份驗證方式的確佔領了一席之地。但假設覺得這是所有項目的終極選擇,咱們便應該更加慎重了。咱們知道,大多數人在訪問站點時都使用同一套密碼。github
對於那些缺乏安全專家支持的 web 項目,假設用戶在該站點的密碼遭到泄露,那就可能傷及他的 Amazon 帳戶。咱們真的要讓用戶承擔這樣的風險麼?此外。這樣的經典的身份驗證機制至少存在兩種攻擊角度:登陸頁與密碼找回頁。而且。後者的實現每每在匆忙中進行。於是風險更高。web
近期。咱們看到了不少不錯的點子。mongodb
筆者尤爲對一個直觀而且低技術含量的解決方法感興趣:一次性密碼。這樣的方法部署高速。攻擊面小,而且不需要 QR codes 或 JavaScript。無論什麼時候,用戶想要登陸或使以前的會話失效,都可以經過電子郵件或短信息收到一個短期有效的一次性連接與令牌(token)。假設你想試一試,可以下載passwordless.net中的演示代碼。數據庫
不幸的是。因爲技術棧的區別。基本上不存在現成的解決方式。
所以,Passwordless 針對 Node.js 作出了一些修改。
Passwordless 入門很easy。兩個小時之內。你就能學會部署全面且安全的身份驗證解決方式:
$ npm install passwordless --save
獲取主要的框架。你還要安裝某個現成的存儲接口,比方 MongoStore,用於安全地存儲令牌。
$ npm install passwordless-mongostore --save
在傳送令牌給用戶時。電子郵件通常是最好的選擇(只是。短消息也可以)。
你可以隨意選擇郵件框架,比方:
$ npm install emailjs --save
首先,請求上文用到的所有模塊,將它們放在用於初始化 Express 的同一個文件裏:
var passwordless = require('passwordless');
var MongoStore = require('passwordless-mongostore');
var email = require("emailjs");
假設你選用 emailjs 進行令牌傳遞。此時應該與郵箱帳戶進行鏈接(比方:Gmail 帳戶):
var smtpServer = email.server.connect({
user: yourEmail,
password: yourPwd,
host: yourSmtp,
ssl: true
});
最後的一個預備步驟是告知 Passwordless 你選擇了哪種存儲接口。並將之初始化:
// Your MongoDB TokenStore
var pathToMongoDb = 'mongodb://localhost/passwordless-simple-mail';
passwordless.init(new MongoStore(pathToMongoDb));
函數 passwordless.addDelivery(deliver)
會加入新的傳送機制。每次需要傳送令牌時。就會調用deliver
。默認狀況下,你選擇的機制應該依照下面格式爲用戶提供連接:
http://www.example.com/token={TOKEN}&uid={UID}
deliver
在調用時。需要所有的細節信息。所以,令牌的傳遞(在本例中,使用的是 emailjs)例如如下所看到的。至關簡單:
passwordless.addDelivery(
function(tokenToSend, uidToSend, recipient, callback) {
var host = 'localhost:3000';
smtpServer.send({
text: 'Hello!nAccess your account here: http://'
+ host + '?token=' + tokenToSend + '&uid=' + encodeURIComponent(uidToSend), from: yourEmail, to: recipient, subject: 'Token for ' + host }, function(err, message) { if(err) { console.log(err); } callback(err); }); });
app.use(passwordless.sessionSupport());
app.use(passwordless.acceptToken({ successRedirect: '/'}));
函數 sessionSupport()
會使登陸狀態獲得保持,所以。用戶在瀏覽站點時才幹一直處於登陸狀態。請確保你已經提早準備好了會話中間件(比方express-session)。
函數 acceptToken()
會截獲不論什麼外來的令牌,驗證用戶身份,再將他們重定向至正確的頁面。
雖然 successRedirect
選項不是嚴格要求的。但筆者強烈建議你使用此選項,從而避免合法令牌經過站點向外的 HTTP 連接的 header 來源泄露出去。
下文默認你已經經過 var router = express.Router();
配置好路徑選擇器。
此外,express 文檔中也有對應的說明。
你至少需要兩個 URLs,從而:
- 展現用於獲取用戶郵箱地址的頁面
- 獲取表格細節(經過 POST 方法)
/* GET: login screen */
router.get('/login', function(req, res) {
res.render('login');
});</p>
/* POST: login details */
router.post('/sendtoken',
function(req, res, next) {
// TODO: Input validation
},
// Turn the email address into a user ID
passwordless.requestToken(
function(user, delivery, callback) {
// E.g. if you have a User model:
User.findUser(email, function(error, user) {
if(error) {
callback(error.toString());
} else if(user) {
// return the user ID to Passwordless
callback(null, user.id);
} else {
// If the user couldn’t be found: Create it!
// You can also implement a dedicated route
// to e.g. capture more user details
User.createUser(email, '', '',
function(error, user) {
if(error) {
callback(error.toString());
} else {
callback(null, user.id);
}
})
}
})
}),
function(req, res) {
// Success! Tell your users that their token is on its way
res.render('sent');
});
此處有何貓膩?passwordless.requestToken(getUserId)
的任務有二:第1、確保郵箱地址真實存在。第2、將該地址轉變爲獨一無二的用戶 ID,經過郵件一併發送,並在以後用於驗證用戶身份。一般,你都有一套存儲用戶細節信息的模型,你可以依照上例的說明。進行簡單的設置。
在一些狀況下(好比。由兩個用戶編輯過的博客)。你可以跳過用戶模型,將他們有效的郵箱地址與其各自的 ID 相聯繫:
var users = [
{ id: 1, email: 'marc@example.com' },
{ id: 2, email: 'alice@example.com' }
];
/* POST: login details */
router.post('/sendtoken',
passwordless.requestToken(
function(user, delivery, callback) {
for (var i = users.length - 1; i >= 0; i--) {
if(users[i].email === user.toLowerCase()) {
return callback(null, users[i].id);
}
}
callback(null, null);
}),
// Same as above…
本例僅僅需要一個簡單的 HTML 頁面,用以獲取用戶的郵箱地址。
默認狀況下,Passwordless 會查找 user
輸入欄中的內容:
<html>
<body>
<h1>Login</h1>
<form action="/sendtoken" method="POST">
Email:
<br /><input name="user" type="text">
<br /><input type="submit" value="Login">
</form>
</body>
</html>
Passwordless 提供了能確保僅僅有驗證用戶才幹看到指定頁面的中間件:
/* Protect a single page */
router.get('/restricted', passwordless.restricted(),
function(req, res) {
// render the secret page
});
/* Protect a path with all its children */
router.use('/admin', passwordless.restricted());
默認狀況下,Passwordless 贊成經過請求對象 req.user
獲取用戶 ID。想要展現或重用此 ID,或從數據庫中獲得不少其它細節信息,你可以這麼實現:
router.get('/admin', passwordless.restricted(),
function(req, res) {
res.render('admin', { user: req.user });
});
或者。更通常化地,你可以加入還有一箇中間件,從模型中抽取出某個用戶的所有記錄,再將其分配給站點中的隨意路徑:
app.use(function(req, res, next) {
if(req.user) {
User.findById(req.user, function(error, user) {
res.locals.user = user;
next();
});
} else {
next();
}
})
以上就是安全地驗證用戶身份的簡單方法。
若想了解不少其它細節,你可以查看深刻剖析,從而瞭解所有的可選項。以及將上述知識整合爲一套可行的解決方式的案例。
如前所看到的。所有的身份驗證系統都有其優缺點。你應該依照本身的需求進行合理的選擇。基於令牌的驗證方式,與絕大多數解決方法(包含經典的username/密碼方法)同樣。都存在一個風險:假設用戶的郵箱帳戶被盜用。而且/或者 SMTP 服務器與用戶的鏈接被入侵,用戶在站點的帳戶就會隨之遭到盜用。默認狀況下。有兩種辦法可以減弱該風險(但不是全然避免):短期有效的令牌以及令牌在使用後本身主動失效。
對大多數站點而言,基於令牌的身份驗證方法表明着走向安全的進步:用戶無需再想新的密碼(這些密碼每每很easy)。也不存在用戶重用密碼的風險。
對於身爲開發人員的咱們。Passwordless 提供了惟一一種(且至關簡單的)身份驗證解決方式,該方案易於理解。所以easy保護。此外,咱們也無需再處理用戶的密碼了。
還有一個值得討論的點是可用性。咱們應該同一時候考慮兩種狀況:用戶在站點的首次使用以及興許的登陸。對首次用戶而言。基於令牌的身份驗證很直觀:與經典的登陸機制同樣,他們仍是不得不驗證郵箱地址。
但是,在最佳案例中,除此以外就不需要其它細節信息了。不需要再煞費苦心地想一個符合所有限制條件的密碼,也不需要刻意記住密碼。假設用戶再次登陸,其體驗與特定的用戶案例有關。大多數站點的會話有效期都很長,於是不需要再次登陸。
或者,用戶訪問該站點的頻率事實上很低,以至於他們想不起來本身是否已經擁有帳戶,或者忘記了帳戶密碼。在這樣的狀況下。Passwordless 在可用性方面的優點至關明顯。相同地,這需要經歷很少的幾個步驟,解釋起來也很easy。然而,那些用戶訪問頻繁的站點。以及/或那些要求用戶一週內手動登陸幾回的站點(比方 Amazon),經典的身份驗證(或更保險地:雙重驗證)也許更加適合。因爲用戶極可能會真的記住他們的密碼,而且意識到好密碼的重要性。
雖然 Passwordless 眼下比較穩定,筆者仍是很期待你能在 GitHub 留下評論或一些貢獻,或者在 Twitter:@thesumofall 上提問筆者。
原文地址:https://hacks.mozilla.org/2014/10/passwordless-authentication-secure-simple-and-fast-to-deploy/
OneAPM 助您輕鬆鎖定 Node.js 應用性能瓶頸,經過強大的 Trace 記錄逐層分析,直至鎖定行級問題代碼。以用戶角度展現系統響應速度。以地域和瀏覽器維度統計用戶使用狀況。
想閱讀不少其它技術文章。請訪問 OneAPM 官方博客。
本文轉自 OneAPM 官方博客