本文概述
如你所知, apk文件实际上只是一个zip存档, 因此你可以尝试将文件重命名(或简单地迫使你的解压缩工具打开apk文件)为appname.apk.zip并使用任何zip实用程序将其解压缩。在此示例中, 我们将构建一个使用React(不是本地本机)和webpack的应用程序, 以在单个文件app.js中生成所有js代码。然后, 我们使用cordova build android –release构建应用程序, 并使用zip实用程序在project / platforms / android / build / outputs / apk中查看apk生成文件(构建或调试)的内容(在这种情况下, 我们将使用WinRAR):
而且, 如果你尝试编辑一些提取的文件(我们的app.js), 那么它将包含:
你看见我看到的了吗 ?你的应用程序的源代码, 任何知道如何使用zip实用程序打开APK的人都可以阅读。尽管我们的代码被最小化用于生产(不是由cordova而是由我们制造), 但该代码仍然可见, 但可以清晰地打印出来, 因此可读性强。 (显然)这不太好, 因为你的代码可能会暴露功能的安全漏洞(如果有的话), 例如登录应用程序, 付款流程等。
正如许多开发人员所说, 你可能需要知道, 针对逆向工程没有100%的安全性。尽管你可以做更多的事情来保护应用程序的代码, 但实际上, 这对于大多数普通用户来说要困难得多, 他们只是在Google上搜索”如何破解APK”。如果有人真的想对你的应用程序进行黑客入侵, 则可以迟早对其进行黑客入侵(除非你的应用程序与服务器通信很多, 并且大多数功能不在设备上)。
在本文中, 你将学习如何在Cordova中保护(或至少提高保护级别)应用程序的源代码。
1.安装加密插件
我们将使用cordova-plugin-crypt-file插件来加密项目的www文件夹内的所有文件(生成的apk中包含的文件)。它可以在任何cordova项目中使用(即使它使用Crosswalk), 尽管本教程仅适用于Android, 但该插件本身也支持iOS平台。
在终端中使用以下命令安装插件(一旦你位于项目文件夹中):
cordova plugin add cordova-plugin-crypt-file
插件安装完成后, 它将自动开始工作, 并且在每个版本上, 它将对文件进行加密。
加密如何工作?
该插件使用AES / CBC / PKCS5Padding加密算法来加密文件。插件会在应用程序编译期间使用随机生成的加密密钥和初始化向量(IV)对文件进行加密(这意味着将不会修改项目内部的原始文件, 仅会修改生成的APK的文件) 。与预期的一样, 每次你的应用程序在已安装设备上启动时, 文件都会被解密。
2.自定义应加密的文件
如前所述, 文件已加密, 无法使用Javascript对其解密。如果不需要加密文件, 则可以使用自定义正则表达式自定义要加密的文件。但是, 你将需要在插件文件中指定此名称。首先打开/ project / plugins / cordova-plugin-crypt-file中的plugin.xml并修改cryptfiles标记。
默认情况下, 插件包含以下正则表达式, 用于加密www目录中的所有css, htm, html和js文件:
<cryptfiles>
<include>
<file regex="\.(htm|html|js|css)$" />
</include>
<exclude>
</exclude>
</cryptfiles>
只需自定义正则表达式以指定将要加密的文件, 例如, 以下标记将仅压缩html, htm, js, 而不压缩css文件, 并使用名称示例排除单个JS文件:
<cryptfiles>
<include>
<file regex="\.(htm|html|js)$" />
</include>
<exclude>
<file regex="example.js" />
</exclude>
</cryptfiles>
完成更改后, 保存更改。
3.验证文件是否已加密
现在, 在安装插件(以及可选的自定义加密文件)之后, 你可以验证插件是否正常工作。重复本文开头提到的相同过程。构建你的应用程序(调试或发布模式), 并使用zip实用程序查看apk(资产/ www)中www文件夹的内容:
如你所见, 文件的结构保持不变。最后, 使用代码编辑器编辑任何文件(只要不将其排除在加密之外), 你将看到文件的内容已加密:
现在看来不可读, 不是吗?安装的插件将在运行时解密文件, 你的应用程序将按预期运行, 并且你的源代码具有更高的保护级别。
额外的安全性
你可以使用Cordova在生成的APK中使用ProGuard。 ProGuard优化字节码, 删除未使用的代码指令, 并用短名称混淆其余的类, 字段和方法。混淆的代码使你的APK难以进行反向工程, 当你的应用使用对安全敏感的功能(例如许可验证)时, 这尤其有价值。
在/ project / platforms / android文件夹中创建具有以下内容的proguard-rules.pro文件:
# By default, the flags in this file are appended to flags specified
# in /usr/share/android-studio/data/sdk/tools/proguard/proguard-android.txt
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
##---------------Begin: proguard configuration common for all Android apps ----------
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-dump class_files.txt
-printseeds seeds.txt
-printusage unused.txt
-printmapping mapping.txt
-optimizations !code/simplification/arithmetic, !field/*, !class/merging/*
-allowaccessmodification
-keepattributes *Annotation*
-renamesourcefileattribute SourceFile
-keepattributes SourceFile, LineNumberTable
-repackageclasses ''
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-dontnote com.android.vending.licensing.ILicensingService
# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# Preserve static fields of inner classes of R classes that might be accessed
# through introspection.
-keepclassmembers class **.R$* {
public static <fields>;
}
# Preserve the special static methods that are required in all enumeration classes.
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep public class * {
public protected *;
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
##---------------End: proguard configuration common for all Android apps ----------
#---------------Begin: proguard configuration for support library ----------
-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class com.actionbarsherlock.** { *; }
-keep interface com.actionbarsherlock.** { *; }
# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**
-dontwarn com.google.ads.**
##---------------End: proguard configuration for Gson ----------
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.example.model.** { *; }
##---------------End: proguard configuration for Gson ----------
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
然后编辑build.gradle文件(在/ project / platforms / android内或创建一个Cordova插件, 将其自动添加到构建文件中):
android {
...
buildTypes {
debug {
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
...
}
最后一步, 使用cordova build android –release在发布模式下构建apk, 就可以开始了。
ProGuard会缩小你的代码, 使其难以阅读(最小化), 这可能会减慢逆向工程过程。尽管ProGuard不会混淆字符串常量, 但它更专门用于Android, DexGuard的闭源同级, 并提供了附加的应用程序保护技术, 例如字符串加密和类加密。
如果你认识Cordova开发人员, 则可能不了解此问题, 请分享此文章。编码愉快!