ECC Dart/Flutter 安全规则指南:从密钥管理到构建混淆的移动端安全实践
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
本指南基于 ECC 仓库中 docs/ja-JP/rules/dart/security.md(与 rules/dart/security.md 同源)整理扩充而成,面向使用 Dart/Flutter 构建移动应用的开发者与 AI 编码代理。文章完整继承原规则文件的全部要点,并结合 ECC 规则体系的分层结构与通用安全基线,补充了底层实现依据与落地操作细节。读完本文,你将掌握一套覆盖密钥管理、网络安全、输入验证、数据保护、双平台(Android/iOS)加固、WebView 治理与构建期混淆的完整移动端安全清单,并能在自己的 Flutter 工程中直接套用。
规则定位:语言层如何扩展通用安全基线
ECC 的规则体系采用「common 通用层 + 语言/框架专用层」的分层结构。根据 rules/README.md 的说明:
rules/common/存放与语言无关的通用原则(如安全、测试、编码风格),任何项目都必须安装;rules/dart/这类语言目录在通用规则之上叠加框架特有内容,每个文件都会引用其对应的 common 版本;- 当语言层规则与 common 层规则冲突时,语言层规则优先(类似 CSS 优先级或
.gitignore的覆盖语义)。
本文主角rules/dart/security.md的 Front Matter 明确声明了它的适用路径范围:
paths: - "**/*.dart" - "**/pubspec.yaml" - "**/AndroidManifest.xml" - "**/Info.plist"也就是说,这套安全规则会在涉及 Dart 源码、Flutter 依赖清单、Android 清单与 iOS 属性列表时生效。而它所扩展的 rules/common/security.md 则定义了所有语言共用的强制检查基线——在任何提交之前必须确认:无硬编码密钥、所有用户输入经过验证、杜绝 SQL 注入(参数化查询)、XSS 防护、CSRF 防护、鉴权授权已验证、端点限流、错误信息不泄漏敏感数据。Dart 层规则正是在这条通用基线上,补充了 Flutter 与移动端特有的实现方式。
密钥管理(Secrets Management)
移动应用最容易犯的安全错误之一,就是把 API Key、Token、凭据直接写死在 Dart 源码中。ECC 规则给出三层递进的密钥管理策略:
- 绝不硬编码:Dart 源代码中禁止出现任何 API 密钥、Token 或认证凭据字面量。
- 编译期配置:使用
--dart-define或--dart-define-from-file注入配置值。需要清醒认识到——这些值并非真正的机密(它们仍可通过逆向从二进制中提取),仅适用于可公开的配置项;真正的服务端机密必须经由后端代理中转,绝不能下发到客户端。 - 运行时机密入安全存储:运行期才需要读取的 Token 等机密,必须存入平台安全存储,Flutter 中即
flutter_secure_storage(iOS 底层走 Keychain,Android 底层走 EncryptedSharedPreferences)。
原规则附带的代码对照示例:
// BAD const apiKey = 'sk-abc123...'; // GOOD — 编译时配置(非机密,仅是可配置的值) const apiKey = String.fromEnvironment('API_KEY'); // GOOD — 从安全存储读取的运行时机密 final token = await secureStorage.read(key: 'auth_token');配套实践还包括:使用flutter_dotenv(或等价方案)管理.env文件,且必须将.env写入.gitignore,防止环境变量文件被误提交进版本库。这一要求与通用层「绝不把机密写进源码、启动时校验必需机密是否存在」的基线一致,可以推断其目的在于让机密泄漏在开发早期就被拦截,而不是等到线上事故才补救。
网络安全(Network Security)
移动端网络层的主要风险是明文传输与无超时导致的不可控行为,规则要求:
- 强制 HTTPS:生产环境禁止任何
http://调用; - Android 侧封锁明文流量:配置
network_security_config.xml; - iOS 侧禁用任意加载:在
Info.plist中设置NSAppTransportSecurity; - 所有 HTTP 客户端必须设置请求超时:绝不保留框架默认值(默认往往过长或为空,容易造成连接挂起);
- 高安全级端点考虑证书钉扎(certificate pinning),用于抵御中间人攻击。
规则给出的 Dio 配置范例:
// 设置超时并强制 HTTPS 的 Dio final dio = Dio(BaseOptions( baseUrl: 'https://api.example.com', connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 30), ));这里connectTimeout与receiveTimeout分别约束建连与接收阶段的最长等待,可按业务场景调整(例如弱网环境可适当放宽接收超时),但核心原则是显式声明而不是依赖默认值。
输入验证(Input Validation)
Dart 层规则聚焦三类输入攻击面:API/存储入口、SQL 查询、深度链接导航。
统一前提:所有用户输入在发送到 API 或写入存储之前,必须先验证并做净化处理。
SQL 注入防护:绝不把未净化的输入拼接进 SQL 语句。sqflite、drift 等数据库驱动都应使用参数化查询:
// BAD — SQL 注入 await db.rawQuery("SELECT * FROM users WHERE email = '$userInput'"); // GOOD — 参数化查询 await db.query('users', where: 'email = ?', whereArgs: [userInput]);深度链接治理:导航前必须校验 scheme、host 与 path 参数,防止恶意链接把用户带到任意路由。规则推荐用Uri.tryParse替代直接Uri.parse,因为前者在解析失败时返回null而非抛异常:
// BAD — 未校验的深度链接 final uri = Uri.parse(incomingLink); context.go(uri.path); // 能导航到任意路由 // GOOD — 校验后的深度链接 final uri = Uri.tryParse(incomingLink); if (uri != null && uri.host == 'myapp.com' && _allowedPaths.contains(uri.path)) { context.go(uri.path); }建议把_allowedPaths这类白名单集中管理,形成可审计的「允许导航路径集合」,并配合通用基线中「错误信息不得泄漏敏感数据」的要求,对外统一返回模糊化错误提示。
数据保护(Data Protection)
移动端本地存储是敏感数据泄漏的高发区,规则给出的数据保护铁律:
- Token、PII、凭据只允许存放于
flutter_secure_storage; - 禁止把敏感数据以明文写入
SharedPreferences或本地文件; - 登出时必须完整清除认证状态:Token、缓存的用户数据、Cookie 一并清理;
- 敏感操作用生物识别门禁(
local_auth)二次确认; - 禁止打印敏感数据——
print(token)、debugPrint(password)这类写法直接踩红线。
这些要求可以理解为「最小留存 + 加密存储 + 及时销毁」的组合:安全存储负责加密落地,登出清理负责生命周期终结,生物识别负责操作授权,日志纪律负责消除旁路泄漏。
Android 平台加固
Android 特有规则围绕「暴露面最小化」展开,共四条:
- 权限最小化:
AndroidManifest.xml中只声明业务必需的权限; - 组件导出收敛:
Activity、Service、BroadcastReceiver仅在必要时导出,不需要时显式标注android:exported="false"; - 意图过滤器审计:带有隐式 intent filter 的导出组件可被任意应用调用,必须逐一审查;
- 敏感界面防截屏:展示敏感数据的界面使用
FLAG_SECURE。
规则给出的清单配置示例:
<!-- AndroidManifest.xml — 限制导出的组件 --> <activity android:name=".MainActivity" android:exported="true"> <!-- 只有启动 Activity 需要 exported=true --> </activity> <activity android:name=".SensitiveActivity" android:exported="false" />注意:自 Android 12(API 31)起,声明了 intent-filter 的组件必须显式设置android:exported,否则无法安装/上架;而按本规则,只有启动入口等确有被外部唤起需求的组件才应设为true,其余组件一律false,从清单层面压缩攻击面。
iOS 平台加固
iOS 侧规则与 Android 规则互为镜像,核心四条:
- 用途说明最小化:
Info.plist中只声明必要的权限用途描述(如NSCameraUsageDescription),避免过度索取隐私权限引发审核与合规问题; - 机密入 Keychain:
flutter_secure_storage在 iOS 上底层即 Keychain,符合 Apple 的钥匙串安全模型; - 启用 ATS:通过
NSAppTransportSecurity禁止任意加载(对应 Android 的network_security_config.xml),强制 HTTPS 通信; - 敏感文件启用数据保护 entitlement:为存放敏感数据的文件开启 Data Protection 文件保护等级。
两条平台规则配合flutter_secure_storage的跨端实现,可以让一套 Dart 代码在双端都落到系统级安全存储,而不是各自为政地使用不安全的本地明文方案。
WebView 安全治理
WebView 是移动应用中公认的高危组件,规则要求从组件选型到运行时导航全面收紧:
- 使用
webview_flutterv4+ 的WebViewController/WebViewWidget:旧版WebView组件已被移除,不得再使用; - 默认禁用 JavaScript:除非业务明确需要,一律保持
JavaScriptMode.disabled; - 加载前校验 URL:绝不从深度链接直接加载任意 URL;
- 不向 JavaScript 暴露 Dart 回调:除非万不得已且经过严格沙箱化;
- 用
NavigationDelegate.onNavigationRequest拦截并校验导航请求。
规则给出的完整实现示例:
// webview_flutter v4+ API(WebViewController + WebViewWidget) final controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.disabled) // 除非必要,否则禁用 ..setNavigationDelegate( NavigationDelegate( onNavigationRequest: (request) { final uri = Uri.tryParse(request.url); if (uri == null || uri.host != 'trusted.example.com') { return NavigationDecision.prevent; } return NavigationDecision.navigate; }, ), ); // 在组件树中: WebViewWidget(controller: controller)这里的核心思想是把 WebView 变成「只认白名单域的受控浏览器」:onNavigationRequest是导航的守门员,host 白名单校验失败即NavigationDecision.prevent拦截;JavaScriptMode.disabled则从默认关闭的角度消除注入型 JS 攻击面。若业务确实需要启用 JavaScript 与 Dart 通信,应严格限定注入的桥接对象与可调用方法,并对传入数据进行校验。
混淆与构建期安全
发布物安全是移动安全的最后一公里,规则给出四条构建纪律:
- 发布构建开启混淆:
flutter build apk --obfuscate --split-debug-info=./debug-info/--split-debug-info输出不纳入版本管理:该目录仅供崩溃符号还原(symbolication)使用,属于构建产物而非源码资产;- 核查 ProGuard/R8 规则:确保序列化类等敏感结构未被规则无意暴露;
- 发布前跑
flutter analyze并清零全部警告,把静态检查问题消灭在发布之前。
建议把上述命令固化进 CI 流水线:发布构建时强制执行--obfuscate,并把./debug-info/加入.gitignore,同时将flutter analyze设为合并请求的质量门槛——这与 ECC 通用基线中「提交前强制安全自检」的精神一脉相承。
落地建议与自检清单
将本规则融入日常开发,可参照 ECC 规则体系的使用方式(详见 rules/README.md):按需将rules/common与rules/dart复制到项目本地(如.claude/rules/ecc/命名空间),让编码代理在触碰 Dart/Flutter 代码时自动遵循。注意必须整目录复制,不要用/*拍平——common 与语言目录存在同名文件,拍平会导致语言层覆盖通用层、破坏../common/相对引用。
每次提交前,对照本文做一轮快速自检:
- 源码中无硬编码密钥、Token、凭据;
.env已进.gitignore; - 所有网络请求走 HTTPS 且显式设置超时;明文流量在 Android/iOS 双侧被封锁;
- 用户输入经校验与净化;SQL 全部参数化;深度链接经
Uri.tryParse与白名单校验; - 敏感数据仅存安全存储;登出清理认证状态;无敏感日志输出;
AndroidManifest.xml权限与导出组件最小化;iOS 用途说明最小化;- WebView 使用 v4+ API、默认禁 JS、导航经
onNavigationRequest白名单校验; - 发布构建开启混淆,
debug-info不进版本库,flutter analyze零警告。
以上每一条都能在 rules/dart/security.md 及其中文日文译本中找到对应依据,并可由 rules/common/security.md 的通用基线兜底——按清单逐项核对,即可为 Flutter 应用建立一条从开发到发布的可审计安全防线。
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考