Python 实现 PBKDF2 + AES-256-GCM 加密存储
PBKDF2 + AES-GCM 是本地加密存储(钱包 vault、配置文件保护)的标准方案:密码经 PBKDF2 派生出密钥,AES-GCM 提供加密和完整性验证。 加密流程 Password ↓ PBKDF2-HMAC-SHA512(iterations=600000, salt=随机16字节) ↓ 32 字节 AES Key ↓ AES-256-GCM(nonce=随机16字节) ↓ Ciphertext + Tag安装依赖 pip install pycryptodome完整实现 import json import os import base64 from hashlib import pbkdf2_hmac from Crypto.Cipher import AESPBKDF2_ITERATIONS = 600_000 # NIST 2023 推荐最低值def derive_key(password: str, salt: bytes) -> bytes: return pbkdf2_hmac( "sha512", password.encode("utf-8"), salt, PBKDF2_ITERATIONS, dklen=32, # AES-256 )def encrypt(password: str, plaintext: str) -> dict: salt = os.urandom(16) nonce = os.urandom(16) key = derive_key(password, salt) cipher = AES.new(key, AES.MODE_GCM, nonce=nonce) ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode("utf-8")) return { "salt": base64.b64encode(salt).decode(), "nonce": base64.b64encode(nonce).decode(), "ciphertext": base64.b64encode(ciphertext).decode(), "tag": base64.b64encode(tag).decode(), }def decrypt(password: str, vault: dict) -> str: salt = base64.b64decode(vault["salt"]) nonce = base64.b64decode(vault["nonce"]) ciphertext = base64.b64decode(vault["ciphertext"]) tag = base64.b64decode(vault["tag"]) key = derive_key(password, salt) cipher = AES.new(key, AES.MODE_GCM, nonce=nonce) plaintext = cipher.decrypt_and_verify(ciphertext, tag) return plaintext.decode("utf-8")使用示例 # 加密 secret = '{"privateKey": "0xabcd..."}' vault = encrypt("my_strong_password", secret)# 存储为 JSON with open("vault.json", "w") as f: json.dump(vault, f, indent=2)# 解密 with open("vault.json") as f: vault = json.load(f)plaintext = decrypt("my_strong_password", vault) print(plaintext)vault.json 格式: { "salt": "base64...", "nonce": "base64...", "ciphertext": "base64...", "tag": "base64..." }为什么选 AES-GCM 而非 CBC模式 加密 完整性验证 推荐AES-CBC ✅ ❌(需额外 HMAC) 不推荐新项目AES-GCM ✅ ✅(内置 Tag) ✅ 推���AES-CTR ✅ ❌ 需配合 HMACGCM 模式同时提供加密和认证,decrypt_and_verify 会在解密前验证 Tag,密文被篡改时抛出 ValueError。 关键参数说明 PBKDF2 迭代次数:越高越安全,NIST 2023 建议 SHA-512 至少 210,000 次,MetaMask 等钱包用 600,000 次。代价是派生速度变慢(约 0.5~2 秒),对正常用户不感知,但让暴力破解成本提高数十万倍。 salt 唯一性:每次加密生成新的随机 salt,防止相同密码产生相同密钥(彩虹表攻击)。salt 不需要保密,公开存储即可。 nonce 唯一性:GCM 的 nonce 绝对不能重用于同一密钥,否则 GCM 的安全性完全崩溃。每次加密随机生成是最安全的做法。 密码错误时的行为 try: plaintext = decrypt("wrong_password", vault) except ValueError: print("密码错误或数据被篡改")decrypt_and_verify 验证 Tag 失败时抛出 ValueError,不会泄漏任何明文信息。
Permissions Policy Violation: unload is not allowed 是什么
Chrome 控制台偶尔出现: [Violation] Permissions policy violation: unload is not allowed in this document.不是代码报错,是一条警告。 原因 Chrome 正在逐步废弃 unload 事件,因为注册了 unload 的页面无法进入 BFCache(Back/Forward Cache),会降低导航性能。某些页面策略或 iframe 设置下,unload 会被直接禁止,此时任何注册 unload 的代码都会打印这条 Violation。 常见触发场景:第三方 SDK(Sentry、埋点、广告脚本)注册了 unload 旧版 Citrix/Enterprise 插件 页面在 <iframe> 里,父页面设置了 Permissions-Policy: unload=()是否影响功能 通常不影响。[Violation] 只是警告,页面照常运行,unload 回调不会执行而已。如果你的业务逻辑依赖 unload(如发送离开事件),需要改用 visibilitychange 或 pagehide。 查看当前页面的 Permissions Policy 方法一:Network 响应头 打开 DevTools → Network,刷新页面,点击 HTML 文档请求 → Response Headers,查找: Permissions-Policy: unload=()方法二:控制台查询 // 查看 unload 是否被策略禁止 document.permissionsPolicy?.allowsFeature("unload")// 查看所有允许的特性 document.permissionsPolicy.allowedFeatures()// 抓取当前页面响应头 fetch(location.href) .then(r => console.log(r.headers.get("Permissions-Policy")))定位是谁注册了 unload // 查看 window 上注册了哪些 unload 监听器 getEventListeners(window).unload// 查看 beforeunload getEventListeners(window).beforeunload如果有结果,点击 listener 里的函数名可以跳转到 Sources 对应代码。 也可以在 Sources 全局搜索: addEventListener("unload" window.onunload beforeunload推荐替代方案 // 替代 unload:页面对用户不可见时触发 document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") { // 发送最后的数据 navigator.sendBeacon("/api/leave", payload); } });// 替代 beforeunload(不阻塞 BFCache) window.addEventListener("pagehide", (e) => { if (e.persisted) { // 页面进入 BFCache,不是真正关闭 } });navigator.sendBeacon 在页面隐藏时异步发送请求,不阻塞页面关闭,是发送离开事件的首选方案。 Chrome Extension 的本地存储 Chrome 扩展用 chrome.storage.local 存储的数据保存在: Windows: %LOCALAPPDATA%\Google\Chrome\User Data\Default\Local Extension Settings\<扩展ID> macOS: ~/Library/Application Support/Google/Chrome/Default/Local Extension Settings/<扩展ID>目录内容是 LevelDB 格式(.log、.ldb 文件),不是纯文本。查看方法: # 在扩展页面控制台读取(需要 Inspect views 权限) chrome.storage.local.get(null, console.log)或用 Python plyvel 解析: import plyveldb = plyvel.DB("/path/to/extension/storage", create_if_missing=False) for k, v in db: print(k.decode(errors="replace")) print(v.decode(errors="replace")) db.close()注意:读取时浏览器需要关闭,否则 LevelDB 文件被锁。
格式化后文件仍存在:USB Mass Storage Gadget 只读模式排查
现象描述 USB Mass Storage Gadget 格式化完后:旧文件还在 可以创建新文件、写入数据 删除文件提示成功,重插后文件又出现原因分类 1. 存储设备进入只读模式 U 盘、TF 卡、eMMC 出现坏块时控制器会自动切换为 Read-Only。表现为删除"成功"但重新挂载后文件复原。 Linux 排查: dmesg | grep -i readonly dmesg | grep -i "write protect"出现以下字样即确认只读: Write Protect is on switching to read-only mode mmcblk0: read only2. Gadget 镜像文件损坏 如果通过镜像文件方式挂载(echo /data/usb.img > lun/file),镜像内 FAT/exFAT 文件系统可能损坏: # 检查镜像文件系统 fsck.vfat usb.img # 或 dosfsck usb.img3. Windows 快速格式化未重建文件系统 Windows "快速格式化"只重写 FAT 表,不实际清除旧数据,旧文件仍可被恢复工具读取。 彻底解决方式: 磁盘管理 → 删除分区 → 新建分区 → 完整格式化(取消勾选"快速格式化")4. OverlayFS 层隔离(Android Root 环境) TrickyStore、KernelSU、Magisk 等工具可能挂载了 OverlayFS,删除的是上层(upperdir),底层(lowerdir)文件仍然存在,重挂载后又出现。 mount | grep overlay如有输出,需要先卸载 overlay 再操作底层文件系统。 5. NAND Flash 已损坏 最符合"能写入但删除无效"的情形:控制器接收了写入缓存但没有真正落盘,重插后旧数据回来。 验证方式: touch test.txt && sync rm test.txt && sync umount /dev/sdX && mount /dev/sdX /mnt ls /mnt/test.txt # 如果文件还在,基本确认存储介质损坏修复方法 U 盘 / TF 卡(Linux) # 用 diskpart 或 dd 低格 sudo dd if=/dev/zero of=/dev/sdX bs=1M status=progressU 盘 / TF 卡(Windows) 以管理员运行 CMD: diskpart list disk select disk N clean create partition primary format fs=fat32 quick assign exitAndroid/Linux 开发板 eMMC eMMC 只读后一般无法软件修复,需要更换存储模块或使用厂商专用刷机工具重新初始化 flash 控制器。 快速定位表现象 最可能原因删除后重插文件回来 NAND 只读 / OverlayFS格式化后旧文件可见但访问慢 快速格式化(未清除)fsck 报错 镜像文件 FAT 损坏dmesg 有 write protect 存储介质已进入只读保护
Citrix Gateway 三种模式:Full VPN / Clientless / ICA Proxy 怎么区分
在公司里用 Citrix Gateway(以前叫 NetScaler Gateway)远程办公,能不能 ping 内网、能不能 mstsc 到某台内网机器——完全取决于管理员开的是哪种模式。三种模式差别很大: 三种模式 1. Full VPN(完整 VPN) 登录 Gateway 后建立 VPN 隧道,你的电脑相当于接进了公司内网。 能做的: ping 10.0.0.1 mstsc 10.0.0.100 \\10.0.0.50\share内网 API、SMB 共享、RDP 都能直接连。最接近传统 VPN。 2. Clientless VPN(无客户端 VPN) 只能通过 Gateway 的网页入口访问管理员发布过的内网网站: https://gateway.company.com/vpn/index.html ├─ OA 系统 ├─ JIRA ├─ GitLab └─ Exchange OWAGateway 帮你反向代理到内网。只有网页可访问——ping / RDP / SMB 全都不行。 3. ICA Proxy(最常见) 登录 Gateway 后看到的不是网页应用列表,而是远程桌面 / 应用: [Windows 桌面] [SAP] [Outlook] [Chrome]点击后:浏览器下载 .ica 文件 Citrix Workspace 客户端打开 连到内网一台服务器上运行你本机没进内网——只是远程操作一台内网机器。所有操作都在那台远端上完成,本机能看到的只是画面像素。 怎么判断自己是哪种 登录 Gateway 之后: 方法 1:看网卡 ipconfigFull VPN 会多出: 以太网适配器 Citrix Secure Access: 以太网适配器 Citrix VPN Adapter:看到就是 Full VPN。 方法 2:看路由 route printFull VPN 里有内网网段被路由到 Citrix 虚拟网卡: 10.0.0.0 255.0.0.0 10.0.0.1 <Citrix 网卡 IP>方法 3:直接 ping 内网 ping 10.x.x.x telnet 10.x.x.x 3389通 = Full VPN,不通 = Clientless 或 ICA Proxy。 方法 4:登录后看到什么 登录 Gateway 网页后:看到 大概率是一堆网页链接 Clientless VPN桌面 / 应用图标(点了下 ica) ICA Proxy什么都没看到,只有 VPN 状态 Full VPNICA Proxy 想访问其它内网资源 基本上不行。ICA Proxy 的设计初衷就是"给远程用户桌面 / 应用",不给完整内网访问权限。安全模型里这是"最小暴露面"的选择。 想访问需要管理员:开启 Full VPN 策略(Citrix Secure Access Client) 或者把你需要的服务发布成 Clientless 应用 或者给你专门开一个 RDP 会话,你从那台机器上操作传大文件的坑 ICA Proxy 模式下想把本机文件传到远端会话:Citrix Workspace 支持"客户端驱动映射",可以把本地磁盘挂进远端会话,管理员可能禁用 剪贴板:可能双向、可能单向、可能禁用,也是策略控制的 上传下载按钮:Citrix Workspace 本身有,但公司常常禁真被卡住只能靠在远端邮箱收发、通过 WebOA 上传中转等等。或者直接找 IT 开权限。 Full VPN 的坑 即使有 Full VPN 也不等于什么都能访问:分段路由:管理员可能只给部分内网网段路由,比如只给你 10.10.0.0/16,其它照样不通 应用层过滤:Gateway 可以按端口 / 协议限制,明明有路由但 RDP 不通 DNS:需要用公司内 DNS,否则内网域名解析不了route print 看到给了哪些网段,就只能访问那些网段。 一句话总结 Citrix Gateway 三种模式—— Full VPN 拿到内网路由、Clientless 只发布网页、ICA Proxy 只是远端桌面像素。看 ipconfig 有没有 Citrix 网卡最快分辨。想要更多权限,只能找管理员改策略。
AI 控制浏览器的四种方案:插件 / Playwright / CDP / Browser Use
"AI 控制浏览器"目前有四类方案,权限差异非常大。做爬虫 / 自动化 / Agent 时选错方案会撞墙——比如插件方案下想调试 JS 是不可能的。 权限矩阵方案 操作 DOM 读 DOM 执行 JS 调试 JS Chrome DevTools 全权限 保留登录态Chrome 扩展 ✅ ✅ ✅ ❌ ❌ ✅Playwright / Puppeteer ✅ ✅ ✅ 部分 ❌ 需配置Chrome DevTools Protocol (CDP) ✅ ✅ ✅ ✅ ✅ ✅Browser Use / Open Operator ✅ ✅ ✅ 部分 部分 ✅方案一:Chrome 扩展 适合:日常使用中的 AI 助手(Sider、Harpa、Monica 等)、需要长期挂在浏览器里的工具。 做法:写一个 Chrome/Edge 扩展,通过 content script 读写 DOM: // content.js document.querySelector("input.search").value = "hello"; document.querySelector("button.submit").click();或者用扩展 API 拿到当前 tab 的 DOM 快照喂给 LLM: chrome.tabs.sendMessage(tabId, { type: "getDOM" }, response => { callLLM(response.html); });局限:无法调试 JS(没 DevTools 权限) 无法访问 Network / Sources / Performance 某些反爬网站可能识别扩展环境方案二:Playwright / Puppeteer 适合:爬虫、自动化测试、CI 里跑的一次性任务。 from playwright.sync_api import sync_playwrightwith sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("https://example.com") # 读 DOM html = page.content() # 执行 JS result = page.evaluate("() => document.title") # 点击 page.click("button.submit")配合 LLM:Playwright 抓页面 → 传 DOM 给模型 → 模型返回下一步操作 → Playwright 执行。 局限:每次启动是全新 profile,没登录态(除非 launch_persistent_context) 反自动化检测(Cloudflare、Datadome 之类)会挂 网站升级 selector 就崩有个绕检测的插件叫 playwright-stealth,能过大多数入门反爬。 方案三:Chrome DevTools Protocol(权限最高) 适合:需要完整 DevTools 能力的场景——JS 调试、Network 拦截、Performance profiling、逆向分析。 先以 debug 模式启动 Chrome: chrome.exe --remote-debugging-port=9222 --user-data-dir="D:\chrome-cdp"然后 Playwright 连过去(这里 Playwright 是 CDP 的高层封装): browser = p.chromium.connect_over_cdp("http://127.0.0.1:9222")或者直接用底层 CDP: from pychrome import Browser b = Browser(url="http://127.0.0.1:9222") tab = b.list_tab()[0] tab.start() tab.Runtime.evaluate(expression="document.title")# 拦截 Network tab.Network.enable() tab.call_method("Fetch.enable")# 设置断点 tab.Debugger.enable() tab.Debugger.setBreakpointByUrl(url=".../app.js", lineNumber=100)能力和你按 F12 一样。 方案四:Browser Use / Open Operator 类项目 近一两年新出的一批"给 LLM 用的浏览器 SDK"——比如 browser-use、Anthropic 的 Computer Use、OpenAI Operator: from browser_use import Agent from langchain_openai import ChatOpenAIagent = Agent( task="打开 GitHub 搜索 langchain 项目的最新 star 数", llm=ChatOpenAI(model="gpt-4o"), ) result = agent.run()这类 SDK 帮你封装好:页面截图 + 元素编号 Vision 模型选元素 Playwright 执行操作 失败自动重试写业务代码不用管 selector,直接说自然语言。代价:慢(每步 LLM 调用)、贵(大量 vision token)、有时候错。 怎么选场景 推荐用户浏览时给 AI 助手能力 Chrome 扩展定时任务 / CI 里跑 Playwright需要 JS 断点、Network 拦截、逆向 CDP想让 LLM 自主完成复杂多步任务 Browser Use 类反爬严的网站 CDP + stealth + 真实 profile一句话总结 日常插件、爬虫 Playwright、逆向调试 CDP、Agent 用 Browser Use——四种方案权限从低到高,按需要选。CDP 是"能上 F12 就能做的一切"的上限。
