传统Modbus TCP协议运行在明文之上,ない任何加密和认证机制,在工业互联网时代面临严重的セキュリティ风险。Modbus/TCP Security(又称Modbus Security Protocol)経由TLS加密和X.509证书认证,为Modbus通信提供了工业级セキュリティ保障。本記事详细介绍Modbus TLSセキュリティプロトコル的规范原理、证书生成、pymodbus/Go等多言語実装,以及遗留デバイス的セキュリティアップグレード方案。
一、なぜか。Modbus需要セキュリティ
1.1 传统Modbus TCP的セキュリティ缺陷
- 明文传输:すべてのデータ(包括控制指令)以明文在网络上传输,可被任意抓包読み取り和篡改
- 无认证:任何能连接到502ポート的デバイス都可以読み取り/書き込みレジスタ,无需身份验证
- 无認証:ない权限控制,任何マスター都可以执行任何機能コード
- 无完全性チェック:TCP层チェック和容易被绕过,中间人可以修改データ内容
- 无抗抵赖:无法追溯是谁送信了控制指令
1.2 真实セキュリティ风险
- データ窃取:攻击者可窃取生产データ、能耗データ、工艺参数
- デバイス控制:攻击者可送信写レジスタ指令,改变デバイス运行参数,甚至停机
- 中间人攻击:篡改センサーは读数,导致控制系统做出エラー决策
- 拒绝服务:大量连接耗尽デバイスリソース,导致正常通信中断
- 勒索软件:経由Modbus漏洞入侵工业网络,加密控制系统
二、Modbus/TCP Security规范详解
2.1 规范概述
Modbus/TCP Security(官方名称"Modbus TCP Security Protocol",简称MBAPS)由Modbus Organization于2018年发布,中国对应標準为GB/T 41868-2022。它在保持Modbusアプリケーション層プロトコル不变的前提下,在传输层引入TLS加密和X.509证书认证。
| characteristic | Modbus TCP | Modbus TCP Security |
|---|---|---|
| ポート | 502 | 802(IANARegister) |
| 传输层 | 纯TCP | TLS 1.2+ over TCP |
| 加密 | 无 | AES-128/256-GCM |
| 认证 | 无 | X.509v3双向证书认证 |
| 認証 | 无 | 基于角色的アクセス控制(RBAC) |
| 完全性 | TCPchecksum | TLS MAC(HMAC-SHA256) |
| ADUformat | MBAP + PDU | MBAP + PDU(不变) |
| 协议标识 | mbap | mbaps |
2.2 协议架构
┌─────────────────────────────────────────┐
│ Modbus Application Layer │
│ (function code/register/data - 完全不变) │
├─────────────────────────────────────────┤
│ MBAP Header (7バイト) │
│ トランザクションID | ProtocolID=0 | Length | ユニットID │
├─────────────────────────────────────────┤
│ TLS Record Layer │
│ 加密 + 认证 + 完全性保护 │
├─────────────────────────────────────────┤
│ TCP (ポート802) │
├─────────────────────────────────────────┤
│ IP / Ethernet │
└─────────────────────────────────────────┘
关键设计:Modbusアプリケーション層PDU完全不变,
只是在TCP之上增加了TLS层,
因此现有Modbus程序只需替换传输层即可アップグレード。
2.3 X.509证书认证
Modbus TLS使用双向证书认证(mTLS):
- サーバー·サーバー证书:Modbusslave/サーバー·サーバー持有,クライアント側は验证サーバー·サーバー身份
- クライアント側は证书:Modbusmaster/クライアント側は持有,サーバー·サーバー验证クライアント側は身份
- CA证书:签发クライアント側は和サーバー·サーバー证书的根证书,双方都需信任
- 证书字段:Common Name(CN)用于标识デバイス,Organization(O)用于标识厂商
- 证书扩展:可包含角色信息(Role),用于RBAC認証
2.4 基于角色的アクセス控制(RBAC)
Modbus Security规范定义了四种角色,経由证书中的角色扩展字段控制アクセス権:
| Role | 权限 | 説明 |
|---|---|---|
| Monitor | 読み取り専用(function code01/02/03/04) | データの収集和モニタリング,不能修改任何值 |
| Operator | 读+写コイル/register | 可执行常规操作,如启停デバイス |
| Maintainer | 读+写+诊断 | 可执行维护操作,如校准、复位 |
| Administrator | すべて权限 | 可修改設定、管理证书、更新固件 |
三、生成TLS证书
使用OpenSSL生成CA证书、サーバー·サーバー证书和クライアント側は证书。
#!/bin/bash
# generate_certs.sh - 生成Modbus TLS证书
mkdir -p certs
cd certs
# ====== 1. 生成CA根证书 ======
echo "生成CA根证书..."
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=ModbusCA/OU=IT/CN=Modbus Root CA"
# ====== 2. 生成サーバー·サーバー证书(Modbusslave) ======
echo "生成サーバー·サーバー证书..."
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=Industrial/CN=modbus-server.local"
# 用CA签发サーバー·サーバー证书
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt
# ====== 3. 生成クライアント側は证书(Modbusmaster) ======
echo "生成クライアント側は证书..."
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=Engineering/CN=modbus-client-001"
# 用CA签发クライアント側は证书
openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt
# ====== 4. 验证证书 ======
echo "验证证书..."
openssl verify -CAfile ca.crt server.crt
openssl verify -CAfile ca.crt client.crt
echo ""
echo "证书生成完成:"
ls -la *.crt *.key
# 证书文件説明:
# ca.crt - CA根证书(クライアント側は和サーバー·サーバー都需要)
# server.crt - サーバー·サーバー证书(部署在Modbusslave)
# server.key - サーバー·サーバー私钥(部署在Modbusslave,保密!)
# client.crt - クライアント側は证书(部署在Modbusmaster)
# client.key - クライアント側は私钥(部署在Modbusmaster,保密!)
# 运行证书生成脚本
chmod +x generate_certs.sh
./generate_certs.sh
# 表示证书内容
openssl x509 -in server.crt -text -noout | head -20
# 测试证书一致するかどうか私钥
openssl x509 -noout -modulus -in server.crt | md5sum
openssl rsa -noout -modulus -in server.key | md5sum
# 2つMD5值必须相同
四、pymodbus TLSクライアント側は実装
# tls_client.py - pymodbus TLSクライアント側は
from pymodbus.client import ModbusTlsClient
# 作成TLSクライアント側は
# デフォルトポート802,framer=TLS
client = ModbusTlsClient(
host="127.0.0.1",
port=802, # Modbus TLS標準ポート
certfile="certs/client.crt", # クライアント側は证书
keyfile="certs/client.key", # クライアント側は私钥
password=None, # 私钥パスワード(もし有)
# 也可以直接传入sslctx
# sslctx=ssl_context,
timeout=5,
)
# connect(TLS握手会自动进行证书验证)
connection = client.connect()
if not connection:
print("TLS接続の失敗!")
exit(1)
print("TLS接続成功です。,证书验证経由")
# Read and hold register(アプリケーション層与普通TCP完全一样)
result = client.read_holding_registers(
address=0, count=10, device_id=1
)
if not result.isError():
print("レジスタを保持する。:", result.registers)
else:
print("Read failed:", result)
# write single register
client.write_register(address=100, value=1234, device_id=1)
print("書き込み済み。レジスタ100=1234")
# リードコイルを読む
result = client.read_coils(address=0, count=8, device_id=1)
if not result.isError():
print("Coil status:", result.bits[:8])
# 接続を閉じる
client.close()
print("连接已关闭")
4.1 CustomizationSSLContextより
# tls_client_custom.py - CustomizationSSLContextより(更セキュリティ的設定)
import ssl
from pymodbus.client import ModbusTlsClient
# 作成SSLContextより
sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
# 强制TLS 1.2以上(Modbus Security规范要求)
sslctx.minimum_version = ssl.TLSVersion.TLSv1_2
# loadCA证书(验证サーバー·サーバー证书)
sslctx.load_verify_locations(cafile="certs/ca.crt")
# 加载クライアント側は证书和私钥(双方向認証)
sslctx.load_cert_chain(
certfile="certs/client.crt",
keyfile="certs/client.key",
)
# 强制验证サーバー·サーバー证书(重要!防止中间人攻击)
sslctx.verify_mode = ssl.CERT_REQUIRED
# 設定加密套件(只允许强加密)
sslctx.set_ciphers("ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384")
# 作成クライアント側は
client = ModbusTlsClient(
host="modbus-server.local",
port=802,
sslctx=sslctx,
timeout=5,
)
client.connect()
result = client.read_holding_registers(0, 10, device_id=1)
print(result.registers if not result.isError() else result)
client.close()
五、pymodbus TLSサーバー側。実装
# tls_server.py - pymodbus TLSサーバー側。(セキュリティ的Modbusslave)
from pymodbus.server import StartTlsServer
from pymodbus.datastore import (
ModbusServerContext,
ModbusSlaveContext,
ModbusSequentialDataBlock,
)
# 作成データストア
holding_registers = ModbusSequentialDataBlock(0, [0] * 100)
coils = ModbusSequentialDataBlock(0, [False] * 100)
discrete_inputs = ModbusSequentialDataBlock(0, [False] * 100)
input_registers = ModbusSequentialDataBlock(0, [0] * 100)
# 预设データ
holding_registers.setValues(0, [250, 652, 1013]) # temperature/湿度/气压
slave_context = ModbusSlaveContext(
di=discrete_inputs,
co=coils,
hr=holding_registers,
ir=input_registers,
unit_id=1,
)
server_context = ModbusServerContext(slaves=slave_context, single=True)
print("Modbus TLSセキュリティサーバー·サーバー启动")
print("ポート: 802")
print("Protocol: TLS 1.2+")
print("认证: X.509双向证书")
print("按 Ctrl+C Stop")
# 启动TLSServer
StartTlsServer(
context=server_context,
address=("0.0.0.0", 802), # 標準TLSポート
certfile="certs/server.crt", # サーバー·サーバー证书
keyfile="certs/server.key", # サーバー·サーバー私钥
# 可选:要求クライアント側は证书(双方向認証)
# sslctx可以自定义,要求验证クライアント側は证书
)
# 注意:StartTlsServerデフォルト可能不验证クライアント側は证书
# 生产环境应自定义sslctx,設定verify_mode=CERT_REQUIRED
5.1 带クライアント側は证书验证的TLSサーバー側。
# tls_server_secure.py - 强制双方向認証的TLSサーバー側。
import ssl
from pymodbus.server import ModbusTlsServer
from pymodbus.datastore import (
ModbusServerContext, ModbusSlaveContext, ModbusSequentialDataBlock
)
# 作成SSLContextより(サーバー側。)
sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sslctx.minimum_version = ssl.TLSVersion.TLSv1_2
# 加载サーバー·サーバー证书和私钥
sslctx.load_cert_chain(
certfile="certs/server.crt",
keyfile="certs/server.key",
)
# loadCA证书,用于验证クライアント側は证书
sslctx.load_verify_locations(cafile="certs/ca.crt")
# 强制验证クライアント側は证书(关键!)
sslctx.verify_mode = ssl.CERT_REQUIRED
# 作成データストア
hr = ModbusSequentialDataBlock(0, [0] * 100)
co = ModbusSequentialDataBlock(0, [False] * 100)
slave = ModbusSlaveContext(di=co, co=co, hr=hr, ir=hr, unit_id=1)
context = ModbusServerContext(slaves=slave, single=True)
# 作成并启动TLSServer
server = ModbusTlsServer(
context=context,
address=("0.0.0.0", 802),
sslctx=sslctx,
)
print("セキュリティModbus TLSサーバー·サーバー已启动(强制双方向認証)")
import asyncio
asyncio.run(server.serve_forever())
六、Go语言Modbus TLS実装
// modbus_tls_client.go - Go语言Modbus TLSクライアント側は
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"github.com/grid-x/modbus"
)
func main() {
// loadCA证书
caCert, err := os.ReadFile("certs/ca.crt")
if err != nil {
panic(err)
}
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caCert)
// 加载クライアント側は证书和私钥
clientCert, err := tls.LoadX509KeyPair("certs/client.crt", "certs/client.key")
if err != nil {
panic(err)
}
// 作成TLS設定
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: caPool,
MinVersion: tls.VersionTLS12, // 强制TLS 1.2+
}
// 作成TLS処理器
handler := modbus.NewTCPClientHandler("modbus-server.local:802")
handler.TLSConfig = tlsConfig
handler.Timeout = 5 * time.Second
handler.SlaveID = 1
// connect
client := modbus.NewClient(handler)
err = handler.Connect()
if err != nil {
fmt.Printf("TLS接続の失敗: %v\n", err)
return
}
defer handler.Close()
fmt.Println("TLS接続成功です。")
// Read and hold register(アプリケーション層API与普通TCP完全一样)
regs, err := client.ReadHoldingRegisters(0, 10)
if err != nil {
fmt.Printf("Read failed: %v\n", err)
return
}
fmt.Printf("保持Register: %v\n", regs)
// write single register
_, err = client.WriteSingleRegister(100, 1234)
if err != nil {
fmt.Printf("写入失敗: %v\n", err)
return
}
fmt.Println("写入レジスタ100=1234成功")
}
七、遗留デバイスセキュリティアップグレード方案
绝大多数现有Modbusデバイスサポートなし。TLS,直接替换成本高昂。以下是几种渐进式セキュリティアップグレード方案。
7.1 方案一:TLSセキュリティゲートウェイ(おすすめ)
┌──────────────┐ TLS(802) ┌──────────────┐ Modbus TCP(502) ┌──────────────┐
│ master/SCADA │◄──────────────►│ TLSゲートウェイ │◄──────────────────►│ 遗留スレーブデバイス │
│ サポートTLS │ │ 解密/加密 │ │ サポートなし。TLS │
└──────────────┘ └──────────────┘ └──────────────┘
工作原理:
- ゲートウェイ对外提供TLS 802ポート,マスター経由加密连接アクセス
- ゲートウェイ对内経由普通Modbus TCP 502ポート连接遗留デバイス
- ゲートウェイ负责TLS加解密和证书认证,对遗留デバイス透明
- 可以用Raspberry Piについて+modbus-gateway或pymodbus実装
7.2 方案二:VPN隧道
- IPsec VPN:网络层加密,すべてのModbusトラフィック経由加密隧道传输,对应用透明
- WireGuard:轻量高速VPN,設定简单,适合工业场景
- OpenVPN:成熟稳定,サポート证书认证和细粒度アクセス控制
- Advantages:必要なし。修改任何Modbusdevice,通用性强
- 缺点:需要在网络デバイス上設定VPN,维护成本较高
7.3 方案三:SSHポートフォワーディング
# 在マスター端建立SSH隧道,将本地502ポートフォワーディング到远程デバイス
ssh -L 502:192.168.1.100:502 user@gateway.example.com -N
# マスター连接本地127.0.0.1:502,トラフィック経由SSH加密隧道传输
# 远程ゲートウェイ解包后转发到192.168.1.100:502
# Advantages:简单快速,SSH自带加密和认证
# 缺点:不适合高い同時性,SSH连接可能中断
7.4 方案四:网络絶縁与ファイアウォール
- 工业DMZ:将Modbusデバイス放在独立网络エリア,経由ファイアウォール严格限制アクセス
- ホワイトリスト。IP:只允许已知マスターIPアクセス502ポート
- VLAN絶縁:Modbusトラフィック在独立VLAN中,与办公网絶縁
- 深度包检测:工业ファイアウォール可检测Modbusfunction code,限制書き込み操作。
- 这是基础セキュリティ措施,不能替代加密,但应与其他方案配合使用
八、セキュリティベストプラクティス
- 使用TLS 1.2或更高版本:無効SSLv3、TLS 1.0、TLS 1.1
- 强制双向证书认证:クライアント側は和サーバー·サーバー都必须验证对方证书
- 使用强加密套件:优先AES-256-GCM、ChaCha20-Poly1305,無効RC4、3DES
- 证书有效期管理:設定合理的证书有效期(建议1-2年),建立自动续期机制
- 私钥保护:私钥文件权限600,存储在セキュリティ位置,使用硬件セキュリティモジュール(HSM)
- 证书吊销:部署CRL或OCSP,及时吊销丢失或泄露的证书
- 最小权限原则:使用RBACRole,モニタリング角色只能读,操作员才能写
- ポートセキュリティ:不要将502/802ポート直接暴露到公网,使用VPN或跳板机
- 日志审计:记录すべてのModbusOperation,包括连接来源、function code、register address
- 定期更新:及时更新TLS库和デバイス固件,修复セキュリティ漏洞
- 無効空パスワード和匿名アクセス:任何情况下都不允许无认证アクセス
- 网络セグメント化:按エリア/功能划分网络,限制横向移動
九、完全実践:搭建セキュリティ的Modbus TLSCommunication
#!/bin/bash
# 完全実践:从证书生成到TLS通信测试
# 1. 生成证书
bash generate_certs.sh
# 2. 启动TLSサーバー側。(终端1)
python3 tls_server_secure.py &
SERVER_PID=$!
sleep 2
# 3. 测试TLSクライアント側は连接(终端2)
python3 tls_client.py
# 4. 验证加密(抓包確認データ是密文)
sudo tcpdump -i lo port 802 -X -c 10
# 应该看到TLS握手记录和加密的应用データ,
# 看不到明文的Modbus関数コードとレジスタの値
# 5. 测试无证书クライアント側は被拒绝
python3 -c "
from pymodbus.client import ModbusTcpClient
c = ModbusTcpClient('127.0.0.1', port=802)
c.connect() # 应该失敗,因为ではないTLSクライアント側は
print('普通TCPconnectTLSポート结果:', c.connect())
"
# 6. 停止サーバー側。
kill $SERVER_PID
echo "测试完成"
十、common problems
| 問題 | 原因 | 解決方法 |
|---|---|---|
| TLS握手失敗 | 证书不被信任或过期 | ConfirmCA证书正确,チェック证书有效期 |
| クライアント側は证书被拒 | サーバー側。未設定クライアント側は认证 | サーバー側。sslctx.verify_mode=CERT_REQUIRED |
| 连接被リセット | TLS版本不兼容 | 確認双方都サポートTLS 1.2+ |
| 证书マスター名不マッチング | 证书CN与实际マスター名不符 | 使用正确的マスター名,或設定check_hostname=False |
| ポート802连接被拒 | 服务未启动或ファイアウォール | チェック服务実行状態の状態,开放802ポート |
| 性能下降明显 | TLS加解密开销 | 使用TLS会话恢复,硬件加速,长连接复用 |
| 遗留デバイスサポートなし。TLS | デバイス固件老旧 | 使用TLSゲートウェイ或VPN方案,不要直接暴露 |
| 私钥パスワードエラー | 加载加密私钥时未提供パスワード | 传入password参数或解密私钥 |
| 中间人攻击 | 未验证サーバー·サーバー证书 | 必须設定verify_mode=CERT_REQUIRED |
| 证书链不完全 | 缺少中间CA证书 | 将完全证书链放入certfile |
十一、学习リソース
- Modbus TCP Security规范:https://modbus.org/specs.php
- 中国標準GB/T 41868-2022:国家標準全文公开系统
- pymodbus TLSDocument:https://pymodbus.readthedocs.io/
- IANAポート登録:ポート802(Modbus Security)
- OpenSSLDocument:https://www.openssl.org/docs/
- NIST工业控制系统セキュリティガイド:SP 800-82
Modbus/TCP Security経由TLS加密和X.509双向证书认证,为传统Modbusプロトコル提供了工业级セキュリティ保障,且アプリケーション層プロトコル完全不变,现有程序只需替换传输层即可平滑アップグレード。对于サポートなし。TLS的遗留デバイス,可経由TLSゲートウェイ、VPN隧道或网络絶縁等方案渐进式提升セキュリティ性。在工业互联网时代,Modbus通信セキュリティ不再是可选项,而是必须落实的基础要求。
VIP专属:Modbus TLSSecure Communications Configuration Packについて
ポート802、X.509双方向認証、pymodbus ModbusTlsClient完全な構成,証明書生成コマンド。
Activate VIP即可下载完全代码,同时解锁 30+ 工程実践パッケージの内容:スクリプトのデバッグ、クイックチェック·テーブル、项目模板、トラブルシューティング案例……
前往VIP资料库下载 → 月费仅9.9元 / 年费199元