标准WinCC无Audit实现电子签名与审计追踪的完整方案
发布时间:2026/9/28 13:14:06来源:尧图网络
去年接了一个生物制剂车间的改造项目设备是利旧的统一平台用的WinCC 7.4 SP1合同里并没有包含Audit选件。等业主QA甩过来一份四十多页的《审计追踪与电子签名需求说明书》再回头看组态环境时才意识到问题大了——原定选型根本没有买Audit授权。找原厂补报价选件加服务费下来足够再买两台工控机工期也不允许。于是我开始研究在标准WinCC上自行实现电子签名与审计追踪最终这套方案通过了客户QA的现场验证也服务了两个后续项目。今天把它完整整理出来内容包括需求拆解、三层架构设计、签名弹窗实现、审计日志存储、哈希链防篡改以及我踩过的一堆坑。1. 为什么说无Audit做审计不是天方夜谭需求拆解与方案边界1.1 先弄清客户到底要审什么接触过制药、食品、精细化工产线的朋友都会懂所谓审计追踪落到现场核心就是回答四个问题谁、在什么时间、对哪个对象、做了什么操作。审计追踪要记录的是操作过程比如操作员A在14:23:06把1号反应釜的PID设定值从72.5改成75.0顺手点了报警确认电子签名则是对关键操作的身份确认比如放行本批产品修改配方参数确认清洗程序完成这类动作必须由特定权限的人输入账号密码确认后才能执行。我当时把客户四十多页说明书压缩成一张表格分为必做和可选两类需求客户原文描述是否必做操作员登录/注销记录所有用户登录和注销必须有据可查必做关键参数修改记录记录修改前、修改后的值及操作人必做报警处理记录报警确认/禁用/使能需追踪必做电子签名关键批次环节需二次身份确认必做趋势曲线操作记录缩放、游标操作也要留痕可选系统级全量操作捕获任何窗口打开、任何鼠标点击都记录可选做完需求拆解就该明白自制方案的目标不是复刻Audit选件而是覆盖产线审计中90%的实际需求。理解这一点非常重要它决定了整个技术方案走多深、做多重。1.2 Audit选件自己带什么能力西门子Audit选件之所以贵是因为它在系统底层把采集这件事做完了。WinCC的每一次运行系统登录、用户切换、变量修改、画面操作Audit都能在系统内核层面捕获不需要你去每个按钮上挂脚本。它同时提供了加密存储和专门的审计查询界面操作记录、报警记录、变量归档记录还能做时间轴关联查询。这套能力自制的确做不到同样程度。比如用户在内置变量归档里修改归档时间范围、打开一个窗口、右键点击某个对象这类UI层面的动作靠脚本去捕获会漏掉很多而且脚本本身只能挂在已知对象上——你不可能为每一个系统控件都写一遍监控逻辑。1.3 自制方案的取舍边界我的判断标准很简单这世上没有免费的完美只有值得的取舍。自制方案能覆盖的是有价值的关键操作包括登录注销、关键参数设定值修改、模式切换、报警确认、配方调用、电子签名放行。做不到的是全量UI操作审计也就是无法像Audit那样记录操作者浏览了哪个画面、拖动了哪条趋势、打开了哪个对话框。在大多数GMP和ISO内审场景下前者才是核心这也是我敢向客户拍胸脯的原因。但我也明确告诉客户如果出口欧美、监管方明确要求符合21 CFR Part 11的电子记录电子签名且审计范围包含全量UI操作那还是得上正版Audit。自制方案可以作为过渡和补充不能在所有场景下宣称完全替代。2. 整体架构三层模型与关键技术选型2.1 三层模型表现层、逻辑层、存储层整套方案我按分层思想设计避免所有代码堆在画面脚本里最后变成一锅粥。表现层是操作员和审计人员能看到的东西包括登录窗口、电子签名确认弹窗、审计查询画面。这层做的是输入采集和结果显示不含业务规则。逻辑层是公共函数库和独立归档服务。公共函数库提供写入审计日志、校验用户密码、计算哈希等接口所有画面脚本统一调用独立归档服务负责把队列文件加工后写入数据库并维护哈希链。存储层是SQL Server和队列文件。队列文件是过渡缓冲防止高频写库阻塞画面SQL Server保存最终审计数据和用户档案。表现层画面窗口/弹窗/查询画面 ↓ 调用 逻辑层公共函数库 / 独立归档服务 ↓ 写入 存储层队列文件 → SQL Server2.2 为什么选WinCC自带的SQL Server而不是另装数据库这是整个选型中最关键的一个决定。WinCC本身是基于SQL Server的组态软件项目运行时会自动创建一个项目数据库用来存变量归档、报警记录、用户管理信息。也就是说只要装了WinCC这台机器上就已经有一套SQL Server在运行。直接往这个库里加几张自定义表省去了另装数据库、另做服务配置、另管一套备份策略的麻烦。更重要的是工控上位机环境往往不允许你随便装软件用系统自带的数据库最稳妥。不过这里有个红线不要往WinCC的系统表里写东西只建自己的自定义表表名加上统一前缀比如Cust_开头避免和系统对象混淆。备份时除了WinCC项目本身的备份我会把自定义审计表单独做一份定时备份。2.3 脚本语言选VBS还是C脚本WinCC的V7及以上版本同时支持C脚本和VBS脚本。我的习惯是数据库访问、文件读写、字符串拼接这类工作优先用VBSADO访问数据库在VBS里写起来很顺而且不需要关心指针和内存释放问题。需要直接操作WinCC内部函数、调用系统C API的场景用C脚本比如读取某个系统变量、控制画面窗口切换、操作内部函数的效率更高。实际项目里我的公共审计函数是用VBS写的画面按钮事件里的一小段取变量→调公共函数→写日志逻辑也用VBS。只有涉及画面窗口动态加载这类动作时才在C脚本里写。这个分工足够清晰维护起来不容易乱。3. 电子签名的落地操作员档案、确认弹窗与签名记录3.1 操作员表与密码管理不走WinCC用户管理的理由WinCC自带用户管理可以为用户分配授权等级登录后也能识别当前用户名为什么还要自建一套操作员档案原因在于电子签名的语义比系统登录更严格。系统登录解决的是谁能进画面电子签名解决的是这次关键操作由谁确认。在实际车间里经常出现工程师登录了系统、操作员过来请他帮忙点一下就离开的情况。如果电子签名沿用当前登录用户名就无法证明这个关键操作确实由本人确认。所以我自建了操作员档案表电子签名弹窗要求当事人现场输入工号和密码。表结构CREATE TABLE Cust_User ( ID INT IDENTITY PRIMARY KEY, UserCode NVARCHAR(20) NOT NULL UNIQUE, UserName NVARCHAR(50) NOT NULL, Role NVARCHAR(20) NOT NULL, PassHash NVARCHAR(128) NOT NULL, PassSalt NVARCHAR(32) NOT NULL, IsActive BIT DEFAULT 1 );密码绝不存明文。我在项目初始化时写了一个独立小工具为每个用户生成随机盐值再用SHA256算法计算SHA256(盐 密码)后写入表里。WinCC画面脚本运行时只负责接收输入、计算哈希、比对哈希不涉及任何密钥管理避免Key泄漏。3.2 签名确认弹窗的执行流程电子签名弹窗我做成一个画面窗口Faceplate通过WinCC的画面窗口控件动态打开模态显示。设计上遵循一个原则签名弹窗不直接执行业务操作它只做两件事——验证身份并记录签名验证通过后通知主画面继续执行原始操作。完整流程操作员在主画面点击配方放行按钮。主画面脚本先不做任何实际动作只记录本次待执行操作的信息操作代码、对象、目标值然后打开签名弹窗。弹窗加载待执行操作摘要显示即将执行配方批号B20240517放行确认请签名。操作员输入工号、密码点击确认签名。脚本查询Cust_User表对密码加盐计算哈希并比对。比对通过写入Cust_Signature表通知主画面执行原操作关闭弹窗。比对失败写入一条签名失败的审计记录弹出提示主画面保持挂起状态。第7步非常重要。签名失败不能只是静默拒绝它本身也是审计事件。如果审计记录里全是成功的签名反而会让人怀疑有人绕过了签名机制。失败记录的存在恰好证明机制在认真拦截。3.3 签名记录的写入与公共函数示例签名记录写入Cust_Signature表CREATE TABLE Cust_Signature ( ID INT IDENTITY PRIMARY KEY, Ts DATETIME NOT NULL DEFAULT GETDATE(), UserCode NVARCHAR(20) NOT NULL, UserName NVARCHAR(50) NOT NULL, ActionCode NVARCHAR(30) NOT NULL, ActionDesc NVARCHAR(200) NOT NULL, Result BIT NOT NULL, RecordHash NVARCHAR(64) NULL, PrevHash NVARCHAR(64) NULL );画面脚本调公共函数时我习惯把用户名直接作为入参传进来而不是在函数内部再去读WinCC当前用户变量。这样函数更独立也方便做单元测试。VBS版本的公共函数大致长这样Sub AddSignatureLog(sUserCode, sUserName, sActionCode, sActionDesc, bResult) Dim conn Set conn CreateObject(ADODB.Connection) conn.Open ProviderSQLOLEDB.1;Data Sourcelocalhost\WINCC;Initial CatalogCC_DemoProject;Integrated SecuritySSPI Dim cmd Set cmd CreateObject(ADODB.Command) cmd.ActiveConnection conn cmd.CommandText INSERT INTO Cust_Signature(Ts, UserCode, UserName, ActionCode, ActionDesc, Result) VALUES(GETDATE(), ?, ?, ?, ?, ?) cmd.Parameters.Append cmd.CreateParameter(p1, 200, 1, 20, sUserCode) cmd.Parameters.Append cmd.CreateParameter(p2, 200, 1, 50, sUserName) cmd.Parameters.Append cmd.CreateParameter(p3, 200, 1, 30, sActionCode) cmd.Parameters.Append cmd.CreateParameter(p4, 200, 1, 200, sActionDesc) cmd.Parameters.Append cmd.CreateParameter(p5, 11, 1, 1, bResult) cmd.Execute conn.Close End Sub数据库连接串里的实例名和库名在不同项目里不一样这个需要在现场确认。第一次配置时可以从WinCC项目属性里找到运行时数据库的连接信息再调整连接串。4. 审计追踪的实现事件采集、队列缓冲与哈希链防篡改4.1 哪些事件必须捕获我定义了一套事件类型编码表统一了不同画面的日志规范事件类型编码捕获方式用户登录LOGIN登录按钮脚本用户注销LOGOUT注销按钮脚本设定值修改SETPOINT_CHANGEIO域输出输入事件模式切换MODE_CHANGE按钮/选择控件事件报警确认ALARM_ACK报警控件事件配方调用RECIPE_CALL配方按钮事件签名失败SIGN_FAIL签名弹窗脚本签名成功SIGN_OK签名弹窗脚本编码统一用英文字母和下划线不要用中文入库避免字符集问题。人眼看的描述信息放在另一个字段比如ActionDesc写中文没问题数据库字段用NVARCHAR类型。这样既保证查询稳定又保证显示友好。4.2 公共审计函数的调用方式在WinCC中每个对象的事件页都可以挂脚本。按钮的鼠标点击事件、IO域的输出输入事件、画面窗口的打开关闭事件是审计捕获的主要挂载点。我的做法是写一个公共函数AddAudit在需要捕获的地方调用Sub AddAudit(sEventType, sObject, sBefore, sAfter) Dim sUser sUser HMIRuntime.Tags(CurrentUserName).Read Dim sLine sLine Now | sUser | sEventType | sObject | sBefore | sAfter WriteQueueFile sLine End Sub注意这里用的是内部变量CurrentUserName不是WinCC系统变量。系统登录成功后登录脚本同步把这个内部变量刷新为当前操作员工号。这样做的好处是不依赖具体WinCC版本的系统变量名逻辑完全自控。按钮事件里的调用示例 按钮点击事件 Sub OnClick(ByVal Item) Dim sOldValue sOldValue HMIRuntime.Tags(PID_Reactor1_SP).Read HMIRuntime.Tags(PID_Reactor1_SP).Write 75.0 AddAudit SETPOINT_CHANGE, PID_Reactor1_SP, sOldValue, 75.0 End Sub这里的思路是先把操作前的值读出来执行操作后把旧值和新值一起传给审计函数。只记新值不记旧值审计追溯能力就打了对折这个细节极其重要但经常被忽略。4.3 队列文件设计避免写库阻塞画面操作最开始我直接在画面脚本里同步调用ADO写SQL Server结果在高频操作时出现了按钮点击后卡顿3到5秒的严重问题原因后面会细讲。后来我把存储改成队列文件独立归档服务的两段式设计。画面脚本只需往队列文件追加一行写入速度是毫秒级然后立即返回继续处理画面操作。队列文件路径固定比如D:\AuditQueue\queue_001.log。WinCC单用户项目只有一个运行系统进程在写不存在多进程并发写同一文件的问题。如果是双服务器冗余结构我给两台服务器各自配置独立队列文件文件名里带节点标识避免文件锁冲突。队列文件里的每一行是竖线分隔的纯文本2024-05-17 14:23:06.250|OP001|SETPOINT_CHANGE|PID_Reactor1_SP|72.5|75.0 2024-05-17 14:23:08.410|OP001|ALARM_ACK|Ack_Alarm_07|0|1固定字段顺序解析简单乱码风险小。日志文件按天轮转每天零点自动新建一个归档服务处理完的行会移动到done子目录。这个轮转和移动由归档服务自己负责不在画面脚本里做。4.4 独立归档服务用计划任务做哈希链写入归档服务我用PowerShell脚本实现注册为Windows计划任务每30秒执行一次。脚本逻辑读取队列目录里最新的未处理日志文件。读取数据库Cust_AuditLog表当前最后一条记录的RecordHash。逐行处理新日志为每行计算SHA256(行内容 上一条RecordHash)作为本条RecordHash。批量插入SQL Server处理完的行移动到done目录。关键片段$prevHash { $sql SELECT TOP 1 RecordHash FROM Cust_AuditLog ORDER BY ID DESC # 执行查询并返回哈希值 } $lines Get-Content $queueFile foreach ($line in $lines) { $hashInput $line | $prevHash $sha256 [System.Security.Cryptography.SHA256]::Create() $bytes [System.Text.Encoding]::UTF8.GetBytes($hashInput) $hash [System.BitConverter]::ToString($sha256.ComputeHash($bytes)).Replace(-, ).ToLower() # INSERT INTO Cust_AuditLog(Ts, UserCode, EventType, ObjectName, BeforeValue, AfterValue, RecordHash, PrevHash) $prevHash $hash }这里最关键的设计是哈希链每一条新记录的哈希值都依赖于上一条的哈希值形成一条不可断开的链。如果有人事后修改了数据库里某条记录哪怕只改一个字符该记录的哈希值立即失效且它之后所有记录的PrevHash都无法对账。审计人员用校验工具跑一遍全链表任何断裂点都是篡改证据。4.5 审计查询画面让数据可以被看见审计查询我做了两个入口。一个是在WinCC画面里嵌入查询窗口用WinCC脚本读取数据库并按时间范围、用户名、事件类型过滤以只读表格展示另一个是做一个独立的Excel导出工具审计人员拿到数据后可以在Excel里做二次透视分析。查询页面上一律不提供修改和删除按钮这是底线。查询脚本里我额外加了一个完整性校验按钮一键遍历全表的哈希链把断链位置直接标红。这个按钮在客户QA现场演示时是加分项比任何口头解释都有说服力。5. 上线踩坑实录这些问题不遇到一次真的不会注意5.1 C脚本换行与分号语句未结束的坑第一个踩的坑就是热词里那个wincc脚本语句未结束。我在一个按钮的C脚本里写了一段很长的变量赋值逻辑因为从旧项目复制过来时少写了一个分号编译阶段没有报错运行到那一行就直接不执行按钮看起来毫无反应。排查时打开WinCC的脚本调试器逐行单步才定位到问题。后来我养成一个习惯所有C脚本写完先做一次全编译再用调试器跑一遍典型路径绝不因为工程量大就跳过这一步。脚本报语句未结束这类错误时多半是上一行末尾的分号丢了或者是中英文符号混用了。C脚本不接受中文分号全角括号也会导致解析错乱这在从Word文档复制代码时特别容易发生。5.2 SQL Server连接串的实例名问题WinCC自带的SQL Server实例名在不同版本和安装方式下不一样。常见的有localhost\WINCC但有些OEM版本是localhost\WINCC_项目名还有的是localhost\SQLEXPRESS。我第一次做连接测试时按照网上教程写了localhost\WINCC死活连不上后来在WinCC项目属性里查到实际实例名才解决。另一点连接串里通常会写Initial Catalog指定数据库名WinCC的项目数据库名是一串类似CC_DemoProject_2024_05_17_...的长字符串复制时容易截断。建议直接在数据库管理工具里查一遍完整库名再粘贴到脚本里。5.3 同步写库导致画面操作卡顿队列方案为什么是必须的我在4.3节提到同步写库卡顿的问题具体现象是这样的操作员疯狂点按钮时画面脚本每点击一次就执行一次ADO插入SQL Server在繁忙响应时偶发延迟插入语句被阻塞按钮事件脚本就卡在那里界面表现为点一下转三秒圈。后来我抓了数据库的阻塞视图发现是日志文件增长和锁等待导致偶发慢插入。改用队列文件后画面脚本只负责写一行文本写入耗时基本在1到2毫秒彻底解决了卡顿。归档服务慢一点没关系审计数据晚30秒入库完全可以接受。5.4 时间戳不可靠问题操作员改系统时间的对策项目试运行第三天发现一批审计记录的时间戳比PLC时间早了整整一小时。排查半天原因是某个夜班操作员觉得电脑时间不对顺手把系统时间改了WinCC脚本里用的Now取的是PC本地时间自然跟着变。从那以后我的审计脚本全部改为优先读取PLC时间。西门子PLC里一般都有读取系统时间的系统功能块可以把时间读到DB块再通过WinCC变量读到上位机。这样审计时间戳的来源就是PLC而不是操作员可以随意改动的PC时钟。如果项目里没有PLC时间可用至少要在Windows组策略里限制操作员账户没有修改系统时间的权限。5.5 数据库磁盘写满导致WinCC整体异常WinCC项目数据库和我们的自定义审计表在同一个SQL Server实例里这意味着审计日志膨胀会连累整个WinCC的运行。我在压测时故意连续写了200万条测试数据结果磁盘满了WinCC运行系统直接弹出数据库错误画面数据归档也停了。这个教训让我给审计库单独建了磁盘容量预警。归档服务每轮写入前先检查磁盘剩余空间低于5GB就发报警到WinCC画面同时暂停写入并缓存到队列文件绝不冒险继续写导致数据库崩溃。容量规划上按单条审计记录约200字节计算一个中等规模的产线一天大约产生2万条记录约4MB一年约1.5GB这个量级不算大但必须纳入备份和容量管理。6. 向审计人员证明这套系统可信验证清单与方案边界6.1 现场演示的完整性校验流程客户QA来验收那天我没有拿一沓纸讲PPT而是直接做了一个篡改挑战。我在审计库里随机选了一条记录让客户QA亲自用SQL把这条数据里的设定值从75改成76然后点击查询画面上的完整性校验按钮。几秒钟后校验结果把这条记录和它之后的所有记录全部标红提示哈希链断裂自ID208741处起数据不可信。这个演示的效果比任何承诺都好。它证明的不是我们不会被人改数据而是如果有人改了数据系统一定能发现。审计追踪的本质不是防止修改而是让修改可被发现、可被追踪。6.2 备份策略与留存周期审计数据的留存周期客户要求是三年我做了三级备份数据库每日全量备份备份文件用SHA256计算校验值并记录到独立文件中每周把备份文件复制到另一台存储服务器每月刻录一次光盘归档。在审计查询界面上我还加了一个备份清单页签展示历次备份的时间、文件名、哈希值方便审计人员核对备份链的完整性。6.3 什么时候该上正版Audit写到这里我得讲一句良心话自制方案有它的适用边界。如果项目合同里明确写了符合FDA 21 CFR Part 11监管审计会要求供应商提供完整的验证文档IQ/OQ/PQ、软件版本受控证明、系统安全配置说明这套自制方案在文档链完整性上很难满足要求。此外如果客户要求审计范围包含所有UI操作、跨画面操作追踪、甚至操作回放自制方案也做不到。我的判断标准是内审、客户审计、非关键批次追溯这些场景自制方案完全够用且性价比极高涉及法规强制、出口产品、监管方现场检查的场合建议直接采购Audit选件。这不是能力问题而是风险分配问题——合规责任不是技术部门能单独扛住的。7. 写在最后个人经验与后续扩展建议这套方案做下来我最大的收获不是脚本写得多熟练而是真正理解了审计追踪的底层逻辑。一条可信的审计记录不是业务完成后顺便补一下的数据而是操作触发时强制同步生成的事件一套可信的审计系统必须有可靠的时间源、唯一的用户身份绑定、无法悄无声息抹掉的存储结构。做到这三点自制的审计追踪在实战中就站得住脚。最后分享两个实操小建议。第一上线前一定把异常路径全预演一遍签名失败、写库失败、队列文件被占用、磁盘空间不足、计划任务忘了启动这些场景每一样都要真的触发一次看看系统表现。只在顺利路径上跑通在验收时会被现场突发状况打回原形。第二给审计查询界面留一个导出原始数据按钮。审计人员经常要拿数据回办公室做分析与其在WinCC画面上花心思做复杂的统计报表不如直接导出CSV/Excel让他们自己处理。这在验收中反而更容易获得认可。如果后续项目预算允许我会把归档服务改成一个独立的Windows服务而不是计划任务减少被误杀或遗忘启动的风险再把哈希链升级为时间戳哈希双重链进一步强化防抵赖能力。这些扩展不复杂但能明显提升系统的可靠性和说服力。
网站建设高端定制企业官网