従来のModbus TCPプロトコルは、暗号化や認証メカニズムなしに平文で動作し、インダストリアル·インターネット時代に深刻なセキュリティリスクに直面しています。Modbus/TCP Security(Modbus Security Protocolとも呼ばれる)は、TLS暗号化とX.50 9証明書認証により、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证书认证。
| プロパティ | Modbus TCP | Modbus TCP Security |
|---|---|---|
| ポートポートポート | 502 | 802(IANA登録) |
| トランスポート層 | 纯TCP | TLS 1.2+ over TCP |
| 暗号化 | なしなし。 | -128/256-GCM |
| 认证 | 无 | X.50 9 v 3双方向証明書 |
| 認可の付与 | なしなし。 | 役割ベースのアクセス制御RBAC |
| インテグリティ | TCPチェックサム | TLS MAC HMAC-SHA256 |
| ADUフォーマット | MBAP + PDU | MBAP + PDU変更なし |
| プロトコルID | MBAP | mbapsとは |
2.2プロトコルアーキテクチャ
�������������
│ │ │ Modbus Application Layerについて │ │ │
機能コード/レジスタ/データ- 完全に変更なし │ │ │
├─ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ┤
│ │ │ MBAPヘッダー 8バイト │ │ │
│トランザクションID|契約ID=0|長さは|ユニットID │ │ │
├─ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ┤
│ │ │ TLSレコード層 │ │ │
暗号化+認証+完全性保護 │ │ │
├─ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ┤
│ │ │ TCPポート802 │ │ │
├─ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ┤
│ │ │ IP /イーサネット │ │ │
└─ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ┘
キーデザイン:Modbusアプリケーション層PDUは全く変更されず、
TCPの上にTLS層を追加しただけで、
既存のModbusプログラムはトランスポート層を置き換えるだけでアップグレードできます。
2.3 X.50 9証明書
Modbus TLS使用双向证书认证(mTLS):
- サーバー証明書:Modbusスレーブ/サーバー·サーバー持有,クライアント側は验证サーバー·サーバー身份
- クライアント側は证书:Modbusマスター/クライアント側は持有,サーバー·サーバー验证クライアント側は身份
- CA証明書クライアント証明書とサーバ証明書のルート証明書を発行し、双方が信頼する必要がある。
- 証明書フィールド:Common Name(CN)用于标识デバイス,Organization(O)用于标识厂商
- 証明書の拡張RBAC承認のための役割情報
2.4役割ベースのアクセス制御RBAC
Modbus Security规范定义了四种角色,経由证书中的角色扩展字段控制アクセス権:
| 役割の役割 | アクセス権 | 説明書は |
|---|---|---|
| Monitor | 読み取り専用(機能コード01/02/03/04) | データ収集と監視、値の変更なし |
| Operatorの | Read + Writeコイル/レジスタ | 可执行常规操作,如启停デバイス |
| メイテイナー | 読み取り+書き込み+診断 | 校正、リセットなどのメンテナンス操作が可能 |
| Administrator | すべての権限 | 構成の変更、証明書の管理、ファームウェアの更新が可能 |
3. TLS証明書の作成
使用OpenSSL生成CA证书、サーバー·サーバー证书和クライアント側は证书。
#!/ビン/bash
#generate_certs.sh-Modbus TLS証明書を生成する
mkdir -p ts
CD Certsより
####1。CAルート书の生成=======
echo "CAルート証明書を生成します。.. "
openssl genrsa -out.key 2048
openssl req-new-x 509-days 3650-key ca.key-out ca.crt
-subj "/C=CN/ST= /L= /O=ModbusCA/=IT/CN= Root CA"
#####2。サーバ証明書の生成(Modbusスレーブ) =======
echo "サーバー証明書を生成します。.. "
openssl genrsa -out server.key 2048
openssl req -new -key server.key-out server.csr
-subj "/C=CN/ST= /L= /O=MyCompany/= al/CN= modbus-server.local"
#CAでサーバ証明書を発行
openssl x 509-req-days 3650-in server.csr-CA ca.crt-CAkey ca.key
- CAcreates-out server.crt
#####3。クライアント証明書の生成(Modbusマスタ) ======
echo "クライアント証明書を生成します。.. "
openssl genrsa-out client.key 20 4 8
openssl req -new -key.key -out.csr
-subj "/C=CN/ST= /L= /O=MyCompany/=/CN= modbus--001"
#CAでクライアント証明書を発行
openssl x 509-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-サーバー証明書(Modbusスレーブにデプロイ)
#server.key-サーバー秘密鍵(Modbusスレーブにデプロイされ、秘密に!)
client.crt-クライアント証明書(Modbusマスターにデプロイ)
client.key-クライアント秘密鍵(Modbusマスターにデプロイされ、秘密に!)
#証明書生成スクリプトを実行する
chmod +x generate_certs.sh
./ generate_certs.sh
#証明書の内容
openssl x 509-in server.crt-text-noout|ヘッド-20
証明書が秘密鍵と一致するかどうかテスト
openssl x 509-noout-modulus-in server.crt| MD 5 Sum
openssl rsa-noout-modulus-in server.key| MD 5 SUM
2つのMD 5値は同じである必要がある。
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,
)
# 连接(TLS握手会自动进行证书验证)
connection = client.connect()
if not connection:
print("TLS接続の失敗!")
exit(1)
print("TLS接続成功です。,证书验证経由")
# リードホールドレジスタ(アプリケーション層与普通TCP完全一样)
result = client.read_holding_registers(
address=0, count=10, device_id=1
)
if not result.isError():
print("レジスタを保持する。:", result.registers)
else:
print("読み取り失敗:", result)
# 単一レジスタの書き込み
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("コイルの状態:", result.bits[:8])
# 接続を閉じる
client.close()
print("连接已关闭")
4.1 SSLコンテキストのカスタマイズ
# tls_client_custom.py - 自定义SSLContextより(更セキュリティ的設定)
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
# 加载CA证书(验证サーバー·サーバー证书)
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サーバー側。(セキュリティ的Modbusスレーブ)
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]) # 温度/湿度/气压
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("协议: TLS 1.2+")
print("认证: X.509双向证书")
print("按 Ctrl+C 停止")
# 启动TLSサーバー·サーバー
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",
)
# 加载CA证书,用于验证クライアント側は证书
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)
# 作成并启动TLSサーバー·サーバー
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() {
// 加载CA证书
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
// 连接
client := modbus.NewClient(handler)
err = handler.Connect()
if err != nil {
fmt.Printf("TLS接続の失敗: %vn", err)
return
}
defer handler.Close()
fmt.Println("TLS接続成功です。")
// リードホールドレジスタ(アプリケーション層API与普通TCP完全一样)
regs, err := client.ReadHoldingRegisters(0, 10)
if err != nil {
fmt.Printf("読み取り失敗: %vn", err)
return
}
fmt.Printf("レジスタを保持する。: %vn", regs)
// 単一レジスタの書き込み
_, err = client.WriteSingleRegister(100, 1234)
if err != nil {
fmt.Printf("写入失敗: %vn", err)
return
}
fmt.Println("写入レジスタ100=1234成功")
}
七、レガシー設備のセキュリティアップグレード方案
绝大多数现有Modbusデバイスサポートなし。TLS,直接替换成本高昂。以下是几种渐进式セキュリティアップグレード方案。
7.1シナリオ1:TLSセキュリティゲートウェイ(推奨)
┌──────────────┐ TLS(802) ┌──────────────┐ Modbus TCP(502) ┌──────────────┐
│ マスター/SCADA │◄──────────────►│ TLSゲートウェイ │◄──────────────────►│ 遗留スレーブデバイス │
│ サポートTLS │ │ 解密/加密 │ │ サポートなし。TLS │
└──────────────┘ └──────────────┘ └──────────────┘
工作原理:
- ゲートウェイ对外提供TLS 802ポート,マスター経由加密连接アクセス
- ゲートウェイ对内経由普通Modbus TCP 502ポート连接遗留デバイス
- ゲートウェイ负责TLS加解密和证书认证,对遗留デバイス透明
- 可以用Raspberry Piについて+modbus-gateway或pymodbus実装
7.2オプション2:VPNトンネル
- IPsec VPN:网络层加密,すべてのModbusトラフィック経由加密隧道传输,对应用透明
- WireGuard:軽量高速VPN、シンプルな設定、産業シナリオに適しています。
- Open VPNについて:成熟稳定,サポート证书认证和细粒度アクセス控制
- 利点:Modbusデバイスを変更する必要がなく、汎用性が高い
- 短所:ネットワーク機器にVPNを設定する必要があり、メンテナンスコストが高い
7.3シナリオ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
# 优点:简单快速,SSH自带加密和认证
# 缺点:不适合高い同時性,SSH连接可能中断
7.4オプション4:ネットワーク分離とファイアウォール
- 産業用DMZ:Modbusデバイスを別のネットワークエリアに設定し、ファイアウォールでアクセスを厳しく制限
- ホワイトリストIP既知のプライマリIPのみがポート502にアクセスできます。
- VLANの分離Modbusトラフィックは、オフィスネットワークから分離された独立したVLANにあります。
- ディープパケット検出:工业ファイアウォール可检测Modbus機能コード,限制書き込み操作。
- これは基本的なセキュリティ対策であり、暗号化の代替ではありませんが、他のスキームと組み合わせて使用する必要があります。
八、セキュリティベストプラクティス
- TLS 1.2以上の使用SSLv3、TLS 1.0、TLS 1.1を无効にする
- 强制双向证书认证クライアントとサーバは互いの証明書を検証する必要がある
- Strong Encryption Suiteの使用AES-256-GCM、ChaCha20-Poly 1305を優先、RC4、3DES無効
- 証明書の有効性管理合理的な証明書の有効期間(推奨1-2年)を設定し、自動更新メカニズムを確立する
- 秘密鍵の保護秘密鍵ファイルのアクセス権600、セキュリティな場所に保存、HSM(ハードウェアセキュリティモジュール)を使用
- 証明書の取り消しCRLまたはOCSPを導入し、紛失または漏洩した証明書をタイムリーに取り消す
- 最小権限の原則:RBACの役割を使用すると、監視役割は読み取りのみでオペレータは書き込みが可能です。
- ポートのセキュリティポート502/802をパブリックネットワークに直接公開しないでください。VPNまたはSpringboardを使用してください。
- ログ監査接続元、関数コード、レジスタアドレスを含むすべてのModbus操作を記録する
- 定期的な更新TLSライブラリとデバイスファームウェアをタイムリーに更新し、セキュリティ脆弱性を修正
- 空のパスワードと匿名アクセスの無効化:いかなる場合も無認証アクセスは許可されません。
- ネットワークセグメンテーション:地域/機能別にネットワークを分割し、横移動を制限
九、完全実践:搭建セキュリティ的Modbus TLS通信
#!/ビン/bash
証明書生成からTLS通信テストまでの完全な実践
# 1。証明書の作成
bash generate_certs.sh
# 2です。TLSサーバの起動(端末1)
python 3 tls_server_secure.py&
SERVER_PID =$!
Sleep 2について
# 3です。TLSクライアント接続のテスト端末2
Python 3 tls_client.py
# 4です。暗号化の検証(データが暗号文であることを確認するパケットスナップ)
sudo tcpdump -i lo port 80 2-X -c 10
#TLSハンドシェイク記録と暗号化されたアプリケーションデータを見る必要があります。
#平文のModbus関数コードとレジスタ値が表示されない
# 5のこと。証明書のないクライアントのテストが拒否されました
Python 3-C“
from pymodbus.client import modbusTcpClient
c = ModbusTcpClient '127.0.0.1'、port=802
c.connect #はTLSクライアントではないため失敗するはずです
print '通常のTCP接続TLSポート結果' c.connect
"
# 6です。サービスの停止。
$SERVER_PIDを殺す
echo“テスト完了”
X.よくある質問
| 問題は | 理由は | 解決策は |
|---|---|---|
| TLS握手失敗 | 证书不被信任或过期 | CA証明書が正しいことを確認し、証明書の有効期限を確認する |
| クライアント証明書の拒否 | サーバにクライアント認証が設定されていません | サーバsslctx.verify_mode=CERT_IRED |
| 接続がリセットされた | TLSバージョンの非互換性 | 両方ともTLS 1.2+をサポート |
| 証明書のホスト名が一致しません | 证书CN与实际マスター名不符 | 正しいホスト名を使用するか、check_hostname =Falseを設定します。 |
| ポート80 2接続拒否 | サービスが開始されていないか、ファイアウォールがファイアウォール | サービスの稼働状況を確認し、ポート802をオープン |
| 性能低下が著しい | TLS+復号化のオーバーヘッド | TLSによるセッション回復、ハードウェアアクセラレーション、長時間接続多重化 |
| レガシーデバイスはTLSをサポートしない | デバイス固件老旧 | TLSゲートウェイまたはVPNスキームを使用し、直接公開しない |
| 秘密鍵パスワードエラー | 暗号化された秘密鍵のロード時にパスワードが指定されなかった | passwordパラメータの受信または秘密鍵の復号化 |
| 中間者攻撃。 | サーバ証明書が認証されていません | verify_mode = CERT_REQUIREDを設定する必要があります。 |
| 証明書チェーンが不完全 | 缺少中间CA证书 | 完全な証明書チェーンをcertfileに入れる |
十一、学习リソース
- Modbus TCPセキュリティ仕様modbus.org/specs.php
- 中国標準GB/T 41868-2022:国家標準全文公开系统
- pymodbus TLSドキュメントpymodbus.readthedocs.io/
- IANAポート登録ポート802
- OpenSSLのドキュメントwww.openssl.org/docs/
- NIST産業用制御システムセキュリティガイドSP 80 0 - 8 2
Modbus/TCP Securityは、TLS暗号化とX.50 9双方向証明書認証により、従来のModbusプロトコルに産業グレードのセキュリティを提供します。アプリケーション層プロトコルは完全に変更されておらず、既存のプログラムはトランスポート層を置き換えるだけでスムーズにアップグレードできます。TLSをサポートしていないレガシーデバイスでは、TLSゲートウェイ、VPNトンネル、ネットワーク分離などのソリューションを使用してセキュリティを段階的に強化できます。インダストリアル·インターネットの時代において、Modbus通信セキュリティはもはやオプションではなく、実装する必要がある基本要件です。
VIP专属:Modbus TLSSecure Communications Configuration Packについて
ポート80 2、X.50 9双方向認証、証明書生成コマンドを含むpymodbus ModbusTlsClient完全設定。
完全なコードをダウンロードしてロックを解除 30歳以上。 工程実戦パッケージ:デバッグスクリプト、速検表、プロジェクトテンプレート、検査ケース
VIPデータベースからダウンロード 月額9.9ドル/年間199ドル