保护你的 Flutter 应用程序
原文 https://medium.com/flutter-community/securing-your-flutter-applications-77c2bf3ff25e
如果本文对你有帮助,请转发让更多的朋友阅读。
如果您不知道需要更改哪些内容以及这些内容如何影响整个构建,那么保护 Flutter 应用程序可能是一项任务,但在生产应用程序时,这是唯一最重要的事情。
保护 Flutter 应用程序的安全应该是每个开发人员都必须认真对待的事情,因为这直接影响到应用程序的最终用户。他们的数据和通过 Internet 发生在您的应用程序之间的所有通信都可能受到各种攻击,意识到这些风险并采取措施防范这些风险应该是每个开发人员的责任。
Flutter + Security = ❤
这里需要注意的一点是,我在本文中提到的所有内容都取决于您的应用程序是否需要它。您必须决定这对您的应用程序是否至关重要。
首先,让我们从一些基本的列表项目开始,然后转向更加技术性的列表项目。
使用受信任的软件包,总体上使用较少的软件包(如果可能的话)
大多数应用程序的功能或 UI 组件将依赖于外部包,确保所使用的包由受信任的开发人员开发、维护良好并被相当多的人使用非常重要。这样做的原因是,在出现问题的情况下,由于使用它的人数较多,所以它被提前发现。Dev 得分和受欢迎程度应该是一个很好的起点。
始终通过 HTTPS 进行通信
如果您的应用程序与外部 API 和服务通信,请确保应用程序与远程服务器之间的通信是通过 HTTPS 进行的,以便数据在传输过程中被加密,并且在传输过程中没有外部黑客可以轻易访问数据。几乎所有在互联网上的东西都已经迁移到使用 HTTPS,所以这真的不应该再是一个选项,而是一种默认的做事方式。
HTTP vs HTTPS
正确的错误处理和消息传递
有可能抛出异常的函数应该总是放在 try-catch 块中,这样就不会给用户带来不可预见的应用程序崩溃。除此之外,向用户显示有用的错误信息也是一个很好的做法,但是始终不要显示错误的确切细节,因为这些信息可能/可以很容易地用来找到漏洞。
import 'package:flutter/material.dart';
Future<dynamic> functionThatThrowsException() async {
// some code
throw Exception('Could not perform operation.');
}
Future<void> testFunction() async {
try {
var x = await functionThatThrowsException();
// do something with x
} catch (e) {
debugPrint(e.toString());
}
}
Try Catch with Flutter
当 Flutter 应用程序生产发布,如果发生错误,灰色屏幕显示给用户,这可以从用户的观点令人沮丧。因此,显示一个有用的错误小部件/屏幕可能是简化用户体验的好方法。
return MaterialApp(
title: 'Flutter App',
builder: (context, widget) {
ErrorWidget.builder = (FlutterErrorDetails errorDetails) {
return CustomErrorWidget(errorDetails: errorDetails);
};
return widget ?? const Scaffold();
},
...
...
class CustomErrorWidget extends StatelessWidget {
final FlutterErrorDetails errorDetails;
const CustomErrorWidget({
Key? key,
required this.errorDetails,
}) : super(key: key);
Widget build(BuildContext context) {
return const Card(
color: Colors.red,
margin: EdgeInsets.zero,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"Something is not right here...",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
);
}
}
Displaying a Custom Error Widget
📱Minimal Permissions
确保只使用您需要的足够的应用程序权限。如果某个特定权限对应用程序的运行不是必需的,请避免使用它。在不必要的情况下使用权限会使应用程序暴露在商店的审查之下。此外,如果您没有正确使用这些权限,则可能侵犯用户的隐私。随着全球范围内的数据和隐私政策日益严格,这一问题变得极其严重。
始终加密数据
在设备上存储数据(如令牌或任何 PII)时,应始终使用平台的安全存储对这些数据进行加密。一个非常有用的软件包就是 flutter_security_Storage。此包是一个简单的键-值对加密存储,非常适合用于存储简单数据类型。理想情况下,应该尽量减少存储在设备上的数据,但是如果由于性能提高或其他原因需要存储比键-值对更多的数据,那么 hive 可能非常有用。Hive 是 NoSQL,为 Dart 编写的设备上数据库,性能极佳,支持 AES-256 加密开箱即用。
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
Future<void> secureStorageActions() async {
// Create storage
const storage = FlutterSecureStorage();
// Read value
String? value = await storage.read(key: 'key');
// Read all values
Map<String, String> allValues = await storage.readAll();
// Delete value
await storage.delete(key: 'key');
// Delete all
await storage.deleteAll();
// Write value
await storage.write(key: 'key', value: 'value');
}
Using Flutter Secure Storage
接下来是更复杂的建议。
代码混淆
代码混淆是将人类可读的代码转换成看似垃圾的读代码,使攻击者更难理解捆绑是否被反编译以进行反向工程。在 Flutter 2.0 + 上,本地平台默认已经做到了这一点,对于混淆 Dart 代码,我们可以包含——模糊标志以及带有模糊符号位置的—— split-debug-info 标志。然后可以使用这些符号将混淆后的代码转换回可读代码,以便在需要时调试问题。
flutter build appbundle --obfuscate --split-debug-info=./symbols/
Flutter build appbundle —— obfuscate —— split-debug-info = ./marks/
flutter build apk --obfuscate --split-debug-info=./symbols/
Flutter build apk —— obbuscate —— split-debug-info = ./marks/
flutter build ios --obfuscate --split-debug-info=./symbols/
Flutter build ios —— obbuscate —— split-debug-info = ./marks/
SSL Pinning
SSL 固定是一种安全措施,它将可信证书的身份固定在移动应用程序上,并阻止来自可疑服务器的未知文档。带有固定 SSL 证书的应用程序依赖于其存储的证书,而不是依赖于证书颁发机构存储许可证。使用这种技术,您可以在开发期间将 SSL 证书主机ーー可信证书列表固定到您的应用程序,并在运行期间进一步比较服务器证书与列表。
You can read more about SSL Pinning here: https://medium.com/@anuj.rai2489/ssl-pinning-254fa8ca2109
你可以在这里阅读更多关于 SSL 固定的内容: https://medium.com/@anuj.rai2489/SSL-Pinning-254fa8ca2109
SSL Pinning
因此,要实现这一点,我们需要:
- Pem 格式的证书
- 预请求钩子处理程序
要获得所需格式的证书,首先需要从 Web 浏览器下载该证书,该证书将位于。Cerformat,然后使用以下命令:
openssl x509 -in cert.cer -out cert.pem
Openssl x509-in cert.cer.out cert.pem
然后在所选的 http 库中的预请求钩子处理程序中使用 verifyHandshake 函数。
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/io_client.dart';
Future<SecurityContext> get globalContext async {
final sslCert = await rootBundle.load('assets/certs/ryandsilva-dev.pem');
final sc = SecurityContext(withTrustedRoots: false);
sc.setTrustedCertificatesBytes(sslCert.buffer.asInt8List());
return sc;
}
Future<bool> verifyHandshake(String url) async {
try {
final client = HttpClient(context: await globalContext);
client.badCertificateCallback = ((cert, host, port) => false);
final ioClient = IOClient(client);
await ioClient.get(Uri.parse(url));
return true;
} catch (e) {
debugPrint('Could not complete SSL handshake: ${e.toString()}');
return false;
}
}
bool isSecureConnection = await verifyHandshake('https://ryandsilva.dev/');
SSL Pinning
网络安全配置(Android 专用)
这是本机配置,使应用程序只能与强制 HTTPS 连接的允许域进行通信。Security_config.xml 文件应该包含应用程序需要与之通信的所有域的条目。Include 包含子域参数指定是否要包含主域的所有子域。如果选择不包括所有子域,则可以在域列表中单独指定每个子域。中添加到以下位置的每个域的证书。Pem 格式: android/app/src/main/res/raw 和 security_config.xml 文件位于 android/app/src/main/res/xml 文件夹中。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.ryandsilva.flutter_app">
<application
android:label="flutter_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:networkSecurityConfig="@xml/security_config">
...
...
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">ryandsilva.dev</domain>
<trust-anchors>
<certificates src="@raw/ryandsilva-dev"/>
</trust-anchors>
</domain-config>
</network-security-config>
安全配置 ー Android
NSAppTransportation 安全(仅限 iOS)
这是本机配置,使应用程序只能与强制 HTTPS 连接的允许域进行通信。NSExceptionDomains 键应该包含应用程序需要与之通信的所有域的条目。NSPackesSubdomain 键指定是否要包括主域的所有子域。如果选择不包括所有子域,则可以在域列表中单独指定每个子域。下面的代码片段应该附加到 IOS/Runner 下的 Info.plist 文件。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>ryandsilva.dev</key>
<dict>
<!--Include to allow subdomains-->
<key>NSIncludesSubdomains</key>
<true/>
<!--Include to allow HTTP requests-->
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<false/>
</dict>
</dict>
</dict>
NSAppTransportSecurity Config — iOS
越狱及根源侦测
根据应用程序的类型,您可能希望限制在有根/破解设备上的使用。不允许您的应用程序在植根/破解设备上运行将阻止黑客访问设备上的应用程序数据和其他可能危害您的应用程序的活动。为此,我们将使用 flutter_jailbreak_check 包。
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_jailbreak_detection/flutter_jailbreak_detection.dart';
Future<bool> checkJailbrokenOrRooted() async {
bool jailbrokenOrRooted = true;
try {
if (Platform.isAndroid) {
jailbrokenOrRooted = await FlutterJailbreakDetection.developerMode;
} else if (Platform.isIOS) {
jailbrokenOrRooted = await FlutterJailbreakDetection.jailbroken;
} else {
jailbrokenOrRooted = true;
}
return jailbrokenOrRooted;
} on PlatformException {
jailbrokenOrRooted = true;
return jailbrokenOrRooted;
}
}
根/ 越狱检测
谢谢!
如果本文对你有帮助,请转发让更多的朋友阅读。
© 猫哥
微信 ducafecat
https://wiki.ducafecat.tech
https://ducafecat.com