0x00 express框架簡要介紹
Express是一個簡潔而靈活的node.js Web應用框架,提供了一系列強大特性幫助創建各種Web應用,和豐富的 HTTP 工具。使用 Express 可以快速地搭建一個完整功能的網站。
Express 框架核心特性:
- 可以設置中間件來響應 HTTP 請求。
- 定義了路由表用於執行不同的 HTTP 請求動作。
- 可以通過向模板傳遞參數來動態渲染 HTML 頁面。
安裝與工程創建這裏不在敘述。下面直接給出例子:
1、新建appname.js文件
const express = require('express')//1、引入express模塊
const app = express()//2、生成express實例
app.get('/hello', function (req, res) { //3、第一個參數爲URL 第二個爲回調函數中間件
res.send('Hello World')
})
app.listen(3000) //4、服務器端監聽3000端口
然後運行上述腳本:
$ node appname.js
服務器端顯示:
現在就可以訪問http://localhost:3000/hello瀏覽器顯示“Hello World”。
上述同樣的功能,若不使用express框架,使用nodejs搭建,複雜度飆升,尤其路由這一塊。Express方便了快速搭建服務的過程。
Express框架的主要文件如下所示:
您可以看到就是對nodejs中相關功能的封裝,使得express框架搭建服務器更簡單。這些js代碼中也沒有發現加入過濾和白名單等措施。
0x01 Express框架安全研究
一、XSS
1、XSS漏洞驗證
修改服務器端js代碼如下所示:
var express = require('express');
app = express()
app.get('/xss',function(req,res) {
res.send(req.query.q);
})
var server = app.listen(50000)
該代碼獲取Get請求中的參數名稱爲q的值,並把該值作爲響應內容發送給客戶端。
在瀏覽器中正常輸入http://localhost:50000/xss?q=1,則顯示正常:
爲了驗證express框架是否含有XSS防護功能,利用如下URL進行驗證:
http://localhost:50000/xss?q=<script>alert(1)</script>
瀏覽器出現彈框。所以express本身沒有xss防護機制 ,若是未經過濾直接顯示外部的輸入則導致XSS。
2、XSS防護方法
a、使用安全頭X-XSS-Protection
爲開啓瀏覽器XSS防護,服務器端響應代碼如下所示:
var express = require('express');
app = express()
app.get('/xss',function(req,res) {
res.header("X-XSS-Protection", "1; mode=block"); //X-XSS-Protection 響應頭是Internet Explorer,Chrome和Safari的一個功能,當檢測到跨站腳本攻擊 (XSS)時,瀏覽器將停止加載頁面
res.send(req.query.q);
})
var server = app.listen(50000)
相同方法運行後,瀏覽器阻止了頁面的加載,如下圖所示:
b、使用helnet第三方模塊
使用helnet模塊會自動設置好一些安全header,包括1中的header,未使用前響應頭如下:
使用helnet模塊:
var express = require('express');
var helmet = require('helmet');//1
app = express()
app.use(helmet());//2
app.get('/xss',function(req,res) {
res.send(req.query.q);
})
var server = app.listen(50000)
響應頭多處如下幾個:
包含了X-XSS-Protection,起到了開啓瀏覽器防XSS攻擊的功能。
c、使用數據前必須進行黑白名單校驗
二、RCE遠程代碼執行
在Express框架中使用child_process模塊調用操作系統的shell執行響應的命令,它是一個shell解釋器,可執行系統命令,若其直接接受外部參數,則可能造成RCE漏洞。例如下面的代碼:
var express = require('express');
var child_process = require('child_process');
var app = express();
app.get('/rce', function (req, res) {
console.log("code exec");
child_process.exec(req.query.q);
})
var server = app.listen(50000, function () {
console.log("服務已經啓動");
})
代碼獲取url中的q參數,並使用child_process.exec調用執行。根目錄下創建文件1.txt。然後瀏覽器中訪問如下URL:
http://localhost:50000/rce?q=del+1.txt
在訪問該URL後,服務器中的1.txt被刪除。用戶輸入在服務器中被直接執行了。所以使用該模塊執行用戶可控的命令是必須通過嚴格的校驗限制用戶可操作的類型。
三、SSRF
Express框架寫如下js代碼
var express = require('express');
var needle = require('needle');
app = express()
app.get('/ssrf',function(req,res){
var url=req.query['url'];
needle.get(url);
console.log('new request:'+url);
})
var server = app.listen(50000)
打開Tomcat服務器,運行的網頁URL爲:http://localhost:8080/
瀏覽器中訪問如下URL:
http://localhost:50000/ssrf?url=http://localhost:8080/
然後到Tomcat服務器日誌中localhost_access_log.2019-09-19.txt發現被訪問的記錄:
ssrf漏洞在存在於大多數的編程語言中,node.js也不例外,只要web系統接收了外界輸入的URL,並且通過服務端程序直接調用就會造成相應的漏洞。所以服務器必須進行白名單校驗。
四、帶有敏感數據的請求使用post方法處理
帶有敏感數據的表單必須使用 HTTP-POST 方法提交。
通過HTTP-POST提交帶有敏感數據的表單可以有效阻止跨站請求僞造攻擊,並且可以避免帶有敏感數據的URL被記錄於服務端日誌、代理日誌、瀏覽器歷史而造成的信息泄露。
所以需要將之前的Get請求更改爲Post請求,爲了實現Post請求這裏重新設計代碼:
login界面
登錄校驗js
var express = require('express');
app = express()
app.get('/',function(req,res) {
res.sendFile('D:/project/express_WorkSpace/login.html')
})
app.post('/login',function(req,res) { //接受post請求
res.send('hello');
})
var server = app.listen(50000)
瀏覽器直接輸入:http://localhost:50000/login
因爲需要通過表單的post請求才有效。所以先進入首頁:
點擊login後同時IE抓包:
此時該URL請求成功並且爲POST請求
五、模板引擎
1、模板引擎漏洞驗證
模板引擎(Template Engine), 是用來解析對應類型模板文件然後動態生成由數據和靜態頁面組成的視圖文件的一個工具。 它通過標籤(tag)來響應各種解析動作,通過變量佔位的方式動態的將對應數據展示到指定位置。
Express中模板引擎以Mustache爲例,驗證在Express框架是否對使用的模板表達式做了安全措施。
a、安裝mustache組件
npm install mustache --save
npm install mustache-express --save
b、建立含有mustache表達式的html頁面。工程根目錄下新建views目錄,在該目錄中新建一個html文件(例子名爲profile.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{title}}</title>
</head>
<body>
<h1>人物介紹</h1>
{{#personInfoList}}name:{{{name}}} age:{{{age}}}<br />{{/personInfoList}}
</body>
</html>
在使用模板表達式時使用了{{{name}}}的形式,我們知道,該種形式會解讀html標籤、執行script腳本,若該數據外部可控且未經校驗過濾有嚴重的XSS漏洞。
c、新建js文件
var express = require('express');
var mustacheExpress = require('mustache-express');//引入mustache模塊
app = express()
app.engine("mustache", mustacheExpress());//註冊mustache引擎
// 配置模板引擎
app.engine('html', mustacheExpress({
layoutsDir: 'views',
defaultLayout: 'layout',
extname: '.html'
}));
app.set('view engine', 'html');
// 匹配根路由 /
app.get('/', function(req, res) {
res.render('profile', {
layout: false,
title: "homePage",
personInfoList: [{
name: "zhangsang<script>alert(1)</script>",
age: 20
}, {
name: "lisi",
age: 20
}]
});
});
var server = app.listen(50000)
d、運行js文件,並在瀏覽器中訪問
點擊確定後,模板渲染成功,但是執行了惡意腳本。
如上試驗,express在使用模板引擎時僅僅是對接了該功能,而沒有任何安全措施。其他模板表達式依然如此,都需要注意。
六、不使用有漏洞版本的express
舊版本存在漏洞。例如存在如下CVE漏洞:
|
The Express web framework before 3.11 and 4.x before 4.5 for Node.js does not provide a charset field in HTTP Content-Type headers in 400 level responses, which might allow remote attackers to conduct cross-site scripting (XSS) attacks via characters in a non-standard encoding. |
在3.11之前的版本Express web framework和4.5之前的4.x的 Node.js版本存在XSS漏洞。在顯示400響應消息時,易受攻擊的express版本不會在header中的Content-Type字段指定charset,這使得攻擊者通過一個非標準編碼的字符集去執行XSS攻擊。
同時因爲nodejs的漏洞引出express的漏洞也很多,以下列舉了在指定版本更新中修復的 Express 漏洞。
http://expressjs.com/en/advanced/security-updates.html
官方推薦express版本的描述如下:
https://expressjs.com/en/advanced/best-practice-security.html
七、掩蓋express的使用
1、禁用X-Powered-By以防止泄露express的使用
Express默認情況下啓用了X-Powered-By響應頭,攻擊者可能會使用該頭來檢測運行Express的應用程序,然後發動針對特定目標的攻擊。所以需要使用 app.disable()方法來禁用此頭。沒禁用前響應header:
在上節中服務器js代碼修改如下:
var express = require('express');
app = express();
app.disable('x-powered-by');//禁用x-powered-by
app.get('/',function(req,res) {
res.sendFile('D:/project/express_WorkSpace/login.html');
})
app.post('/login',function(req,res) {
res.send('hello');
})
var server = app.listen(50000);
運行後響應頭中的X-Powered-By消失,效果達到。
其實前文使用helmet模塊後,也會禁用該頭,在前文可看出確實該header沒有出現在response中。
2、Cookie SessionID默認名稱修改爲通用名稱
Cookie中存儲的session id的key值express的環境下,默認值爲connect.sid,這樣就暴露了服務器端使用了express框架。例如下述代碼:
var express = require('express');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.use(session({
secret: '12345',
//name: 'sessionid', //這裏的name值得是cookie的name,默認cookie的name是:connect.sid
cookie: {
secure: true,
httpOnly: true,
domain: 'localhost',
path: '/',
expires: expiryDate
}
resave: false,
saveUninitialized: true,
}));
app.get('/', function(req, res){
res.send("helloworld!");
});
app.listen(10000);
瀏覽器訪問後可發現sessionid的key:
所以使用cookie傳送sessionID的時候需要重新設置:
app.use(session({
secret: '12345',
name: 'sessionid',
cookie: {maxAge: 80000 ,httponly:true},
resave: false,
saveUninitialized: true,
}));
除此之外,對於cookie的設置一些通用的web應用規範需要遵守,此處簡單提下:
1、會話cookie的屬性要設置爲HttpOnly
2、爲包含會話標識的cookie設置適當限制的domain和path屬性值
3、 爲cookie設置secure標誌
4、 禁止在 cookie 中以明文形式存儲敏感數據
八、其它
Express是對nodejs框架的一個封裝,使得構建服務器應用程序更容易。Nodejs中使用的方法,express依然可以使用,所以nodejs中存在的安全問題自然也會繼承,本文是express專題,就不在贅述nodejs繼承而來的其它安全問題。
0x02 參考文獻
express用法:https://blog.csdn.net/weixin_41646716/article/details/82460761
進階:https://www.jianshu.com/p/9a30447c32df
express框架原理:https://zhuanlan.zhihu.com/p/56947560
express安全研究:https://www.freebuf.com/articles/web/152891.html
npm常用模塊功能:http://caibaojian.com/nmp-modules.html
express-session:https://segmentfault.com/a/1190000010306099
默認session名稱:https://www.cnblogs.com/chenchenluo/p/4197181.html
express漏洞版本:http://expressjs.com/en/advanced/security-updates.html