Modbus TLSセキュリティ通讯完全ガイド:Modbus TCP SecurityProtocol X.509证书认证与遗留デバイスセキュリティアップグレード実践

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

传统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证书认证。

characteristicModbus TCPModbus TCP Security
ポート502802(IANARegister)
传输层纯TCPTLS 1.2+ over TCP
加密AES-128/256-GCM
认证X.509v3双向证书认证
認証基于角色的アクセス控制(RBAC)
完全性TCPchecksumTLS MAC(HMAC-SHA256)
ADUformatMBAP + PDUMBAP + PDU(不变)
协议标识mbapmbaps

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元
Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support