影刀RPA 流程合规检查清单:上线前的安全检查项

0 阅读7分钟

影刀RPA 流程合规检查清单:上线前的安全检查项

作者:林焱


什么情况用

写完一个流程,迫不及待想上线跑——但你真的确认它安全吗?

在这里插入图片描述

我见过太多这样的场景:流程在开发机上跑得好好的,上线第二天就出事了——不小心把生产数据库的订单表删了、批量发邮件时抄送了不该抄送的人、流程卡在一个弹窗上等了6小时直到超时。

流程上线和代码上线一样,需要走一遍安全检查清单。

核心场景:流程开发完成,准备上线运行前,逐一检查安全性、稳定性和合规性。


怎么做

第一步:安全检查清单

在这里插入图片描述

按这个顺序逐项打勾,别跳:

□ 1. 凭证明文检查
□ 2. 文件路径检查
□ 3. 网络请求检查
□ 4. 异常处理检查
□ 5. 数据安全与隐私检查
□ 6. 资源消耗检查
□ 7. 流程中止条件检查
□ 8. 日志与监控检查
□ 9. 版本与备份检查
□ 10. 灰度测试确认

第二步:逐项详解

1. 凭证明文检查
# ❌ 上线前必须清理的代码
API_KEY = "sk-abc123xyz456"
DB_PASSWORD = "MyP@ssw0rd!"
email_password = "app_pass_123"

# ✅ 正确做法

