前言
做了很久的實驗,一直沒能成功,最後還是down的大佬的源碼,侵删。
正文
簡介
MQTT協定是物聯網應用中重要的應用層協定,上一次實驗開展了MQTT協定的分析,對MQTT協定的長連接配接機制、釋出/訂閱工作模式互動機制進行了分析。但如果MQTT協定不進行安全實作,黑客可以惡意釋出資訊給伺服器,特别是在工業、交通等物聯網應用場合後果不堪設想。本實驗旨建構MQTT協定安全通信。
要求
(1)掌握MQTT的安全通信的基本原理。
(2)設計MQTT安全通信的安全措施。
(3)實作基于TLS的MQTT通信。
基本步驟
(1)應用層:MQTT協定提供了客戶辨別以及使用者名密碼,以便驗證裝置。基于ACL對主題的訂閱和釋出權限設定基本的授權。
(2)傳輸層:傳輸層可使用TLS加密、驗證證書機制,防止中間人攻擊。
(3)網絡層:可以通過專線或者使用VPN來連接配接裝置與MQTT代理,以提高網絡傳輸的安全性。可以通過防火牆保護MQTT代理端,比如限制端口、限制協定、限制IP段。
注意建構一個區域網路,模拟MQTT服務端(代理端)、用戶端(分訂閱和釋出),運用Wireshark抓包分析安全通信過程。
具體過程
伺服器
var mosca = require('mosca')
var SECURE_KEY = __dirname + '/tls-key.pem';
var SECURE_CERT = __dirname + '/tls-cert.pem';
var settings = {
logger: {
name: "secureExample",
level: 40,
},
secure : {
port: 9090,
keyPath: SECURE_KEY,
certPath: SECURE_CERT,
}
};
var server = new mosca.Server(settings);
server.on('published', function(packet, client) {
//當用戶端有連接配接釋出主題消息
var topic = packet.topic;
console.log(packet);
switch (topic) {
case 'test':
console.log('收到消息:', packet.payload.toString());
//MQTT轉發主題消息
//MqttServer.publish({ topic: 'other', payload: 'sssss' });
break;
case 'other':
console.log('message-123', packet.payload.toString());
break;
}
});
server.on('ready', setup);
//若使用者名與密碼有效,接受連結
var authenticate = function(client, username, password, callback) {
var authorized = (username === 'alice' && password.toString() === 'secret');
if (authorized) {
client.user = username;
console.log('通過認證登入的使用者:', username);
}
callback(null, authorized);
}
// 授權為alice的用戶端可以釋出/users/alice,
// 從主題中取得使用者名,并校驗與授權使用者一緻
var authorizePublish = function(client, topic, payload, callback) {
callback(null, client.user == topic);
}
// 授權為alice的用戶端可以訂閱/users/alice,
// 從主題中取得使用者名,并校驗與授權使用者一緻
var authorizeSubscribe = function(client, topic, callback) {
callback(null, client.user == topic);
}
//mosca配置
function setup() {
console.log('Mosca secure server is up and running')
server.authenticate = authenticate;
server.authorizePublish = authorizePublish;
server.authorizeSubscribe = authorizeSubscribe;
}
訂閱方**
var mqtt = require('mqtt');
var fs = require('fs')
var path = require('path')
var KEY = fs.readFileSync(path.join(__dirname, '/tls-key.pem'))
var CERT = fs.readFileSync(path.join(__dirname, '/tls-cert.pem'))
var TRUSTED_CA_LIST = fs.readFileSync(path.join(__dirname, '/crt.ca.cg.pem'))
var PORT = 9090
var HOST = "127.0.0.1"
var options = {
port: PORT,
host: HOST,
key: KEY,
cert: CERT,
rejectUnauthorized: false,
ca: TRUSTED_CA_LIST,
protocol: 'mqtts',
//訂閱者實作認證的參數
username:"alice",
password:"secret"
}
var client2 = mqtt.connect(options)
client2.subscribe('alice',{qos:0});//訂閱主題為alice的消息
client2.on('message',function(top,message) {
console.log('收到消息:', message.toString());
});
釋出方**
var mqtt = require('mqtt');
var fs = require('fs')
var path = require('path')
var KEY = fs.readFileSync(path.join(__dirname, '/tls-key.pem'))
var CERT = fs.readFileSync(path.join(__dirname, '/tls-cert.pem'))
var TRUSTED_CA_LIST = fs.readFileSync(path.join(__dirname, '/crt.ca.cg.pem'))
var PORT = 9090
var HOST = "127.0.0.1"
var options = {
port: PORT,
host: HOST,
key: KEY,
cert: CERT,
rejectUnauthorized: false,
ca: TRUSTED_CA_LIST,
protocol: 'mqtts',
//釋出者實作認證的參數
username:"alice",
password:"secret"
}
var client = mqtt.connect(options)
var num = 0;
var qtt = {}; //定義消息
qtt = 'hello world';
//一秒鐘發送一次消息setr=xxxxxxx1xx
setInterval(function() {
client.publish('alice', qtt, { qos: 0, retain: true });
}, 10000);
抓包分析
1 用戶端送出請求(ClientHello)
(1) 用戶端與服務端通過tcp三次握手建立tcp連接配接後,用戶端首先向伺服器發出建立加密通信的請求,發送ClientHello請求,從消息體結構看,tls/ssl是基于tcp連接配接之上,應用層之下的協定。
2 伺服器響應(SeverHello)
伺服器收到用戶端請求後,向用戶端發出響應,叫做Sever Hello。
從消息體中,可以看到伺服器的響應包含以下内容:
(1) 确認使用的加密通信協定版本,這裡确認使用tls1.2,而不是client hello中的tls1.1。響應握手協定消息 server hello。如果浏覽器與伺服器支援的版本不一緻,伺服器關閉加密通信。
(2) 一個伺服器生成的随機數,稍後用于生成"對話密鑰"。
(3) 确認使用的加密套件,這裡為rsa+aes128+sha256
(4) 壓縮方法為空。
(5) 一些列擴充資訊
3 服務端發送服務端電子證書(CA),密鑰交換(server key exchange),及server hello done三個握手消息
用戶端接收到server hello握手消息後,及時回報ack消息。服務端接收用戶端ack消息後,發送服務端電子證書,密鑰交換,及server hello done三個握手消息
從封裝内容看,包含兩層ssl協定體資訊,頭一個為服務端證書,後面跟着公共密鑰交換和hello done消息體,具體如下:
(1)詳細的電子證書資訊和CA認證機構資訊
(2)密鑰交換資訊,包括DH算法計算出的pubkey公鑰,電子簽名的hash算法值
(3)server hello done消息體
4 用戶端發送密鑰交換資訊(client key exchange)、編碼改變協定消息(change cipher spec)
用戶端發送ack消息給服務端,确認收到server hello done消息,然後發送用戶端的密鑰交換資訊和修改密鑰的協定消息
主要内容如下:
(1) 發送DH算法計算的pubkey,用于服務端計算生成解密私鑰
(2) 發送編碼改變通知,表示随後的資訊都将用雙方商定的加密方法和密鑰發送。
(3) 發送加密後的握手消息,一個随機數。該随機數用伺服器公鑰加密,防止被竊聽
(4) 用戶端握手結束通知,表示用戶端的握手階段已經結束。這一項同時也是前面發送的所有内容的hash值,用來供伺服器校驗。(可能在加密消息中,未确認)
用戶端收到伺服器所有響應消息後,首先驗證伺服器證書。如果證書不是可信機構頒布、或者證書中的域名與實際域名不一緻、或者證書已經過期,就會向通路者顯示一個警告,由其選擇是否還要繼續通信。
如果證書沒有問題,用戶端就會從證書中取出伺服器的公鑰,即server key exchange消息中攜帶的pubkey值。然後,根據根據已經收到的三個随機數計算書加密密鑰,對握手資訊進行加密通信,然後向伺服器發送上面抓包中三項資訊内容。
至此,整個握手階段全部結束。接下來,用戶端與伺服器進入加密通信,就完全是使用普通的HTTP協定,隻不過用”會話密鑰”加密資料内容。
五 實驗體會(不少于300字)
TLS(Transport Layer Security,安全傳輸層),TLS是建立在傳輸層TCP協定之上的協定,服務于應用層,它的前身是SSL(Secure Socket Layer,安全套接字層),它實作了将應用層的封包進行加密後再交由TCP進行傳輸的功能。
TLS協定主要解決如下三個網絡安全問題。
1、保密(message privacy),保密通過加密encryption實作,所有資訊都加密傳輸,第三方無法嗅探;
2、完整性(message integrity),通過MAC校驗機制,一旦被篡改,通信雙方會立刻發現;
3、認證(mutual authentication),雙方認證,雙方都可以配備證書,防止身份被冒充;
下面是TLS的握手過程。詳細步驟上面也說過了。
Client1:TLS版本号 + 所支援加密套件清單 + 希望使用的TLS選項
Server1:選擇一個用戶端的加密套件 + 自己的公鑰 + 自己的證書 + 希望使用的TLS選項 +(要求用戶端證書)
Client2:(自己的證書) + 使用伺服器公鑰和協商的加密套件加密一個對稱密鑰
Server2:使用私鑰解密出對稱密鑰後,發送的加密的Finish消息,表明完成握手
任何事物都是有利有弊,引入TLS機制固然是能夠保證安全,但卻在TCP握手和HTTP通信之間,多加了兩個往返的TLS握手過程。
安全問題:
可能存在中間人攻擊。