【问题标题】:How to secure database (encrypted) password on Android?如何在 Android 上保护数据库(加密)密码?
【发布时间】:2012-08-11 05:14:30
【问题描述】:

我有一个数据库,其中包含只能由我的应用程序访问的私人信息。我在资产文件夹中添加了我的数据库文件,该文件夹在应用程序第一次运行时被复制到应用程序数据库目录,但是任何其他应用程序都可以访问“资产”目录和“数据”目录(在根设备上)所以我决定加密数据库。 Android 默认 SQLite 数据库不提供数据加密,所以我决定使用 SQLCipher for android http://sqlcipher.net/sqlcipher-for-android/

现在我已成功加密数据库,我可以使用特定密码访问它。但问题仍然存在......我应该在哪里存储这个密码?以便它只能由我的应用程序访问。

  1. 无法对其进行硬编码,因为即使经过混淆,反编译后仍可访问。
  2. 也无法将其存储在文件系统中(assets/raw)
  3. 不能要求用户输入,因为用户可能是黑客

它是独立的应用程序,完全没有服务器交互

【问题讨论】:

  • 如果您将用户视为攻击者,您将无法信任客户端上的任何内容。就那么简单。您可以对问题进行大量混淆,然后希望没有人打扰它。
  • 为什么没有服务器交互?除非您真的有充分的理由不这样做,否则我认为您应该使用服务器来存储密钥。

标签: android database security password-protection


【解决方案1】:

Can't ask the user to enter it as the user could be a hacker 那么你就没有办法安全地存储它了。由于您已经确定的相同原因,特别是您的代码可以被反编译,您存储它的任何可访问的位置都可以通过反编译找出并因此由具有正确访问权限的任何代码检索。

用户提供的东西存储在用户存储它的任何地方——大概在他或她的脑海中。这不是软件可以访问的东西,因此非常适合降低恶意软件攻击的风险。如果输入的密码有效,您无法知道用户是否被授权,但是您可以定义策略,例如最小密钥长度、最大输入尝试次数(在引入一些延迟或其他锁定之前)等.

【讨论】:

    【解决方案2】:

    我不确定这个答案有多准确,但我仍然发布它,因为我希望得到 Android 极客的一些回应

    考虑到 C 代码难以反编译,我使用 Android NDK 编写了一个简单的原生 C 库(.so),其中包含生成密码的算法。仅当应用程序由我的证书签名时才会执行该算法。如果有人在任何其他应用程序中重新使用此库,它不会返回密码,因为证书不匹配。我从这篇文章中得到了这个想法: http://digital-identity.dk/2010/12/protecting-ip-in-android-applications/

    这是我获得证书的方式:

    void Java_dk_digitalidetity_android_SomeClass_SomeMethod(JNIEnv* env, jobject obj) {
       // this.getPackageManager()
        jclass cls = (*env)->GetObjectClass(env, obj);
        jmethodID mid = (*env)->GetMethodID(env, cls, "getPackageManager", "()Landroid/content/pm/PackageManager;");
       jobject packageManager = (*env)->CallObjectMethod(env, obj, mid);
    
      // this.getPackageName()
       mid = (*env)->GetMethodID(env, cls, "getPackageName", "()Ljava/lang/String;");
       jstring packageName = (jstring) (*env)->CallObjectMethod(env, obj, mid);
    
      // packageManager->getPackageInfo(packageName, GET_SIGNATURES);
       cls = (*env)->GetObjectClass(env, packageManager);
       mid = (*env)->GetMethodID(env, cls, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
       jint flags = GET_SIGNATURES;
       jobject packageInfo = (*env)->CallObjectMethod(env, packageManager, mid, packageName, flags);
    
      // packageInfo->signatures
       cls = (*env)->GetObjectClass(env, packageInfo);
       jfieldID fid = (*env)->GetFieldID(env, cls, "signatures", "[Landroid/content/pm/Signature;");
       jobject signatures = (*env)->GetObjectField(env, packageInfo, fid);
    
     // signatures[0]
       jobject signature = (*env)->GetObjectArrayElement(env, signatures, 0);
    
     // signature->toByteArray()
       cls = (*env)->GetObjectClass(env, signature);
       mid = (*env)->GetMethodID(env, cls, "toByteArray", "()[B");
       jbytearray certificate = (*env)->CallObjectMethod(env, signature, mid);
    }
    

    【讨论】:

    • 共享对象中的代码被一些人认为更容易受到攻击,因为调试器允许您构建一个场景以轻松遍历它。这可以通过编写纯 C 程序来附加并调用您的共享代码、对其进行反汇编以及动态更改代码来帮助实现。一旦确定了正确的更改,二进制文件就可以被黑客入侵,并且由于 Android 在精灵图像上没有签名,因此攻击者(具有正确的能力)需要克服的障碍更少。只需一名成功的攻击者即可破解该计划。
    • C 共享对象内部有一个与应用程序签名匹配的内置检查。如果其他人调用它,它将永远不会命中敏感代码。怎么会有人调试那些永远不会被执行的代码?
    • 你说的是你的apk的签名,我说的是libXXX.so本机扩展是未签名的。安装您的应用程序后,其本机库将被提取到文件系统中,并且再也不会在 apk 中被引用。关于 apk 签名检查 - 可以修改 .so 的人同样能够修改它以禁用 apk 签名检查,并且由于此代码可能通过 JNI 接口返回到 VM,因此相对容易定位。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多