导出混淆是通过精准控制模块可见性来防御攻击,而非加密或重命名;核心是明确公私接口边界、剥离敏感逻辑到未导出模块、禁用全量/类型导出、移除调试导出项。

导出混淆不是把代码变乱,而是通过精准控制“谁能看到什么”,在模块边界上筑起第一道防线。它不加密、不重命名变量,但能有效阻止攻击者从外部直接触达关键逻辑入口。
明确模块的公共接口与内部实现边界
核心思路是只暴露真正需要被调用的函数或类,其余全部设为私有。比如一个支付模块,对外只需提供 initPayment() 和 confirmTransaction() 两个方法,而签名生成、密钥派生、回调验签等全部封装在内部不可导出的函数中。
- Go 项目中,确保所有内部工具函数首字母小写(如 signPayload()),仅将业务入口函数大写(如 ProcessOrder())
- TypeScript 项目中,不加 export 的函数、类型、常量默认无法被其他文件导入;避免使用 export * 全量导出
- Rust 项目中,用 pub(crate) 限制为本 crate 可见,而非 pub 全局可见
剥离敏感逻辑到未导出的辅助模块
把核心算法、密钥处理、风控判断等拆进独立的、不对外暴露的子模块。主模块只负责协调流程,不持有敏感实现。
- 例如创建 internal/crypto/ 目录,其中的 AesHelper 类不导出,仅由同包内 PaymentService 调用
- 在构建配置中排除该目录的类型声明输出(如 TypeScript 的 exclude 字段、Rust 的 #[doc(hidden)])
- 确保打包产物(如 npm 包、aar、jar)里不包含这些内部模块的声明文件或 class 文件
用默认导出替代命名导出,隐藏成员结构
命名导出会暴露模块内有哪些函数/类,攻击者可通过 import * as X from 'module' 快速枚举全部能力。默认导出则强制调用方必须了解具体使用方式,增加静态分析成本。
- 将主业务类设为 export default class OrderProcessor,而不是 export class OrderProcessor
- 配套导出一个工厂函数 export default createPaymentClient(),而非导出多个构造参数和配置类
- 禁用 export type 暴露内部类型,改用运行时校验或泛型约束传递契约
配合构建时裁剪,移除调试用导出项
开发阶段可能导出一些调试函数(如 dumpState()、triggerMockFlow()),上线前必须彻底清除。
- 在 Webpack/Vite 构建配置中,用 define 注入 process.env.NODE_ENV === 'production',条件编译掉调试导出
- 对 Java/Kotlin 项目,在 ProGuard 或 R8 规则中添加 -assumenosideeffects class **.DebugUtils { public static *** dump*(...); },让编译器直接删掉调用
- Flutter/Dart 项目启用 tree-shaking 并设置 dart2js --omit-implicit-checks,避免未引用的导出成员残留

















