Bas*_*der 342 android reverse-engineering proguard api-key
大多数应用开发者会将一些第三方库集成到他们的应用中.如果要访问服务,例如Dropbox或YouTube,或者用于记录崩溃.第三方图书馆和服务的数量是惊人的.大多数这些库和服务都是以某种方式通过服务进行身份验证而集成的,大部分时间都是通过API密钥进行的.出于安全目的,服务通常生成公共和私有(通常也称为秘密密钥).不幸的是,为了连接到服务,必须使用此私钥进行身份验证,因此可能是应用程序的一部分.毋庸置疑,这面临着巨大的安全问题.公共和私人API密钥可以在几分钟内从APK中提取,并且可以轻松实现自动化.
假设我有类似的东西,我该如何保护密钥:
public class DropboxService {
private final static String APP_KEY = "jk433g34hg3";
private final static String APP_SECRET = "987dwdqwdqw90";
private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;
// SOME MORE CODE HERE
}
Run Code Online (Sandbox Code Playgroud)
您认为存储私钥的最佳和最安全的方式是什么?混淆,加密,你怎么看?
Eri*_*une 327
实际上,您编译的应用程序包含键字符串,但也包含常量名称APP_KEY和APP_SECRET.从这种自我记录代码中提取密钥是微不足道的,例如使用标准的Android工具dx.
您可以申请ProGuard.它将保持关键字符串不变,但它将删除常量名称.它还将尽可能用简短无意义的名称重命名类和方法.然后提取密钥需要更多时间,以确定哪个字符串用于哪个目的.
请注意,设置ProGuard不应该像您担心的那样困难.首先,您只需要启用ProGuard,如project.properties中所述.如果第三方库存在任何问题,您可能需要在proguard-project.txt中禁止某些警告和/或防止它们被混淆.例如:
-dontwarn com.dropbox.**
-keep class com.dropbox.** { *; }
Run Code Online (Sandbox Code Playgroud)
这是一种蛮力的方法; 您可以在处理的应用程序工作后优化此类配置.
您可以在代码中手动对字符串进行模糊处理,例如使用Base64编码,或者最好使用更复杂的内容; 甚至可能是本机代码.然后,黑客必须对您的编码进行静态逆向工程,或者在适当的位置动态拦截解码.
您可以应用商业混淆器,如ProGuard的专业兄弟DexGuard.它还可以为您加密/混淆字符串和类.提取密钥需要更多的时间和专业知识.
您可以在自己的服务器上运行部分应用程序.如果你能把钥匙留在那里,它们是安全的.
最后,你必须做出经济权衡:钥匙的重要性,你能承受多少时间或软件,对钥匙感兴趣的黑客有多复杂,他们想要多长时间花费,在密钥被黑客攻击之前的延迟,在任何成功的黑客以何种规模分发密钥等的价值是多少.像密钥这样的小信息比整个应用程序更难保护.从本质上讲,客户端没有任何东西是牢不可破的,但你肯定可以提高标准.
(我是ProGuard和DexGuard的开发人员)
mar*_*inj 74
很少有想法,在我看来只有第一个给出一些保证:
将您的秘密保存在互联网上的某些服务器上,并在需要时抓住它们并使用.如果用户即将使用dropbox,则不会阻止您向您的站点发出请求并获取您的密钥.
把你的秘密放在jni代码中,添加一些变量代码,使你的库更大,更难以反编译.您也可以将键字串分成几个部分并将它们保存在不同的位置.
使用混淆器,也放入代码哈希秘密,稍后在需要时使用unhash.
将您的密钥作为资产中某个图像的最后一个像素.然后在需要时在您的代码中阅读它.混淆代码应该有助于隐藏将读取它的代码.
如果你想快速了解一下阅读apk代码是多么容易,然后抓住APKAnalyser:
http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/
小智 26
另一种方法是首先在设备上没有秘密!请参阅移动API安全技术(尤其是第3部分).
使用时间传承的间接传统,分享您的API端点和应用认证服务之间的秘密.
当您的客户端想要进行API调用时,它会要求应用程序auth服务对其进行身份验证(使用强大的远程证明技术),并且它会收到由该机密签名的时间限制(通常是JWT)令牌.
令牌随每次API调用一起发送,其中端点可以在对请求执行操作之前验证其签名.
实际的秘密永远不会出现在设备上; 事实上,应用程序永远不知道它是否有效,它会发出请求身份验证并传递生成的令牌.作为间接的一个很好的好处,如果您想要更改秘密,您可以这样做而无需用户更新其已安装的应用程序.
因此,如果您想保护自己的秘密,首先不要在您的应用中使用它是一个非常好的方法.
SAN*_*NAT 19
我们可以使用Gradle来保护API密钥或密钥.
1. gradle.properties(项目属性):使用键创建变量.
GoolgeAPIKey = "Your API/Secret Key"
Run Code Online (Sandbox Code Playgroud)
2. build.gradle(Module:app):在build.gradle中设置变量以在activity或fragment中访问它.将以下代码添加到buildTypes {}.
buildTypes.each {
it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}
Run Code Online (Sandbox Code Playgroud)
3.通过app的BuildConfig在Activity/Fragment中访问它:
BuildConfig.GoogleSecAPIKEY
Run Code Online (Sandbox Code Playgroud)
上面的解决方案有助于开源项目提交Git.(感谢David Rawson和riyaz-ali的评论).
根据Matthew和Pablo Cegarra的评论,上述方式并不安全,Decompiler将允许某人使用我们的密钥查看BuildConfig.
方案:
我们可以使用NDK来保护API密钥.我们可以将密钥存储在本机C/C++类中,并在我们的Java类中访问它们.
请关注此博客以使用NDK保护API密钥.
Mil*_*nia 13
一种可能的解决方案是在应用程序中对数据进行编码,并在运行时使用解码(当您想要使用该数据时).我还建议使用progaurd使您难以阅读和理解应用程序的反编译源代码.例如,我在应用程序中放置了一个编码键,然后在我的应用程序中使用解码方法在运行时解码我的密钥:
// "the real string is: "mypassword" ";
//encoded 2 times with an algorithm or you can encode with other algorithms too
public String getClientSecret() {
return Utils.decode(Utils
.decode("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}
Run Code Online (Sandbox Code Playgroud)
已编译应用程序的反编译源代码如下:
public String c()
{
return com.myrpoject.mypackage.g.h.a(com.myrpoject.mypackage.g.h.a("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}
Run Code Online (Sandbox Code Playgroud)
至少它对我来说足够复杂.当我别无选择但在我的应用程序中存储值时,这就是我的方式.当然我们都知道这不是最好的方式,但它对我有用.
/**
* @param input
* @return decoded string
*/
public static String decode(String input) {
// Receiving side
String text = "";
try {
byte[] data = Decoder.decode(input);
text = new String(data, "UTF-8");
return text;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "Error";
}
Run Code Online (Sandbox Code Playgroud)
反编译版本:
public static String a(String paramString)
{
try
{
str = new String(a.a(paramString), "UTF-8");
return str;
}
catch (UnsupportedEncodingException localUnsupportedEncodingException)
{
while (true)
{
localUnsupportedEncodingException.printStackTrace();
String str = "Error";
}
}
}
Run Code Online (Sandbox Code Playgroud)
你可以在谷歌搜索一下这么多的加密类.
Aym*_*bsi 12
添加到@Manohar Reddy解决方案中,可以使用firebase数据库或firebase RemoteConfig(默认值为Null):
此解决方案有什么不同?
小智 11
App-Secret密钥应保密 - 但在发布应用程序时,有些人可以撤销它们.
对于那些不会隐藏的人,锁定ProGuard代码.它是一个重构器,一些付费的混淆器正在插入几个按位运算符来取回jk433g34hg3
String.如果你工作3天,你可以让黑客长5到15分钟:)
imho,最好的方法是保持原样.
即使您存储在服务器端(您的PC),密钥也可能被黑客入侵并打印出来.也许这花费的时间最长?无论如何,最好的情况是几分钟或几小时.
普通用户不会反编译您的代码.
The*_*ist 10
这个例子有许多不同的方面.我将提到一些我认为其他地方没有明确涵盖过的观点.
保护运输中的秘密
首先要注意的是,使用应用程序身份验证机制访问Dropbox API 需要您传输密钥和密钥.连接是HTTPS,这意味着您无法在不知道TLS证书的情况下拦截流量.这是为了防止人们在从移动设备到服务器的旅程中拦截和读取分组.对于普通用户来说,这是确保其流量隐私的一种非常好的方式.
它不擅长的是阻止恶意的人下载应用程序并检查流量.对于进出移动设备的所有流量,使用中间人代理非常容易.由于Dropbox API的性质,在这种情况下,不需要对代码进行反汇编或逆向工程来提取应用密钥和密钥.
你可以做牵制,检查你从服务器接收TLS证书是你所期望的一个.这会向客户端添加一个检查,并使拦截流量变得更加困难.这将使得检查飞行中的流量变得更加困难,但是在客户端中发生钉扎检查,因此可能仍然可以禁用钉扎测试.它确实让它变得更难.
在休息时保护秘密
作为第一步,使用像proguard这样的东西将有助于使任何秘密保持不太明显.您还可以使用NDK存储密钥和密钥并直接发送请求,这将大大减少具有提取信息的适当技能的人数.通过不将值直接存储在内存中任何时间长度,可以实现进一步的混淆,您可以加密它们并在使用之前解密它们,如另一个答案所示.
更高级的选择
如果您现在想把秘密放在应用程序的任何地方,并且您有时间和金钱投资更全面的解决方案,那么您可以考虑将凭证存储在您的服务器上(假设您有任何凭据).这会增加对API的任何调用的延迟,因为它必须通过您的服务器进行通信,并且由于数据吞吐量的增加可能会增加运行服务的成本.
然后,您必须决定如何最好地与服务器通信,以确保它们受到保护.这对于防止内部API再次出现所有相同问题非常重要.我可以给出的最好的经验法则是不要因为中间人的威胁而直接传递任何秘密.相反,您可以使用您的密钥对流量进行签名,并验证来到您服务器的任何请求的完整性.这样做的一种标准方法是计算密钥上的消息的HMAC.我在一家拥有安全产品的公司工作,该产品也在这个领域运作,这就是为什么这种东西让我感兴趣的原因.事实上,这是一篇来自我的同事的博客文章,其中大部分内容都是如此.
我该怎么办?
有了这样的任何安全建议,你需要做出成本/收益决定,让你有多难做到让别人闯入.如果你是一家保护数百万客户的银行,你的预算与支持他们的应用程序的人完全不同.空余时间.实际上几乎不可能阻止某人破坏您的安全,但在实践中,很少有人需要所有的花里胡哨和一些基本的预防措施,您可以获得很长的路要走.
最安全的解决方案是将密钥保存在服务器上,并通过服务器路由所有需要该密钥的请求.这样,密钥永远不会离开您的服务器,因此只要您的服务器是安全的,那么您的密钥也是如此.当然,这种解决方案会带来性能成本.
年老的帖子,但仍然足够好.我认为将它隐藏在.so库中会很棒,当然使用NDK和C++..so文件可以在十六进制编辑器中查看,但好运反编译:P
保持这些隐私的唯一真正方法是将它们保留在您的服务器上,并让应用程序将任何内容发送到服务器,然后服务器与 Dropbox 进行交互。这样你就永远不会以任何格式分发你的私钥。
无论您采取什么措施来保护您的秘密密钥,都不是真正的解决方案。如果开发人员可以对应用程序进行反编译,则无法确保密钥的安全,隐藏密钥只是模糊性的安全性,而代码混淆也是如此。保护秘密密钥的问题在于,为了保护它,您必须使用另一个密钥,并且该密钥也需要被保护。想想隐藏在用钥匙锁的盒子中的钥匙。您将一个盒子放在房间内并锁定房间。您剩下另一把钥匙来保护。而且该密钥仍将在您的应用程序内进行硬编码。
因此,除非用户输入PIN或短语,否则无法隐藏密钥。但是要做到这一点,您将必须有一种方案来管理带外发生的PIN,这意味着要通过不同的渠道。对于保护Google API之类的服务的密钥当然不切实际。
firebase database当应用程序启动时,要保守秘密并从中获取秘密,这比调用Web服务要好得多。
| 归档时间: |
|
| 查看次数: |
124026 次 |
| 最近记录: |