导出包的定向性本质是切断非授权访问路径,通过语言级封装(如go小写变量、ts未export声明、rust pub(crate))、默认导出收口、构建时剔除调试变量、禁止类型导出及敏感字段掩码等机制实现。

导出包的定向性不是让变量“更难找”,而是让变量“根本不可见”——关键不在隐藏,而在切断非授权访问路径。它通过语言原生可见性规则和构建时裁剪,把模块间调用从“开放集市”变成“预约通道”。
明确公共接口与私有实现的物理分界
变量能否被跨模块调用,取决于它是否出现在模块的“导出清单”里,而不是有没有被声明或使用。真正起作用的是语言级封装机制:
- Go 中所有小写首字母的变量(如 cacheTimeout、defaultConfig)天然不可导出,即使被同包内函数引用,外部包 import 后也完全无法访问
- TypeScript 中未加 export 的 const/let/变量,默认仅文件内有效;即使用了 export default 导出一个对象,其内部属性也不会自动暴露
- Rust 中用 pub(crate) 声明的变量,只对当前 crate 可见,连子模块若在不同 crate 就拿不到
用默认导出收口,避免命名导出暴露结构
命名导出(export const API_KEY)相当于把钥匙挂门口,攻击者用 import * as X from 'pkg' 就能枚举全部变量。默认导出则强制调用方必须知道具体用法:
- 把配置变量打包进一个工厂函数:export default () => ({ timeout: 5000, retries: 3 }),外部无法直接解构取值
- 导出一个只读配置对象:export default Object.freeze({ baseUrl: 'https://' }),防止运行时篡改
- 不导出原始常量,改导出带校验的访问器:export default { getApiKey() { return process.env.NODE_ENV === 'production' ? _key : null; } }
构建阶段主动剔除调试与临时变量
开发期为方便调试导出的变量(如 export const __DEBUG_STATE),上线后就是安全隐患。不能靠“人工删”,要靠构建链自动清理:
- Webpack/Vite 中用 define 配合条件编译:process.env.DEBUG === 'false' 时,整个 if (DEBUG) { export const dumpCache = ... } 块被 Tree-shaking 移除
- TypeScript 项目在 tsconfig.json 的 exclude 字段加入 debug/*.ts,确保类型声明不打进 npm 包
- Java/Kotlin 使用 R8 规则:-assumenosideeffects class **.DebugVars { public static *** get*(); },直接抹掉调用字节码
禁止类型导出,用运行时契约替代静态暴露
导出 export type ConfigSchema 看似无害,实则泄露了内部字段名、嵌套结构甚至敏感键名(如 secretKey)。应转向运行时约束:
- 不导出类型,只导出校验函数:export function isValidConfig(x: unknown): x is ConfigInstance
- 用泛型隐式传递结构:export function createClient
>(config: T) ,调用方无需知道 T 具体长什么样 - 对必须透出的配置,提供带掩码的 toString() 或 toJSON() 方法,敏感字段统一显示为 "***"