[video(video-8mL3e7it-1784481802357)(type-csdn)(url-https://live.csdn.net/v/embed/526818)(image-https://v-blog.csdnimg.cn/asset/582d14c3bd0451c5399cd990b56e2a0d/cover/Cover0.jpg)(title-拼多多店群自动化报活动上架!)]

import os
API_KEY = os.environ.get("RPA_API_KEY")
# 或从凭证管理器获取

检查方法:在代码里搜索 passwordsecrettokenkeyapi,确认没有硬编码的凭证。

在这里插入图片描述

2. 文件路径检查
# ❌ 绝对路径(换机器就跑不了)
file_path = "C:/Users/Administrator/Desktop/data.csv"

# ✅ 使用相对路径或配置化路径
file_path = os.path.join(RPA_DATA_DIR, "data.csv")

# ❌ 硬编码的系统临时目录
temp_dir = "C:/Windows/Temp"

# ✅ 用系统API获取
import tempfile
temp_dir = tempfile.gettempdir()

关键检查点

  • 输入文件路径是否正确(测试数据可能和生产数据不在同一位置)
  • 输出文件路径是否会在生产环境产生权限问题
  • 文件名是否包含时间戳(防止覆盖历史结果)
3. 网络请求检查
# ❌ 没有超时的请求
resp = requests.get("https://api.example.com/data")

# ✅ 必须设置超时
resp = requests.get("https://api.example.com/data", timeout=30)

# ❌ 没有重试机制
# ✅ 至少要处理网络异常
try:
![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/c2bf5fa106434936a247f701117bb95a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5p6X54SxX1JQQUFJ:q75.awebp?rk3s=f64ab15b&x-expires=1786209526&x-signature=pZVmbHbSHcK7VYMytU2IFH7Yb54%3D)

    resp = requests.post(url, json=data, timeout=30)
    resp.raise_for_status()
except requests.exceptions.Timeout:
    print(f"请求超时:{url}")
    # 记录到失败队列
except requests.exceptions.RequestException as e:
    print(f"请求失败:{e}")

检查清单

  • 所有网络请求设置了timeout
  • 关键请求有重试逻辑
  • 处理了可能的网络异常
  • 频率限制(QPS)不会超过服务端限制
4. 异常处理检查
# ❌ 裸奔的代码——出错了流程直接挂
data = get_data_from_web()
process_data(data)
save_to_database(data)

# ✅ 必须要有异常处理
try:
    data = get_data_from_web()
    if not data:
        raise ValueError("获取数据为空")
    
    try:
        result = process_data(data)
    except Exception as e:
        # 处理逻辑出错时,原始数据别丢
        backup_raw_data(data, f"failed_{timestamp}.json")
        raise
    
    save_to_database(result)
    
except Exception as e:
    # 至少通知运维
    send_alert(f"流程异常:{e}")
    # 记录详细日志
    log_error(e, traceback.format_exc())
5. 数据安全与隐私检查
□ 导出的CSV/Excel中是否包含未经脱敏的个人信息
□ 日志文件是否会打印完整手机号/身份证号
□ 截图是否可能包含敏感信息
□ 邮件收件人列表是否正确(别把测试列表带到生产)
□ 数据库操作是否有限制条件(别忘记WHERE子句)

特别注意DELETE和UPDATE操作

在这里插入图片描述

# ❌ 极其危险——没有WHERE条件
cursor.execute("DELETE FROM orders")

# ✅ 必须有限制条件,且限制条件来自明确的变量
cursor.execute("DELETE FROM orders WHERE status='cancelled' AND created_at < ?", 
               (threshold_date,))

# ✅ 危险操作前先SELECT确认影响范围
cursor.execute("SELECT COUNT(*) FROM orders WHERE status='cancelled'")
count = cursor.fetchone()[0]
print(f"将删除 {count} 条记录,确认后执行")
6. 资源消耗检查
□ 内存使用:大文件是否分块处理而不是一次性加载
□ 磁盘空间:输出文件是否会无限增长
□ CPU占用:是否有死循环
□ 数据库连接:是否用完就关闭(不是等垃圾回收)
□ 临时文件:是否及时清理
# ❌ 一次性加载大文件到内存
df = pd.read_csv("huge_file.csv")  # 2GB的CSV直接爆内存

# ✅ 分块处理
chunk_size = 10000
for chunk in pd.read_csv("huge_file.csv", chunksize=chunk_size):
    process(chunk)  # 每次只处理10000行
7. 流程中止条件检查
□ 是否有可能无限循环?(while True必须有明确的退出条件)
□ 是否有超时机制?(整个流程最长跑多久)
□ 是否有手动中止的退出点?
![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/9812c048991c4f52b698f888ceb4b8a3~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5p6X54SxX1JQQUFJ:q75.awebp?rk3s=f64ab15b&x-expires=1786209526&x-signature=9U9gAXSBBnNvodyhBKnl%2Blannhs%3D)

□ 是否有条件分支处理了「数据为空」的情况?
8. 日志与监控检查
□ 运行是否记录了日志(时间、输入、输出、错误)
□ 失败时是否有人收到通知(企微/钉钉/邮件)
□ 是否方便事后排查问题(日志里有足够的上下文)
□ 日志文件是否会无限增长(是否设置了轮转或定期清理)
9. 版本与备份检查
□ 是否保存了上一个能正常运行的版本
□ 本次修改是否有变更记录(改了哪里、为什么改)
□ 能否一键回滚到上一个版本
□ 输入数据是否有备份(处理失败能重新来)
10. 灰度测试确认

在上全量之前,先小规模验证:

# 灰度测试模式
TEST_MODE = True  # 上线前改为False
TEST_LIMIT = 10   # 只处理前10条

def process_items(items):
    if TEST_MODE:
        items = items[:TEST_LIMIT]
        print(f"灰度模式:只处理前{TEST_LIMIT}条")
        ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4cf370ab5000421190531ffa6c0915c7~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5p6X54SxX1JQQUFJ:q75.awebp?rk3s=f64ab15b&x-expires=1786209526&x-signature=jIwHtM%2F732OwitgSUNAashyOQ%2Fw%3D)

    
    for item in items:
        process(item)
    
    if TEST_MODE:
        print("灰度测试完成,确认结果无误后再关闭TEST_MODE")

第三步:快速自查脚本

写一个脚本自动扫描代码:

import os
import re

class SecurityScanner:
    """流程安全检查扫描器"""
    
    # 危险模式
    DANGER_PATTERNS = [
        (r'(password|passwd|pwd|secret|token|api_key)\s*=\s*["\'][^"\']+["\']', 
         "硬编码凭证"),
        (r'DELETE\s+FROM\s+\w+\s*$', 
         "危险的DELETE语句(缺少WHERE条件)"),
        (r'UPDATE\s+\w+\s+SET\s+.*\s*$', 
         "危险的UPDATE语句(可能缺少WHERE条件)"),
        (r'requests\.(get|post|put|delete)\([^)]*\)(?!.*timeout)', 
         "网络请求缺少timeout参数"),
        (r'while\s+True', 
         "无限循环(检查是否有退出条件)"),
    ]
    
    @classmethod
    def scan_file(cls, filepath):
        """扫描单个文件"""
        if not filepath.endswith(('.py', '.txt', '.md')):
            return []
        
        issues = []
        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()
        
        for pattern, description in cls.DANGER_PATTERNS:
            matches = re.finditer(pattern, content, re.IGNORECASE)
            for m in matches:
                # 计算行号
                line_num = content[:m.start()].count('\n') + 1
                issues.append({
                    "file": filepath,
                    "line": line_num,
                    "issue": description,
                    "snippet": m.group()[:80]
                })
        
        return issues
    
    @classmethod
    def scan_folder(cls, folder_path):
        """扫描整个文件夹"""
        all_issues = []
        for root, dirs, files in os.walk(folder_path):
            for f in files:
                filepath = os.path.join(root, f)
                issues = cls.scan_file(filepath)
                all_issues.extend(issues)
        
        # 生成报告
        print(f"\n===== 安全检查报告 =====")
        print(f"扫描文件数:扫描完成")
        print(f"发现问题:{len(all_issues)} 个\n")
        
        for issue in all_issues:
            print(f"[{issue['issue']}]")
            print(f"  位置:{issue['file']}:{issue['line']}")
            print(f"  代码:{issue['snippet']}")
            print()
        
        return all_issues

# 使用
# SecurityScanner.scan_folder("./my_rpa_scripts")

有什么坑

[video(video-OmvMMhHH-1784481809073)(type-csdn)(url-live.csdn.net/v/embed/526…)]

坑1:自以为简单的流程不需要检查

在这里插入图片描述

「就是个数据导出工具,哪会出问题?」——然后导出了生产数据库的全部客户信息,发到了一个外网邮箱。

原则:所有上线流程都要走一遍清单,哪怕是「只读不写」的流程也有隐私风险。

坑2:测试环境和生产环境混用

测试时连接的是测试数据库,上线时忘记改配置——流程连上了生产库,用测试逻辑去处理生产数据。

解决方法:环境配置独立管理,测试和生产用不同的配置文件、不同的凭证、不同的输出目录。

坑3:安全检查只在首次上线时做

在这里插入图片描述

流程迭代改了几个地方,觉得改动小不用再检查——然后一个新增的API调用没加timeout,半夜超时把整个流程卡死了。

解决方法:每次修改流程后,至少检查修改涉及的部分。重大版本更新走完整清单。

坑4:忽视操作系统的权限问题

在管理员权限下开发流程,流程可以随便读写文件。上线后用普通用户跑,结果 Permission denied

解决方法:在和生产环境相同权限的账号下做最终测试。

坑5:流程回滚的代价被低估

在这里插入图片描述

没有保留上一个版本,新版本出问题只能临时修——修的过程中业务就断了。

解决方法:流程文件用Git管理,每个版本打tag。出问题 git checkout v1.2.3 就能切回去。


总结:上线前的安全检查不是形式主义,是无数踩坑经验的总结。走完这个清单能挡住90%的生产事故。剩下的10%,靠日志和监控及时发现问题。