影刀RPA 密码安全管理:凭证加密存储与安全调用
影刀RPA 密码安全管理:凭证加密存储与安全调用
作者:林焱
什么情况用
写自动化流程的时候,你不可避免地要把各种密码和Token写在某个地方:
- 登录网站要用账号密码
- 调用API要用API Key
- 连数据库要用数据库密码
- 发邮件要用邮箱授权码
如果你把这些硬编码在流程里,导出分享给别人——密码就跟着走了。更糟糕的是,影刀支持云端存储流程,你把凭证明文写到代码里就上传云端了。
核心场景:流程中需要用到密码/Token,但不能明文写在代码里。
怎么做
第一步:分清安全等级
不是所有凭证都需要同等级的保护:
拼多多店群自动化上架方案
| 安全等级 | 内容 | 存储方案 |
|---|---|---|
| 低 | 测试环境账号、非敏感API Key | 影刀全局变量 |
| 中 | 公司内部系统密码、邮件密码 | AES本地加密文件 |
| 高 | 生产数据库密码、支付密钥 | 密钥管理服务/Windows凭据管理器 |
第二步:利用影刀全局变量(适合低敏感度)
影刀支持设置全局变量,不会导出到分享的流程文件中。
- 打开影刀客户端 → 左侧「全局变量」
- 新增变量,比如
DB_PASSWORD,值填密码 - 在Python节点中读取:
# 影刀全局变量会自动注入到Python节点password=DB_PASSWORDprint(f"连接数据库,密码长度为{len(password)}位")# 不要打印密码本身!局限:变量值在影刀服务端明文存储,不适合真正的敏感信息。所以这只适合测试环境。
第三步:AES本地加密存储(适合中等敏感度)
核心思路:密码加密后存到本地文件,用的时候解密,用完不残留。
importosimportjsonimportbase64fromcryptography.fernetimportFernetfromcryptography.hazmat.primitivesimporthashesfromcryptography.hazmat.primitives.kdf.pbkdf2importPBKDF2HMACclassSecureCredentialManager:""" 凭证安全管理器 基于AES-256加密,主密码+盐值派生密钥 """def__init__(self,vault_path="credentials.enc",master_password=None):""" vault_path: 加密凭证文件路径 master_password: 主密码,不存盘,每次运行输入 """self.vault_path=vault_path# 主密码不存盘,如果没提供就尝试从环境变量读取ifmaster_passwordisNone:master_password=os.environ.get("RPA_MASTER_KEY","default-dev-key")# 从主密码派生加密密钥self.cipher=self._derive_key(master_password)def_derive_key(self,master_password):"""从主密码派生AES密钥"""salt=b"yingdao_rpa_2024"# 固定盐值(生产环境换成随机值)kdf=PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,# 256位salt=salt,iterations=480000,# 迭代次数越高越安全,但也越慢)key=base64.urlsafe_b64encode(kdf.derive(master_password.encode()))returnFernet(key)defstore_credential(self,service_name,username,password,metadata=None):"""存储一个凭证(加密后写文件)"""# 读取现有凭证vault=self._read_vault()# 加密密码encrypted_pwd=self.cipher.encrypt(password.encode()).decode()# 存入vault[service_name]={"username":username,"password_encrypted":encrypted_pwd,"metadata":metadataor{},"updated_at":str(__import__('datetime').datetime.now())}self._write_vault(vault)print(f"已存储凭证:{service_name}->{username}")defget_credential(self,service_name):"""获取凭证——解密后返回"""vault=self._read_vault()ifservice_namenotinvault:raiseKeyError(f"未找到凭证:{service_name}")entry=vault[service_name]# 解密密码decrypted_pwd=self.cipher.decrypt(entry["password_encrypted"].encode()).decode()return{"username":entry["username"],"password":decrypted_pwd,"metadata":entry.get("metadata",{})}deflist_services(self):"""列出所有已存储的服务名(不泄露密码)"""vault=self._read_vault()returnlist(vault.keys())defdelete_credential(self,service_name):"""删除凭证"""vault=self._read_vault()ifservice_nameinvault:delvault[service_name]self._write_vault(vault)print(f"已删除凭证:{service_name}")def_read_vault(self):"""读取凭证文件"""ifnotos.path.exists(self.vault_path):return{}try:withopen(self.vault_path,"r")asf:returnjson.load(f)except(json.JSONDecodeError,IOError):return{}def_write_vault(self,vault):"""写入凭证文件"""withopen(self.vault_path,"w")asf:json.dump(vault,f,indent=2,ensure_ascii=False)# 限制文件权限(仅当前用户可读写)os.chmod(self.vault_path,0o600)# ===== 使用示例 =====# 初始化——主密码建议从环境变量或手动输入获取cm=SecureCredentialManager(vault_path="D:/RPA_Secrets/credentials.enc",master_password="MyS3cur3M4st3rP@ss")# 存储凭证cm.store_credential(service_name="公司邮箱",username="lin.yan@company.com",password="email_app_password_123",metadata={"smtp_server":"smtp.company.com","port":587})cm.store_credential(service_name="数据库_WMS",username="rpa_robot",password="DbP@ssw0rd2024!",metadata={"host":"192.168.1.100","database":"wms_prod"})# 使用时获取cred=cm.get_credential("公司邮箱")print(f"邮箱登录:{cred['username']}")# 注意:不要在日志里打印密码!# 直接传给SMTP登录函数即可第四步:Python节点中使用——解密即用即弃
# ===== 影刀Python节点:安全的数据库连接 =====importpymysql# 初始化凭证管理器(主密码从影刀全局变量读取)cm=SecureCredentialManager(vault_path="D:/RPA_Secrets/credentials.enc",master_password=MASTER_KEY# 影刀全局变量)# 获取数据库凭证cred=cm.get_credential("数据库_WMS")# 建立连接——密码存在变量中,用完最好不打印conn=pymysql.connect(host=cred["metadata"]["host"],user=cred["username"],password=cred["password"],# 这里用完就完database=cred["metadata"]["database"],charset="utf8mb4")# 执行业务逻辑cursor=conn.cursor()cursor.execute("SELECT COUNT(*) FROM orders WHERE status='pending'")count=cursor.fetchone()[0]print(f"待处理订单:{count}")# 清理——密码变量不要残留在内存中delcred conn.close()第五步:更安全的选择——Windows凭据管理器
如果你的流程只跑在Windows上,可以直接调用系统的凭据管理器,安全性更高(由操作系统保护)。
""" 需要安装:pip install keyring """importkeyring# 存储凭证(只需执行一次)keyring.set_password("yingdao_rpa","公司邮箱","email_app_password_123")keyring.set_password("yingdao_rpa","数据库_WMS","DbP@ssw0rd2024!")# 使用时获取password=keyring.get_password("yingdao_rpa","数据库_WMS")print(f"密码已从Windows凭据管理器读取,长度={len(password)}")# 列出所有服务名importkeyring.backends.Windowsprint(keyring.get_credential("yingdao_rpa","公司邮箱"))Windows凭据管理器可以在「控制面板 → 凭据管理器 → Windows凭据」中手动查看和管理,是生产环境的推荐方案。
有什么坑
坑1:主密码丢失 → 所有凭证报废
AES加密是单向的——主密码丢了,所有加密的凭证就无法解密了。没有后门,没有恢复机制。
踩坑实录:把主密码写在一个txt文件里,换电脑时忘了备份,三周后需要改流程,发现所有凭证都解不开了,只能挨个重置密码。
解决方法:主密码至少备份在两个独立的地方(比如1Password + 纸质记录)。生产环境用keyring的Windows凭据管理器,没有主密码的问题。
坑2:影刀全局变量 ≠ 加密
影刀的全局变量是「不导出到分享文件」,不是「加密存储」。在影刀服务端可能是明文存的。别把真正的生产密码放里面。
TEMU店群如何管理运营?
正确姿势:影刀全局变量只放主密码的引用(比如密码文件路径),不放密码本身。
坑3:日志/截图泄密
即使密码没写在代码里,但调试时print(password)打出密码,或者截图时刚好截到了密码框,一样泄露。
解决方法:
- 生产流程严禁打印密码变量
- 调试完成后删除所有
print(password)语句 - 截图前确认没有敏感信息在屏幕上
坑4:keyring跨平台兼容性
在Windows上用keyring没问题(调用Windows凭据管理器),但如果你把这个流程移到Mac上跑,keyring的后端就变了(Mac用Keychain),行为可能不一致。
解决方法:确定运行平台后再选方案。Windows用keyring或直接调win32cred,Linux可以考虑secretstorage。
坑5:版本控制中的凭证泄露
即使你没把凭证写在代码里,但如果把credentials.enc文件不小心提交到Git了,虽然加密了,但如果主密码很弱(比如123456),一样能被暴力破解。
解决方法:把.enc、.key、包含密码的配置文件都加入.gitignore。
总结:密码安全的铁律——永远不要明文写在代码里。轻量方案用AES本地加密,生产环境用Windows凭据管理器。关键是形成习惯:每个需要密码的地方,第一反应是「从凭证管理器拿」,而不是「先写死回头再改」。