本来想按照上集的思路,续写包解析的文章,没想到中间蹦出了CVE-2021-26414,同时微软留给用户修改的时间也是相当紧凑。对于不了解这个漏洞的同学,简单地说这是一个关于DCOM的安全漏洞,微软要做相应的DCOM加固,要求所有客户端在创建一个服务端的示例时都使用packet层面上的认证。今年3/8的更新会让用户在注册表掌控要不要使用这个加固的DCOM设置,而6/24的更新则强制用户使用加固后的DOCM设置,此时用户再没有不使用该加固的可能,后果就是如果OPC软件客户端如没做修改就不能正常访问OPC服务端了。做为有超过二十年历史的OPC Classic,把当时的开发商找出来修改原来的源程序真不是件容易事,破产的破产,转行的转行,开发的重点都转向了UA,很难找到有愿意做OPC Classic的活跃开发商了。如果原来的开发商不存在了而又没有源程序可以修改怎么办?在此背景下迫切需要一个方案来解决这个问题,最好是一劳永逸的方案,即使以后再出现任何与DCOM相关的安全漏洞也不用担心。
业界第一款让你忘掉COM/DCOM,忘掉RPC,只要会Javascript/Java/C++/C#/Python等就可以获得OPC数据的方案横空出世了。此方案巧妙地利用最新的WebSocket技术将OPC服务器转为WEB服务器,使用一个IIS的原生模块起到连接OPC和WEB的作用。作为Javascript的演示,客户端只要写下如下的简单代码就可以轻松获取OPC的数据,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OPC to Web made easy</title>
</head>
<script language="javascript" type="text/javascript">
var wsUri = "ws://127.0.0.1/OPC/main.opc";
var output;
function init() {
var input = document.getElementById("messageId");
input.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
event.preventDefault();
document.getElementById("sendId").click();
}
});
output = document.getElementById("output");
startWebSocket();
}
function startWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) };
}
function onClose(evt) {
writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
writeToScreen('<span style="color: red; white-space: pre-line;">' + evt.data + '</span>');
}
function endChannel() {
websocket.close();
}
function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message) {
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message) {
output.innerHTML = message;
}
window.addEventListener("load", init, false);
</script>
<h2>Permanent DCOM-free solution for CVE-2021-26414</h2>
<h2>Turn OPC Server to Web Server the FIRST time, EVER</h2>
<h2>Use JavaScript to access OPC data ANYWHERE</h2>
<div id="output"></div>
<body>
<p>
<input type="text" id="messageId"> <label>(type a command or ? to get help)</label>
</p>
<button id="sendId" onclick="doSend(getElementById('messageId').value);getElementById('messageId').value=''">Send</button> <button onclick="endChannel()">End</button>
</body>
</html>
代码并不复杂,就是打开一个WebSocket的连接,设置好相应的事件接口。运行以后是这样的,
我们在右侧得到一个JSON返回,格式也非常简单,告诉我们服务器上是否装有DA或者AE,状态提示已连接上了。下面该干什么不清楚,就简单地敲个?,再按下Send按钮,得到了如下,
妥妥的使用说明,包括支持的命令及返回的JSON内容及含义。既然不知道DA的结构,那就输入一个命令,browse,看看顶层的tag有哪些,
右边显示返回的JSON包括有tag的名字,ID和枝/叶状态。b(branch)值为1表示该tag是枝,下面还有它的子tag。有了这些信息接着输入感兴趣的枝tag比如,browse:Simulation Items,运行后得到了下图,
得到的都是位于Simulation Items下面的tag内容,看看右面的JSON展开图,是不是有和在OPC Explorer下似曾相识的感觉?对DA的tag结构有了进一步的了解后,选取一个枝tag比如Random,browse: Random 得到的是,
这些都是位于枝tag下的叶tag的信息,下面我们就可以选感兴趣的tag来监测了。比如这个命令,subscribe: Random.Int1, Random.Int2, Random.Int3, Random.Int4 然后得到了如下,
这正是所期望的4个实时tag的值,是不是很酷和很简单哦?
运行过程中可以随时添加或移除不想要的tag,比如下一个命令,unsubscribe: Random.Int4 就会把该tag移走而不影响其它tag的正常运行,
如果想移除所有的tag,只要输入unsubscribe不跟随任何tag就行,是不是很爽?
以上都是和DA相关的命令,下面谈谈AE。AE相比DA显得更为简单,只有二个命令,subscribeAE和unsubscribeAE。第一个命令是监测获得任何AE发出的报警和事件,第二个命令则是取消所有的监测。来看看输入第一个命令后的样子,
得到了期望的AE发出的警报和事件,不想监测就简单地输入unsubscribeAE就行了。
由于服务端使用的是IIS原生模块,性能得到了保证,同时用户可以轻松地使用IIS提供的其它内嵌功能,比如用户身份验证方式的选择,安全的HTTPS协议等等而不需要额外编程。原来因为OPC Classic使用COM/DCOM而被诟病的主要缺陷,如DCOM的设置困难和防火墙问题等,因为OPC和WEB在同一个服务器上并使用缺省的HTTP 80端口而迎刃而解,完全助力边缘计算和云计算。当年大肆鼓吹的UA针对Classic的那些优势在这一方案面前丝毫不占上风,从而对你的OPC Classic投资做到了最大的保护。
刚出校门的小鲜肉们拥抱的是容易上手轻巧灵活的Javascript,拒绝历史的重负,这也应该是打开OPC的正确方式!作为开发者,你是愿意读寥寥数行的在线命令帮助还是上千页的UA文档呢?