diff --git a/README.md b/README.md index 8adf7db26..68c04e578 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,10 @@ Google Android团队在2012年的时候开设了**Android Training**板块 - 编写:[craftsmanBai](https://github.com/craftsmanBai) - - 原文: -这些课程文章告诉你怎样让app数据得到安全保护。 +下面的课程教你如何确保应用程序数据的安全。 -#### [安全要点](security/security-tips.html) -怎样执在执行多个任务的同时保护app数据和用户数据安全。 -#### [HTTPS和SSL的安全](security/security-ssl.html) -怎样确保app进行网络传输时是安全的。 +## [安全要点](security/security-tips.html) -#### [企业级开发](security/enterprise/index.html) -怎样为企业级的app实施设备管理策略。 +怎样执在执行多个任务的同时确保应用程序数据和用户数据的安全。 + +## [HTTPS和SSL的安全](security/security-ssl.html) + +如何确保应用程序在进行网络传输时是安全的。 + +## [更新你的Security Provider对抗SSL漏洞攻击](security/security-gms-provider.html) + +如何使用和更新Google Play services security provider来对抗SSL漏洞攻击。 + +## [企业级开发](enterprise/index.html) + +如何为企业级应用程序实施设备管理策略。 diff --git a/enterprise/app-compatibility.md b/enterprise/app-compatibility.md new file mode 100644 index 000000000..96133c066 --- /dev/null +++ b/enterprise/app-compatibility.md @@ -0,0 +1,5 @@ +# Ensuring Compatibility with Managed Profiles + +> 编写: - 原文: + +待认领进行编写,有意向的小伙伴,可以直接修改对应的markdown文件,进行提交! diff --git a/security/security-gms-provider.md b/enterprise/app-restrictions.md similarity index 77% rename from security/security-gms-provider.md rename to enterprise/app-restrictions.md index 221f11217..abc8f4c74 100644 --- a/security/security-gms-provider.md +++ b/enterprise/app-restrictions.md @@ -1,4 +1,4 @@ -# 为防止SSL漏洞而更新Security +# Implementing App Restrictions > 编写: - 原文: diff --git a/enterprise/index.md b/enterprise/index.md new file mode 100755 index 000000000..dd9f539cc --- /dev/null +++ b/enterprise/index.md @@ -0,0 +1,47 @@ +# 创建企业级应用 + +> 编写:[craftsmanBai](https://github.com/craftsmanBai) - - 原文: + +![](work-launcher.png) + +Android框架提供安全支持、数据分离、企业环境管理的功能。作为应用开发者,通过适当地处理企业安全和功能限制,你可以让你的应用程序吸引更多的企业客户。也可以修改你的应用使技术管理员可远程配置使用企业资源。 + +为了帮助企业将安卓设备和应用程序进入工作场所,Google通过Android for Work为设备的分配和管理提供了一套API和服务。通过这项计划,企业可以连接到企业移动性管理(EMM)供应商,将Android整合到工作中。 + +通过下面的链接获取,可以了解更多关于如何更新您的Android应用程序来支持企业环境或建立企业解决方案的信息。 + + +## 企业级应用开发 + +了解在企业环境中如何使您的应用程序运行顺畅,限制设备的功能和数据访问。通过加入限制进一步支持企业使用你的app,让管理员可以远程配置使用你的应用程序: + +确保与管理兼容: + +[http://developer.android.com/training/enterprise/app-compatibility.html](http://developer.android.com/training/enterprise/app-compatibility.html +) + +加入应用限制: + +[http://developer.android.com/training/enterprise/app-restrictions.html](http://developer.android.com/training/enterprise/app-restrictions.html) + +应用限制计划: + +[http://developer.android.com/samples/AppRestrictionSchema/index.html](http://developer.android.com/samples/AppRestrictionSchema/index.html) + + +应用限制执行者: + + +[http://developer.android.com/samples/AppRestrictionEnforcer/index.html](http://developer.android.com/samples/AppRestrictionEnforcer/index.html) + +## 设备与应用管理 + +学习如何为应用程序建立策略控制器,使企业的技术管理人员来管理设备,管理企业应用程序,并提供访问公司资源的权限: + +建立工作策略控制: + +[http://developer.android.com/training/enterprise/work-policy-ctrl.html](http://developer.android.com/training/enterprise/work-policy-ctrl.html) + +基本管理模型: + +[http://developer.android.com/samples/BasicManagedProfile/index.html](http://developer.android.com/samples/BasicManagedProfile/index.html) diff --git a/enterprise/work-launcher.png b/enterprise/work-launcher.png new file mode 100755 index 000000000..3bbd83544 Binary files /dev/null and b/enterprise/work-launcher.png differ diff --git a/enterprise/work-policy-ctrl.md b/enterprise/work-policy-ctrl.md new file mode 100644 index 000000000..048efb3f6 --- /dev/null +++ b/enterprise/work-policy-ctrl.md @@ -0,0 +1,5 @@ +# Building a Work Policy Controller + +> 编写: - 原文: + +待认领进行编写,有意向的小伙伴,可以直接修改对应的markdown文件,进行提交! diff --git a/security/device-management-policy.md b/security/device-management-policy.md new file mode 100755 index 000000000..478a4e44b --- /dev/null +++ b/security/device-management-policy.md @@ -0,0 +1,139 @@ +# 使用设备管理策略增强安全性 + +> 编写:[craftsmanBai](https://github.com/craftsmanBai) - - 原文: + +Android 2.2(API Level 8)之后,Android平台通过设备管理API提供系统级的设备管理能力。 + +在这一小节中,你将学到如何通过使用设备管理策略创建安全敏感的应用程序。比如某应用可被配置为:在给用户显示受保护的内容之前,确保已设置一个足够强度的锁屏密码。 + +## 定义并声明你的策略 + +首先,你需要定义多种在功能层面提供支持的策略。这些策略可以包括屏幕锁密码强度、密码过期时间以及加密等等方面。 + +你须在res/xml/device_admin.xml中声明选择的策略集,它将被应用强制实行。在Android manifest也需要引用声明的策略集。 + +每个声明的策略对应[DevicePolicyManager](http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html)中一些相关设备的策略方法(例如定义最小密码长度或最少大写字母字符数)。如果一个应用尝试调用XML中没有对应策略的方法,程序在会运行时抛出一个[SecurityException](http://developer.android.com/reference/java/lang/SecurityException.html)异常。 + +如果应用程序试图管理其他策略,那么强制锁force-lock之类的其他权限就会发挥作用。正如你将看到的,作为设备管理权限激活过程的一部分,声明策略的列表会在系统屏幕上显示给用户。 +如下代码片段在res/xml/device_admin.xml中声明了密码限制策略: + +```xml + + + + + +``` +在Android manifest引用XML策略声明: + +```xml + + + + + + +``` + +## 创建一个设备管理接受端 + +创建一个设备管理广播接收端(broadcast receiver),可以接收到与你声明的策略有关的事件通知。也可以对应用程序有选择地重写回调函数。 + +在同样的应用程序(Device Admin)中,当设备管理(device administrator)权限被用户设为禁用时,已配置好的策略就会从共享偏好设置(shared preference)中擦除。 + +你应该考虑实现与你的应用业务逻辑相关的策略。例如,你的应用可以采取一些措施来降低安全风险,如:删除设备上的敏感数据,禁用远程同步,对管理员的通知提醒等等。 + +为了让广播接收端能够正常工作,请务必在Android manifest中注册下面代码片段所示内容。 + +```xml + + + + + + +``` + +## 激活设备管理器 + +在执行任何策略之前,用户需要手动将程序激活为具有设备管理权限,下面的程序片段显示了如何触发设置框以便让用户为你的程序激活权限。 + +通过指定[EXTRA_ADD_EXPLANATION](http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#EXTRA_ADD_EXPLANATION)给出明确的说明信息,以告知用户为应用程序激活设备管理权限的好处。 + +```java +if (!mPolicy.isAdminActive()) { + + Intent activateDeviceAdminIntent = + new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); + + activateDeviceAdminIntent.putExtra( + DevicePolicyManager.EXTRA_DEVICE_ADMIN, + mPolicy.getPolicyAdmin()); + + // It is good practice to include the optional explanation text to + // explain to user why the application is requesting to be a device + // administrator. The system will display this message on the activation + // screen. + activateDeviceAdminIntent.putExtra( + DevicePolicyManager.EXTRA_ADD_EXPLANATION, + getResources().getString(R.string.device_admin_activation_message)); + + startActivityForResult(activateDeviceAdminIntent, + REQ_ACTIVATE_DEVICE_ADMIN); +} +``` + +![](device-mgmt-activate-device-admin.png) + +如果用户选择"Activate",程序就会获取设备管理员权限并可以开始配置和执行策略。 +当然,程序也需要做好处理用户选择放弃激活的准备,比如用户点击了“取消”按钮,返回键或者HOME键的情况。因此,如果有必要的话,策略设置中的*[onResume()](http://developer.android.com/reference/android/app/Activity.html#onResume())*方法需要加入重新评估的逻辑判断代码,以便将设备管理激活选项展示给用户。 + +## 实施设备策略控制 + +在设备管理权限成功激活后,程序就会根据请求的策略来配置设备策略管理器。要牢记,新策略会被添加到每个版本的Android中。所以你需要在程序中做好平台版本的检测,以便新策略能被老版本平台很好的支持。例如,“密码中含有的最少大写字符数”这个安全策略只有在高于API Level 11(Honeycomb)的平台才被支持,以下代码则演示了如何在运行时检查版本: + +```java +DevicePolicyManager mDPM = (DevicePolicyManager) + context.getSystemService(Context.DEVICE_POLICY_SERVICE); +ComponentName mPolicyAdmin = new ComponentName(context, PolicyAdmin.class); +... +mDPM.setPasswordQuality(mPolicyAdmin, PASSWORD_QUALITY_VALUES[mPasswordQuality]); +mDPM.setPasswordMinimumLength(mPolicyAdmin, mPasswordLength); +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mDPM.setPasswordMinimumUpperCase(mPolicyAdmin, mPasswordMinUpperCase); +} +``` + +这样程序就可以执行策略了。当程序无法访问正确的锁屏密码的时候,通过设备策略管理器(Device Policy Manager)API可以判断当前密码是否适用于请求的策略。如果当前锁屏密码满足策略,设备管理API不会采取纠正措施。明确地启动设置程序中的系统密码更改界面是应用程序的责任。例如: + +```java +if (!mDPM.isActivePasswordSufficient()) { + ... + // Triggers password change screen in Settings. + Intent intent = + new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); + startActivity(intent); +} +``` + +一般来说,用户可以从可用的锁屏机制中任选一个,例如“无”、“图案”、“PIN码”(数字)或密码(字母数字)。当一个密码策略配置好后,那些比已定义密码策略弱的密码会被禁用。比如,如果配置了密码级别为“Numeric”,那么用户只可以选择PIN码(数字)或者密码(字母数字)。 + +一旦设备通过设置适当的锁屏密码处于被保护的状态,应用程序便允许访问受保护的内容。 + +```java +if (!mDPM.isAdminActive(..)) { + // Activates device administrator. + ... +} else if (!mDPM.isActivePasswordSufficient()) { + // Launches password set-up screen in Settings. + ... +} else { + // Grants access to secure content. + ... + startActivity(new Intent(context, SecureActivity.class)); +} +``` diff --git a/security/device-mgmt-activate-device-admin.png b/security/device-mgmt-activate-device-admin.png new file mode 100755 index 000000000..1be1831e3 Binary files /dev/null and b/security/device-mgmt-activate-device-admin.png differ diff --git a/security/enterprise/device-management-policy.md b/security/enterprise/device-management-policy.md index bbfefecec..478a4e44b 100644 --- a/security/enterprise/device-management-policy.md +++ b/security/enterprise/device-management-policy.md @@ -1,23 +1,23 @@ -# 使用设备管理条例增强安全性 +# 使用设备管理策略增强安全性 > 编写:[craftsmanBai](https://github.com/craftsmanBai) - - 原文: +Android 2.2(API Level 8)之后,Android平台通过设备管理API提供系统级的设备管理能力。 -在Android 2.2(API Level 8)之后,Android平台通过设备管理API提供了系统级的设备管理能力。 +在这一小节中,你将学到如何通过使用设备管理策略创建安全敏感的应用程序。比如某应用可被配置为:在给用户显示受保护的内容之前,确保已设置一个足够强度的锁屏密码。 -在这一课中,你将学到如何通过使用设备管理策略创建一个对安全敏感的应用程序。比如某应用可被配置为:在给用户显示受保护的内容之前,确保已设置一个足够强度的锁屏密码。 - -## 定义声明你的策略 +## 定义并声明你的策略 首先,你需要定义多种在功能层面提供支持的策略。这些策略可以包括屏幕锁密码强度、密码过期时间以及加密等等方面。 你须在res/xml/device_admin.xml中声明选择的策略集,它将被应用强制实行。在Android manifest也需要引用声明的策略集。 -每个声明的策略对应[DevicePolicyManager](http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html)中一些相关设备的策略方法(例如定义最小密码长度或最少大写字母字符数)。如果一个应用尝试调用在XML中没有对应策略的方法,程序在运行时便会抛出一个[SecurityException](http://developer.android.com/reference/java/lang/SecurityException.html)异常。 + +每个声明的策略对应[DevicePolicyManager](http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html)中一些相关设备的策略方法(例如定义最小密码长度或最少大写字母字符数)。如果一个应用尝试调用XML中没有对应策略的方法,程序在会运行时抛出一个[SecurityException](http://developer.android.com/reference/java/lang/SecurityException.html)异常。 如果应用程序试图管理其他策略,那么强制锁force-lock之类的其他权限就会发挥作用。正如你将看到的,作为设备管理权限激活过程的一部分,声明策略的列表会在系统屏幕上显示给用户。 如下代码片段在res/xml/device_admin.xml中声明了密码限制策略: -``` +```xml @@ -26,7 +26,7 @@ ``` 在Android manifest引用XML策略声明: -``` +```xml = Build.VERSION_CODES.HONEYCOMB) { } ``` -这样程序就可以执行策略了。当程序无法访问使用的正确的锁屏密码的时候,通过设备策略管理器(Device Policy Manager)API可以判断当前密码是否适用于请求的策略。如果当前锁屏密码满足策略,设备管理API不会采取纠正措施。明确地启动设置程序中的系统密码更改界面是应用程序的责任。例如: +这样程序就可以执行策略了。当程序无法访问正确的锁屏密码的时候,通过设备策略管理器(Device Policy Manager)API可以判断当前密码是否适用于请求的策略。如果当前锁屏密码满足策略,设备管理API不会采取纠正措施。明确地启动设置程序中的系统密码更改界面是应用程序的责任。例如: -``` +```java if (!mDPM.isActivePasswordSufficient()) { ... // Triggers password change screen in Settings. @@ -121,10 +120,11 @@ if (!mDPM.isActivePasswordSufficient()) { } ``` -一般来说,用户可以从可用的锁屏机制中任选一个,例如“无”、“图案”、“PIN码”(数字)或密码(字母数字)。当一个密码策略配置好后,那些比已定义密码策略弱的密码会被禁用。比如,如果配置了密码级别为“数字”,那么用户只可以选择PIN码(数字)或者密码(字母数字)。 -一旦设备通过设置适当的锁屏密码并保护好后,应用程序便被允许访问受保护的内容。 +一般来说,用户可以从可用的锁屏机制中任选一个,例如“无”、“图案”、“PIN码”(数字)或密码(字母数字)。当一个密码策略配置好后,那些比已定义密码策略弱的密码会被禁用。比如,如果配置了密码级别为“Numeric”,那么用户只可以选择PIN码(数字)或者密码(字母数字)。 -``` +一旦设备通过设置适当的锁屏密码处于被保护的状态,应用程序便允许访问受保护的内容。 + +```java if (!mDPM.isAdminActive(..)) { // Activates device administrator. ... diff --git a/security/enterprise/index.md b/security/enterprise/index.md deleted file mode 100644 index 12bc8f321..000000000 --- a/security/enterprise/index.md +++ /dev/null @@ -1,13 +0,0 @@ -# 为企业开发 - -> 编写:[craftsmanBai](https://github.com/craftsmanBai) - - 原文:[http://developer.android.com/training/enterprise/index.html](http://developer.android.com/training/enterprise/index.html) - - -在这堂课中,你将会学到API和为企业开发时应用程序的技术。 - -## Lessons - -* [使用设备管理策略加强安全](device-management-policy.html) - - 在这堂课中,你将学会如何学会如何去创建一个通过使用设备管理策略管理内容访问权限的具有安全意识的应用程序。 - diff --git a/security/security-ssl.md b/security/security-ssl.md old mode 100644 new mode 100755 index 2a045ee43..f7529e9e5 --- a/security/security-ssl.md +++ b/security/security-ssl.md @@ -2,22 +2,21 @@ > 编写:[craftsmanBai](https://github.com/craftsmanBai) - - 原文: - SSL,传输层安全([TSL](http://en.wikipedia.org/wiki/Transport_Layer_Security)),是一个常见的用来加密客户端和服务器通信的模块。 -但是应用程序错误地使用SSL可能会导致应用程序的数据在网络中被恶意攻击者拦截。为了帮助你确保这种情况不在你的应用程序中发生,这篇文章突出讲解了使用网络安全协议常见的陷阱和使用[Public-Key Infrastructure(PKI)](http://en.wikipedia.org/wiki/Public-key_infrastructure)时一些值得关注的问题。 +但是应用程序错误地使用SSL可能会导致应用程序的数据在网络中被恶意攻击者拦截。为了帮助你确保这种情况不在你的应用中发生,这篇文章主要说明使用网络安全协议常见的陷阱和使用[Public-Key Infrastructure(PKI)](http://en.wikipedia.org/wiki/Public-key_infrastructure)时一些值得关注的问题。 ## 概念 -一个典型的SSL使用场景是,服务器配置中包含了一个证书,并且有匹配的公钥和私钥。作为SSL客户端和服务端握手的一部分,服务端通过使用[public-key cryptography(公钥加密算法)](http://en.wikipedia.org/wiki/Public-key_cryptography)进行证书签名来证明它有私钥。 +一个典型的SSL使用场景是,服务器配置中包含了一个证书,有匹配的公钥和私钥。作为SSL客户端和服务端握手的一部分,服务端通过使用[public-key cryptography(公钥加密算法)](http://en.wikipedia.org/wiki/Public-key_cryptography)进行证书签名来证明它有私钥。 -然而,任何人都可以生成他们自己的证书和私钥,因此一次简单的握手不能证明服务端具有匹配证书公钥的私钥。一种解决这个问题的方法是让客户端拥有一套或者更多的可信赖的证书。如果服务端提供的证书不在其中,那么它将不能得到客户端的信任。 +然而,任何人都可以生成他们自己的证书和私钥,因此一次简单的握手不能证明服务端具有匹配证书公钥的私钥。一种解决这个问题的方法是让客户端拥有一套或者更多可信赖的证书。如果服务端提供的证书不在其中,那么它将不能得到客户端的信任。 -这种简单的方法有一些缺陷。服务端应该根据时间升级到强壮的密钥(key rotation),更新证书中的公钥。不幸的是,现在客户端app需要根据服务端配置的变化来进行更新。如果服务端不在应用程序开发者的控制下,问题将变得更加麻烦,比如它是一个第三方网络服务。如果程序需要和任意的服务器进行对话,例如web浏览器或者email应用,这种方法也会带来问题。 +这种简单的方法有一些缺陷。服务端应该根据时间升级到强壮的密钥(key rotation),更新证书中的公钥。不幸的是,现在客户端应用需要根据服务端配置的变化来进行更新。如果服务端不在应用程序开发者的控制下,问题将变得更加麻烦,比如它是一个第三方网络服务。如果程序需要和任意的服务器进行对话,例如web浏览器或者email应用,这种方法也会带来问题。 -为了解决这个问题,服务端通常配置了知名的的发行者证书(称为[Certificate Authorities(CAs)](http://en.wikipedia.org/wiki/Certificate_authority).提供的平台通常包含了一系列知名可信赖的CAs。在Android4.2(Jelly Bean),Android现在包含了超过100CAs在每个发行版中更新。和服务端相似的是,一个CA拥有一个证书和一个私钥。当为一个服务端发布颁发证书的时候,CA用它的私钥为服务端签名。客户端可以通过服务端拥有被已知平台CA签名的证书来确认服务端。 +为了解决这个问题,服务端通常配置了知名的的发行者证书(称为[Certificate Authorities(CAs)](http://en.wikipedia.org/wiki/Certificate_authority).提供的平台通常包含了一系列知名可信赖的CAs。Android4.2(Jelly Bean)包含了超过100CAs并在每个发行版中更新。和服务端相似的是,一个CA拥有一个证书和一个私钥。当为一个服务端发布颁发证书的时候,CA用它的私钥为服务端签名。客户端可以通过服务端拥有被已知平台CA签名的证书来确认服务端。 然而,使用CAs又带来了其他的问题。因为CA为许多服务端证书签名,你仍然需要其他的方法来确保你对话的是你想要的服务器。为了解决这个问题,使用CA签名的的证书通过特殊的名字如 gmail.com 或者带有通配符的域名如 *.google.com 来确认服务端。 -下面这个例子会使这些概念具体化一些。在这个片段中,[openssl](http://www.openssl.org/docs/apps/openssl.html)工具的客户端命令关注Wikipedia服务端证书信息。端口为443因为默认为HTTPS。这条命令将open s_client的输出发送给openssl x509,根据[X.509 standard](http://en.wikipedia.org/wiki/X.509)格式化证书中的内容。特别的是,这条命令需要subject参数,包含服务端名字和issuer来确认CA。 +下面这个例子会使这些概念具体化一些。[openssl](http://www.openssl.org/docs/apps/openssl.html)工具的客户端命令关注Wikipedia服务端证书信息。端口为443,因为默认为HTTPS。这条命令将open s_client的输出发送给openssl x509,根据[X.509 standard](http://en.wikipedia.org/wiki/X.509)格式化证书中的内容。特别的是,这条命令需要subject,包含服务端名字和issuer来确认CA。 ``` $ openssl s_client -connect wikipedia.org:443 | openssl x509 -noout -subject -issuer @@ -25,7 +24,7 @@ subject= /serialNumber=sOrr2rKpMVP70Z6E9BT5reY008SJEdYv/C=US/O=*.wikipedia.org/O issuer= /C=US/O=GeoTrust, Inc./CN=RapidSSL CA ``` -可以看到是RapidSSL CA颁发给匹配*.wikipedia.org服务端的证书。 +可以看到RapidSSL CA颁发给匹配*.wikipedia.org的服务端证书。 ## 一个HTTP的例子 @@ -38,11 +37,11 @@ InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out); ``` -是的,它就是这么简单。如果你想要剪裁HTTP的请求,你可以把它投到 [HttpURLConnection](http://developer.android.com/reference/java/net/HttpURLConnection.html).Android关于[HttpURLConnetcion](http://developer.android.com/reference/java/net/HttpURLConnection.html)文档中还有进一步的例子关于怎样去处理请求,响应头,posting的内容,管理cookies,使用代理,抓responses等等。但是就这些确认证书和域名的细节而言,Android框架已经通过API来为你考虑这些细节。下面是其他的需要关注的问题。 +是的,它就是这么简单。如果你想要修改HTTP的请求,你可以把它扔到 [HttpURLConnection](http://developer.android.com/reference/java/net/HttpURLConnection.html).Android关于[HttpURLConnetcion](http://developer.android.com/reference/java/net/HttpURLConnection.html)文档中还有更贴切的例子关于怎样去处理请求、响应头、posting的内容、cookies管理、使用代理、抓responses等等。但是就这些确认证书和域名的细节而言,Android框架已经通过API来为你考虑这些细节。下面是其他需要关注的问题。 ## 服务器普通问题的验证 -假设替代从[getInputStream()](http://developer.android.com/reference/java/net/URLConnection.html#getInputStream())接受内容,它抛出了一个异常: +假设从[getInputStream()](http://developer.android.com/reference/java/net/URLConnection.html#getInputStream()接受内容,会抛出一个异常: ``` javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. @@ -58,21 +57,24 @@ javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorExcepti ``` 这种情况发生的原因包括: + 1.[颁布证书给服务器的CA不是知名的。](http://developer.android.com/training/articles/security-ssl.html#UnknownCa) + 2.[服务器证书不是CA签名的而是自己签名的。](http://developer.android.com/training/articles/security-ssl.html#SelfSigned) + 3.[服务器配置缺失了中间CA](http://developer.android.com/training/articles/security-ssl.html#MissingCa) -下面将会分别讨论当你保持和服务器的安全连接时如何去解决这些问题。 +下面将会分别讨论当你和服务器安全连接时如何去解决这些问题。 ## 无法识别证书机构 -在这种情况中,[SSLHandshakeException](http://developer.android.com/reference/javax/net/ssl/SSLHandshakeException.html)异常产生的原因是你有一个不被系统信任的CA。可能是你的证书来源于新CA而不被安卓信任,也可能是你的app运行版本较老没有CA。更多的时候,一个CA不知名是因为它不是公开的CA,而是政府,公司,教育机构等组织私有的。 +在这种情况中,[SSLHandshakeException](http://developer.android.com/reference/javax/net/ssl/SSLHandshakeException.html)异常产生的原因是你有一个不被系统信任的CA。可能是你的证书来源于新CA而不被安卓信任,也可能是你的应用运行版本较老没有CA。更多的时候,一个CA不知名是因为它不是公开的CA,而是政府,公司,教育机构等组织私有的。 幸运的是,你可以教会[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)学会信任特殊的CA。过程可能会让人感到有一些费解,下面这个例子是从[InputStream](http://developer.android.com/reference/java/io/InputStream.html)中获得特殊的CA,使用它去创建一个密钥库,用来创建和初始化[TrustManager](http://developer.android.com/reference/javax/net/ssl/TrustManager.html)。[TrustManager](http://developer.android.com/reference/javax/net/ssl/TrustManager.html)是系统用来验证服务器证书的,这些证书通过使用[TrustManager](http://developer.android.com/reference/javax/net/ssl/TrustManager.html)信任的CA和密钥库中的密钥创建。 -在新的TrustManager中,下面这个例子初始化了一个新的[SSLContext](http://developer.android.com/reference/javax/net/ssl/SSLContext.html),提供了一个[SSLSocketFactory](http://developer.android.com/reference/javax/net/ssl/SSLSocketFactory.html),你可以从[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)中覆盖[SSLSocketFactory](http://developer.android.com/reference/javax/net/ssl/SSLSocketFactory.html)。这样连接中会使用你的CA来进行证书验证。 +给定一个新的TrustManager,下面这个例子初始化了一个新的[SSLContext](http://developer.android.com/reference/javax/net/ssl/SSLContext.html),提供了一个[SSLSocketFactory](http://developer.android.com/reference/javax/net/ssl/SSLSocketFactory.html),你可以覆盖来自[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)的默认[SSLSocketFactory](http://developer.android.com/reference/javax/net/ssl/SSLSocketFactory.html)。这样连接时会使用你的CA来进行证书验证。 -下面是一个华盛顿的大学的CA使用例子 +下面是一个华盛顿的大学的组织性的CA的使用例子 ``` // Load CAs from an InputStream @@ -112,19 +114,20 @@ InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out); ``` -使用一个常用的知道你的CA的TrustManager,系统可以确认你的服务器证书来自于一个可信任的发行者。 +使用一个常用的了解你CA的TrustManager,系统可以确认你的服务器证书来自于一个可信任的发行者。 -注意:许多网站描述了一种简陋的二选一方案是否安装TrustManager。如果这样你最好不要加密通讯过程,因为任何人都可以在公共wifi热点下,使用他们伪装成你的服务器的代理发送你的用户流量,进行DNS欺骗,来攻击你的用户。然后攻击者便可记录用户密码和其他个人资料。这种方式奏效是因为攻击者可以生成一个证书,并且缺少可以验证该证书是否来自受信任的来源的TrustManager。你的app可以同任何人会话。所以不这样做,暂时的也不行。如果你能总是让你的app信任服务器证书的发行者,那么你可以这么做。 +注意:许多网站提供了简陋的二选一方案是否安装TrustManager。如果你这样做还不如不加密通讯过程,因为任何人都可以在公共wifi热点下,使用伪装成你的服务器的代理发送你的用户流量,进行DNS欺骗,来攻击你的用户。然后攻击者便可记录用户密码和其他个人资料。这种方式奏效是因为攻击者可以生成一个证书,并且缺少可以验证该证书是否来自受信任的来源的TrustManager。你的应用可以同任何人会话。所以不这样做,暂时的也不行。如果你能始终让你的应用信任服务器证书的发行者,那么你可以这么做。 ## 自签名服务器证书 第二种[SSLHandshakeException](http://developer.android.com/reference/javax/net/ssl/SSLHandshakeException.html)取决于自签名证书,意味着服务器就是它自己的CA。这同未知证书权威机构类似,因此你同样可以用前面部分中提到的方法。 -你可以创建你自己的TrustManager,这一次直接信任服务器证书。这有之前提到的将你的app直接捆绑证书的所有缺点,但是可以安全的执行。然而,你应该小心确保你的自签名证书拥有合适的强密钥。到2012年,一个2048位65537指数位一年到期的RSA签名是合理的。当轮换密钥时,你应该查看权威机构(比如[NIST](http://www.nist.gov/))的建议([recommendation](http://csrc.nist.gov/groups/ST/key_mgmt/index.html))来了解哪种密钥是合适的。 + +你可以创建你自己的TrustManager,这一次直接信任服务器证书。有之前提到的将你的应用直接捆绑证书的所有缺点,但是可以安全的执行。然而你应该小心确保你的自签名证书拥有合适的强密钥。到2012年,一个2048位65537指数位一年到期的RSA签名是合理的。当轮换密钥时,你应该查看权威机构(比如[NIST](http://www.nist.gov/))的建议([recommendation](http://csrc.nist.gov/groups/ST/key_mgmt/index.html))来了解哪种密钥是合适的。 ## 缺少中间证书颁发机构 -第三种SSLHandshakeException情况的产生于缺少中间CA。大多数公开的CA不直接给服务器签名。相反,他们使用它们主要的机构(简称根认证机构)证书来给中间认证机构签名,他们这样做,因此根认证机构可以离线存储减少危险。然而,操作系统典型的比如安卓只信任直接地根认证机构,在服务器证书(由中间证书颁发机构签名)和证书验证者(只知道根认证机构)之间留下了一个缺口。为了解决这个问题,服务器并不SSL握手的过程中向客户端发送它的证书,而是一系列从服务器到必经的任何中间机构到达根认证机构的证书。 +第三种SSLHandshakeException情况的产生于缺少中间CA。大多数公开的CA不直接给服务器签名。相反,他们使用它们主要的机构(简称根认证机构)证书来给中间认证机构签名,他们这样做,因此根认证机构可以离线存储减少危险。然而,操作系统典型的比如安卓只信任直接地根认证机构,在服务器证书(由中间证书颁发机构签名)和证书验证者(只知道根认证机构)之间留下了一个缺口。为了解决这个问题,服务器并不在SSL握手的过程中只向客户端发送它的证书,而是一系列的从服务器到必经的任何中间机构到达根认证机构的证书。 下面是一个 mail.google.com证书链,以openssls_client命令显示: @@ -139,7 +142,7 @@ Certificate chain --- ``` 这里显示了一台服务器发送了一个Thawte SGC CA为mail.google.com颁发的证书,Thawte SGC CA是一个中间证书颁发机构,Thawte SGC CA的证书由被安卓信任的Verisign CA颁发。 -然而,配置一台服务器不包括中间证书机构是不常见的。例如,一台服务器导致安卓浏览器的错误和app的异常: +然而,配置一台服务器不包括中间证书机构是不常见的。例如,一台服务器导致安卓浏览器的错误和应用的异常: ``` @@ -152,10 +155,11 @@ Certificate chain ``` 更有趣的是,用大所属桌面浏览器访问这台服务器不会导致类似于完全未知CA的或者自签名的服务器证书导致的错误。这是因为大多数桌面浏览器缓存随着时间的推移信任中间证书机构。一旦浏览器访问并且从一个网站了解到的一个中间证书机构,下一次它将不需要中间证书机构包含证书链。 -一些站点故意这样做为的是让二级服务器用来提供资源服务。比如,他们可能会让他们的主HTML页面用一台拥有全部证书链的服务器来提供,但是像图片,CSS,或者JavaScript等这样的资源用不包含CA的服务器来提供,以此节省带宽。不幸的是,有时这些服务器可能会提供一个在app中呼叫的web服务。 +一些站点故意这样做目的是让二级服务器用来提供资源服务。比如,他们可能会让他们的主HTML页面用一台拥有全部证书链的服务器来提供,但是像图片,CSS,或者JavaScript等这样的资源用不包含CA的服务器来提供,以此节省带宽。不幸的是,有时这些服务器可能会提供一个在应用中调用的web服务。 这里有两种解决这些问题的方法: * 配置服务器使它包含服务器链中的中间证书颁发机构 + * 或者,像对待不知名的CA一样对待中间CA,并且创建一个TrustManager来直接信任它,就像在前两节中做的那样。 @@ -176,14 +180,14 @@ java.io.IOException: Hostname 'example.com' was not verified ``` 服务器配置错误可能会导致这种情况发生。服务器配置了一个证书,这个证书没有匹配的你想连接的服务器的subject或者命名空间中二选一的subject。一个证书被许多不同的服务器使用是可能的。比如,使用 [openssl](http://www.openssl.org/docs/apps/openssl.html) s_client -connect google.com:443 |openssl x509 -text 查看google证书,你可以看到一个subject支持 *google.con *.youtube.com, *.android.com或者其他的。这种错误只会发生在你在连接的服务器名称没有被证书列为可接受。 -不幸的是另外一种原因也会导致这种情况发生:[虚拟化服务](http://en.wikipedia.org/wiki/Virtual_hosting))。当用HTTP同拥有一个以上主机名的服务器共享时,web服务器可以从 HTTP/1.1请求分别出客户端需要的目标主机名。不行的是,使用HTTPS会使情况变得复杂,因为服务器必须知道在发现HTTP请求前返回哪一个证书。为了解决这个问题,新版本的SSL,特别是TLSV.1.0和之后的版本,支持[服务器名指示(SNI)](http://en.wikipedia.org/wiki/Server_Name_Indication),允许SSL客户端为服务端指定目标主机名,从而返回正确的证书。 -幸运的是,从安卓2.3开始,[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)支持SNI。不幸的是,Apache HTTP客户端不这样,这也是我们不鼓励用它的原因之一。如果你需要支持安卓2.2或者更老的版本或者Apache HTTP客户端的一个解决方法是,建立一个可选的虚拟化服务并且使用特别的端口,这样服务端就能够清楚该返回哪一个证书。 +不幸的是另外一种原因也会导致这种情况发生:[虚拟化服务](http://en.wikipedia.org/wiki/Virtual_hosting)。当用HTTP同时拥有一个以上主机名的服务器共享时,web服务器可以从 HTTP/1.1请求中找到客户端需要的目标主机名。不行的是,使用HTTPS会使情况变得复杂,因为服务器必须知道在发现HTTP请求前返回哪一个证书。为了解决这个问题,新版本的SSL,特别是TLSV.1.0和之后的版本,支持[服务器名指示(SNI)](http://en.wikipedia.org/wiki/Server_Name_Indication),允许SSL客户端为服务端指定目标主机名,从而返回正确的证书。 +幸运的是,从安卓2.3开始,[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)支持SNI。不幸的是,Apache HTTP客户端不这样,这也是我们不鼓励用它的原因之一。如果你需要支持安卓2.2或者更老的版本或者Apache HTTP客户端,一个解决方法是建立一个可选的虚拟化服务并且使用特别的端口,这样服务端就能够清楚该返回哪一个证书。 -用不使用你的虚拟服务的主机名更换[HostnameVerifier](http://developer.android.com/reference/javax/net/ssl/HostnameVerifier.html)而不是用服务器默认的来替换,是很重要的选择。 +采用不使用你的虚拟服务的主机名[HostnameVerifier](http://developer.android.com/reference/javax/net/ssl/HostnameVerifier.html)而不是服务器默认的来替换,是很重要的选择。 -注意:替换[HostnameVerifier](http://developer.android.com/reference/javax/net/ssl/HostnameVerifier.html)可能会非常危险,如果另外一个虚拟服务不在你的控制下,因为中间人攻击可能会直接使流量到达另外一台服务器而超出你的考虑。 -如果你仍然确定你想要覆盖主机名验证,这里有一个为了单[URLConnection](http://developer.android.com/reference/java/net/URLConnection.html)替换验证过程的例子 +注意:替换[HostnameVerifier](http://developer.android.com/reference/javax/net/ssl/HostnameVerifier.html)可能会非常危险,如果另外一个虚拟服务不在你的控制下,中间人攻击可能会直接使流量到达另外一台服务器而超出你的预想。 +如果你仍然确定你想覆盖主机名验证,这里有一个为单[URLConnection](http://developer.android.com/reference/java/net/URLConnection.html)替换验证过程的例子 @@ -212,10 +216,10 @@ copyInputStreamToOutputStream(in, System.out); ## 关于直接使用SSL Socket的警告 -到目前为止,这些例子聚焦于使用HttpsURLConnection上。有时一些app需要让SSL和HTTP分开。举个例子,一个email应用可能会使用SSL的变种,SMTP,POP3,IMAP等。在那些例子中,应用程序会想使用[SSLSocket](http://developer.android.com/reference/javax/net/ssl/SSLSocket.html)直接连接,与HttpsURLConnection做的方法相似。 -这种技术到目前为止处理了证书验证问题,也应用于SSLSocket中。事实上,当使用常规的TrustManager时,传递给HttpsURLConnection的是SSLSocketFactory。如果你需要一个带常规的SSLSocket的TrustManager,跟随下面的步骤使用SSLSocketFactory来创建你的SSLSocket。 -注意:SSLSocket不具有主机名验证功能。它取决于它自己的主机名验证,通过传入预期的主机名调用[getDefaultHostNameVerifier()](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html#getDefaultHostnameVerifier())。进一步需要注意的是,当发生错误时,[HostnameVerifier.verify()](http://developer.android.com/reference/javax/net/ssl/HostnameVerifier.html#verify(java.lang.String, javax.net.ssl.SSLSession))不知道抛出异常,而是返回一个布尔值,你需要进一步明确的检查。 -下面是一个演示的方法。这个例子演示了当它连接gmail.com 443端口并且没有SNI支持的时候,你将会接收到一个mail.google.com的证书。你需要确保证书的确是mail.google.com的。 +到目前为止,这些例子聚焦于使用HttpsURLConnection上。有时一些应用需要让SSL和HTTP分开。举个例子,一个email应用可能会使用SSL的变种,SMTP,POP3,IMAP等。在那些例子中,应用程序会想使用[SSLSocket](http://developer.android.com/reference/javax/net/ssl/SSLSocket.html)直接连接,与HttpsURLConnection做的方法相似。 +这种技术到目前为止处理了证书验证问题,也应用于SSLSocket中。事实上,当使用常规的TrustManager时,传递给HttpsURLConnection的是SSLSocketFactory。如果你需要一个带常规的SSLSocket的TrustManager,采取下面的步骤使用SSLSocketFactory来创建你的SSLSocket。 +注意:SSLSocket不具有主机名验证功能。它取决于它自己的主机名验证,通过传入预期的主机名调用[getDefaultHostNameVerifier()](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html#getDefaultHostnameVerifier())。进一步需要注意的是,当发生错误时,[HostnameVerifier.verify()](http://developer.android.com/reference/javax/net/ssl/HostnameVerifier.html#verify(java.lang.String, javax.net.ssl.SSLSession)不知道抛出异常,而是返回一个布尔值,你需要进一步明确的检查。 +下面是一个演示的方法。这个例子演示了当它连接gmail.com 443端口并且没有SNI支持的时候,你将会收到一个mail.google.com的证书。你需要确保证书的确是mail.google.com的。 ``` @@ -240,14 +244,30 @@ socket.close(); ``` ## 黑名单 -SSL 主要依靠CA来确认证书来自正确无误服务器和域名的所有者。在较少的情况下,CA被欺骗,或者在[Comodo](http://en.wikipedia.org/wiki/Comodo_Group#Breach_of_security)和[DigiNotar](http://en.wikipedia.org/wiki/DigiNotar)的例子中,一个主机名的证书被颁发给了除了服务器和域名的拥有者之外的人,导致被破坏。 +SSL 主要依靠CA来确认证书来自正确无误服务器和域名的所有者。少数情况下,CA被欺骗,或者在[Comodo](http://en.wikipedia.org/wiki/Comodo_Group#Breach_of_security)和[DigiNotar](http://en.wikipedia.org/wiki/DigiNotar)的例子中,一个主机名的证书被颁发给了除了服务器和域名的拥有者之外的人,导致被破坏。 为了减少这着危险,安卓可以将一些黑名单或者整个CA列入黑名单。尽管名单是以前是嵌入操作系统的,从安卓4.2开始,这个名单在以后的方案中可以远程更新。 ## 阻塞 -一个app可以通过阻塞技术保护它自己免于受虚假证书的欺骗。这是简单运用使用未知的CA的例子,限制app信任的CA的仅来自被app使用的服务器。阻止了来自系统中另外一百多个CA的欺骗而导致的app安全通道的破坏。 +一个应用可以通过阻塞技术保护它自己免于受虚假证书的欺骗。这是简单运用使用未知CA的例子,限制应用信任的CA仅来自被应用使用的服务器。阻止了来自系统中另外一百多个CA的欺骗而导致的应用安全通道的破坏。 ## 客户端验证 -这篇文章聚焦在SSL的使用者安全的同服务器对话上。SSL也支持服务端通过验证客户端的证书来确认客户端的身份。文外,这种技术也与TrustManager的特性相似。可以参考在[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)文档中关于创建一个常规的[KeyManager](http://developer.android.com/reference/javax/net/ssl/KeyManager.html)的讨论。 +这篇文章聚焦在SSL的使用者同服务器的安全对话上。SSL也支持服务端通过验证客户端的证书来确认客户端的身份。这种技术也与TrustManager的特性相似。可以参考在[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)文档中关于创建一个常规的[KeyManager](http://developer.android.com/reference/javax/net/ssl/KeyManager.html)的讨论。 + + +## nogotofail:网络流量安全测试工具 + +对于已知的TLS/SSL漏洞和错误,nogotofail提供了一个简单的方法来确认你的应用程序是安全的。它是一个自动化的、强大的、用于测试网络的安全问题可扩展性的工具,任何设备的网络流量都可以通过它。 +nogotofail主要应用于三种场景: + +* 发现错误和漏洞。 + +* 验证修补程序和等待回归。 + +* 了解应用程序和设备产生的交通。 + +nogotofail 可以工作在Android,iOS,Linux,Windows,Chrome OS,OSX环境下,事实上任何需要连接到Internet的设备都可以。Android和Linux环境下有简单易用获取通知的客户端配置设置,以及本身可以作为靶机,部署为一个路由器,VPN服务器,或代理。 +你可以在nogotofail开源项目访问该工具。 + diff --git a/security/security-tips.md b/security/security-tips.md old mode 100644 new mode 100755 index 9bebc1a13..1000cc9f1 --- a/security/security-tips.md +++ b/security/security-tips.md @@ -2,231 +2,240 @@ > 编写:[craftsmanBai](https://github.com/craftsmanBai) - - 原文: -Android的安全特性体现在操作系统显著地减少了应用程序的安全问题带来的影响。你可以在默认的系统设置和文件权限设置中建立app,避免了安全带来的一些头疼问题。 -帮助你建立app的一些核心安全特性如下: +Android的安全性体现在系统显著地减少了应用程序安全问题带来的影响。你可以在默认的系统设置和文件权限设置的环境下建立应用,避免了一堆头疼的安全问题。 -* Android应用程序沙盒,将你的app数据和代码执行同其他程序隔开。 -* 鲁棒性实现常见安全功能的应用框架,例如密码学应用,权限控制,安全IPC -* ASLR, NX,ProPolice,safe_iop,OpenBSD dlmalloc,OpenBSD calloc,Linux mmap_min_addr等技术减少了常见内存管理错误。 -* 加密文件系统可以保护丢失的或被盗走的设备的数据。 -* 用户权限控制限制了访问系统详细情况和用户数据。 -* 应用程序权限以单个app为基础控制了应用程序的数据。 +帮助你建立应用的部分核心安全特性如下: -尽管如此,熟悉Android安全特性是很重要的。遵守这些习惯将其作为好的代码风格,将会减少不经意间给用户带来的安全问题。 +* Android应用程序沙盒,将你的应用数据和代码同其他程序隔开。 +* 具有鲁棒性实现了常见安全功能的应用框架,例如密码学应用,权限控制,安全IPC +* 使用ASLR, NX,ProPolice,safe_iop,OpenBSD dlmalloc,OpenBSD calloc,Linux mmap_min_addr等技术,减少了常见内存管理错误。 +* 加密文件系统可以保护丢失或被盗走的设备数据。 +* 用户权限控制限制访问系统详细情况和用户数据。 +* 应用程序权限以单个应用为基础控制其数据。 + +尽管如此,熟悉Android安全特性仍然很重要。遵守这些习惯将其作为优秀的代码风格,能够减少无意间给用户带来的安全问题。 ## 数据存储 +--------------------------------------------------------------- -对于一个Android的应用程序来讲,最为常见的安全问题是存放在设备上的数据能否被其他app获取。在设备上存放保存数据有三种基本的方式: +对于一个Android的应用程序来说,最为常见的安全问题是存放在设备上的数据能否被其他应用获取。在设备上存放数据基本方式有三种: ### 使用内存储器 -默认情况下,你在[内存储器](http://developer.android.com/guide/topics/data/data-storage.html#filesInternal)中创建的文件只有你的app可以访问。这种机制被Android加强了并且对于大多数应用程序都是有效的。 -你应该避免在IPC文件中使用[MODE_WORLD_WRITEABLE](http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_WRITEABLE)或者[MODE_WORLD_READABLE](http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE)模式,因为它们不对特殊程序提供限制数据访问的功能,它们也不对数据格式提供任何控制。如果你想同其他app的进程共享数据,你可以使用一个[content provider](http://developer.android.com/guide/topics/providers/content-providers.html),它给其他apps提供了可读可写的权限并且可以逐项动态的获取权限。 +默认情况下,你在[内存储器](http://developer.android.com/guide/topics/data/data-storage.html#filesInternal)中创建的文件只有你的应用可以访问。这种机制被Android加强了并且对于大多数应用程序都是有效的。 +你应该避免在IPC文件中使用[MODE_WORLD_WRITEABLE](http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_WRITEABLE)或者[MODE_WORLD_READABLE](http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE)模式,因为它们不为特殊程序提供限制数据访问的功能,它们也不对数据格式进行任何控制。如果你想与其他应用的进程共享数据,可以使用[content provider](http://developer.android.com/guide/topics/providers/content-providers.html),它给其他应用提供了可读写权限以及逐项动态获取权限。 -如果想对一些敏感数据提供特别的保护,你可以选择使用应用程序无法直接获取的密钥来加密本地文件。例如,密钥可以存放在[密钥库](http://developer.android.com/reference/java/security/KeyStore.html),使用用户密码保护的密钥,而不存储在设备上。尽管这种方式不能在具有root权限时监视用户输入的密码的情况下保护数据,但是它可以提供对没有进行[文件系统加密](http://source.android.com/tech/encryption/index.html)的丢失的设备的保护。 +如果想对敏感数据进行特别保护,你可以使用应用程序无法直接获取的密钥来加密本地文件。例如,密钥可以存放在[密钥库](http://developer.android.com/reference/java/security/KeyStore.html)而非设备上,使用用户密码进行保护。尽管这种方式无法在具有root权限时监视用户输入的密码的情况下保护数据,但是它可以为未进行[文件系统加密](http://source.android.com/tech/encryption/index.html)已丢失的设备提供保护。 ### 使用外部存储器 -建立在[外部存储](http://developer.android.com/guide/topics/data/data-storage.html#filesExternal)的文件,比如sd卡,是全局可读写的。 -因为外部存储可以被用户移除并且可被任何应用修改,应用不应该使用外部存储存储敏感信息。 -当处理从外部存储来的数据时应用应该[执行输入验证](http://developer.android.com/training/articles/security-tips.html#InputValidation)(参看输入验证章节) -我们强烈建议应用在动态加载之前不把可执行或者是class文件存储到外部存储中。 -如果一个应用从外部存储检索可执行文件,在动态加载之前,他们应该被签名和加密验证。 +创建于[外部存储](http://developer.android.com/guide/topics/data/data-storage.html#filesExternal)的文件,比如SD卡,是全局可读写的。 +由于外部存储器可被用户移除并且能够被任何应用修改,因此不应使用外部存储存储应用的敏感信息。 +当处理来自外部存储器的数据时,应用程序应该[执行输入验证](http://developer.android.com/training/articles/security-tips.html#InputValidation)(参看输入验证章节) +我们强烈建议应用在动态加载之前不要把可执行文件或class文件存储到外部存储器中。 +如果一个应用从外部存储器检索可执行文件,那么在动态加载之前它们应该进行签名与加密验证。 -### 使用content providers +### 使用Content Providers -[ContentProviders](http://developer.android.com/guide/topics/providers/content-providers.html)提供一个结构存储机制,可以限制你自己的应用,或者导出给其他应用程序允许访问。 -如果你不打算为其他应用提供访问你的[ContentProvider](http://developer.android.com/reference/android/content/ContentProvider.html)功能,在manifest中标记他们为[android:exported=false](http://developer.android.com/guide/topics/manifest/provider-element.html#exported)即可。 -当建立一个由其他应用为使用而导出的[ContentProvider](http://developer.android.com/reference/android/content/ContentProvider.html),你可以为读写指定一个单一的[许可](http://developer.android.com/guide/topics/manifest/provider-element.html#prmsn),或者在manifest中为读写指定确切的许可。我们强烈建议你把你的许可限制在那些必要的事情上来完成临近的任务。 -记住,通常显示新功能稍后加入许可要比把许可拿开并且打断已经存在的用户要容易。 +[ContentProviders](http://developer.android.com/guide/topics/providers/content-providers.html)提供一个结构存储机制,可以限制你自己的应用或者导出给其他应用程序允许访问。 +如果你不打算为其他应用提供访问你的[ContentProvider](http://developer.android.com/reference/android/content/ContentProvider.html)功能,那么在manifest中标记他们为[android:exported=false](http://developer.android.com/guide/topics/manifest/provider-element.html#exported)即可。 +要建立一个给其他应用使用而导出的[ContentProvider](http://developer.android.com/reference/android/content/ContentProvider.html),你可以为读写操作指定一个单一的[permission](http://developer.android.com/guide/topics/manifest/provider-element.html#prmsn),或者在manifest中为读写操作指定确切的许可。我们强烈建议你限制权限给手头要求完成的任务。 +记住,通常显示新功能稍后加入许可比把许可撤走并打断已经存在的用户更容易。 -如果你正在使用ContentProvider在相同开发者的应用间来分享数据,使用签名级别[android:protectionLevel](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel)的许可是更可取的。 -签名许可不需要用户确认,所以这提供一个更好的用户体验并且更能控制ContentProvider访问。 -ContentProviders也可以通过声明[android:grantUriPermissions](http://developer.android.com/guide/topics/manifest/provider-element.html#gprmsn)元素并且在触发组件的Intent对象中使用[FLAG_GRANT_READ_URI_PERMISSION](http://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION)和[FLAG_GRANT_WRITE_URI_PERMISSION](http://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_WRITE_URI_PERMISSION)标志提供更颗粒状的访问。 -这些许可的作用域可以通过[grant-uri-permission](http://developer.android.com/guide/topics/manifest/grant-uri-permission-element.html)元素进一步的限制。 -当访问一个ContentProvider时,使用参数化的查询方法,比如[query()](http://developer.android.com/reference/android/content/ContentProvider.html#query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String)), [update()](http://developer.android.com/reference/android/content/ContentProvider.html#update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[])),和[delete()](http://developer.android.com/reference/android/content/ContentProvider.html#delete(android.net.Uri, java.lang.String, java.lang.String[]))来避免来自不被新人的数据潜在的SQL注入。 -注意,如果提交到方法之前的选择是通过连接用户数据建立的,使用参数化的方法是不够的。 -不要对“写”的许可安全有一个错误的观念 -考虑“写”的许可允许sql语句使得一些数据被确认使用创造性的WHERE从句并且分析结果变为可能。 -例如:一个入侵者可能在通话记录中通过修改一条记录来侦察一个存在的特定的电话号码,只要那个电话号码已经存在。 -如果content provider数据有可预见的结构,“写”许可也许与提供了“读写”等效了。 +如果你使用Content Provider仅在自己的应用中共享数据,使用签名级别[android:protectionLevel](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel)的许可是更可取的。 +签名许可不需要用户确认,当应用使用同样的密钥获取数据时,这提供了更好的用户体验,也更好地控制了Content Provider数据的访问。 +Content Providers也可以通过声明[android:grantUriPermissions](http://developer.android.com/guide/topics/manifest/provider-element.html#gprmsn)并在触发组件的Intent对象中使用[FLAG_GRANT_READ_URI_PERMISSION](http://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION)和[FLAG_GRANT_WRITE_URI_PERMISSION](http://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_WRITE_URI_PERMISSION)标志提供更细致的访问。 +这些许可的作用域可以通过[grant-uri-permission](http://developer.android.com/guide/topics/manifest/grant-uri-permission-element.html)进一步限制。 +当访问一个ContentProvider时,使用参数化的查询方法,比如[query()](http://developer.android.com/reference/android/content/ContentProvider.html#query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String), [update()](http://developer.android.com/reference/android/content/ContentProvider.html#update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]),和[delete()](http://developer.android.com/reference/android/content/ContentProvider.html#delete(android.net.Uri, java.lang.String, java.lang.String[])来避免来自不信任源潜在的SQL注入。 +注意,如果提交方法之前的selection是通过连接用户数据建立的,使用参数化的方法是不够的。 +不要对“写”权限有一个错误的观念。 +考虑“写”权限允许sql语句,使得部分数据使用创造性的WHERE语句并且解析结果变为可能。 +例如:入侵者可能在通话记录中通过修改一条记录来检测某个特定存在的电话号码,只要那个电话号码已经存在。 +如果content provider数据有可预见的结构,提供“写”权限也许等同于同时提供了“读写”权限。 -## 使用权限 +## 使用许可 -因为安卓沙盒使应用程序隔开,程序必须显式地共享资源和数据。为了拥有附加的功能,他们通过声明他们需要的权限,而基本的沙盒不提供这些功能,包括访问设备比如相机的特性。 +因为安卓沙盒将应用程序隔开,程序必须显式地共享资源和数据。它们通过声明他们需要的权限来获取额外的功能,而基本的沙盒不提供这些功能,比如相机访问设备。 ### 请求许可 -我们建议一个应用请求的许可数量最小化,不具有访问敏感的许可可以减少无意中滥用那些许可的风险,可以让用户更能接受,并且使得攻击者对应用减少兴趣。 +我们建议最小化应用请求的许可数量,不具有访问敏感资料的权限可以减少无意中滥用这些权限的风险,可以增加用户接受度,并且减少应用可被攻击者攻击利用。 -如果你的应用有一种可以设计出不需要任何许可的方法,那最好不过。例如:与其请求访问设备信息来建立一个标识,不如建立一个[GUID](http://developer.android.com/reference/java/util/UUID.html)(这个例子也在[Handling User Data](http://developer.android.com/training/articles/security-tips.html#UserData)中有讨论)。 +如果你的应用可以设计成不需要任何许可,那最好不过。例如:与其请求访问设备信息来建立一个标识,不如建立一个[GUID](http://developer.android.com/reference/java/util/UUID.html)(这个例子在[Handling User Data](http://developer.android.com/training/articles/security-tips.html#UserData)中有说明)。 -除了请求许可之外,你的应用可以使用[许可](http://developer.android.com/guide/topics/manifest/permission-element.html)来保护安全敏感的IPC并且会暴露给其他应用:比如[ContentProvider](http://developer.android.com/reference/android/content/ContentProvider.html)。总的来说,我们建议使用访问控制而不是在可能的地方让用户确认许可,因为许可会是用户困惑。例如,考虑在许可上为应用间的IPC通信使用单一开发者提供的[签名保护级别](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel) +除了请求许可之外,你的应用可以使用[permissions](http://developer.android.com/guide/topics/manifest/permission-element.html)来保护可能会暴露给其他应用的安全敏感的IPC:比如[ContentProvider](http://developer.android.com/reference/android/content/ContentProvider.html)。通常来说,我们建议使用访问控制而不是用户权限确认许可,因为许可会使用户感到困惑。例如,考虑在权限设置上为应用间的IPC通信使用单一开发者提供的[签名保护级别](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel) -不要产生许可再次授权,这只有当一个应用通过IPC暴露数据才会发生,因为它有一个指定的许可,但是并不要求它的IPC接口的任何客户端许可。潜在影响的更多细节,和这种问题发生的频率在USENIX: [http://www.cs.be rkeley.edu/~afelt/felt_usenixsec2011.pdf](http://www.cs.berkeley.edu/~afelt/felt_usenixsec2011.pdf)研究论文中都有提供。 +不要泄漏受许可保护的数据。只有当应用通过IPC暴露数据才会发生这种情况,因为它具有特殊权限,却不要求任何客户端的IPC接口有那样的权限。更多细节带来的潜在影响以及这种问题发生的频率在USENIX: [http://www.cs.be rkeley.edu/~afelt/felt_usenixsec2011.pdf](http://www.cs.berkeley.edu/~afelt/felt_usenixsec2011.pdf)研究论文中都有说明。 ### 创建许可 -一般来说,你应该力求建立拥有尽量少许可的应用,直至满足你的安全需要。建立一个新的许可对于大多数应用是相对不常见,因为[系统定义的许可](http://developer.android.com/reference/android/Manifest.permission.html)覆盖很多情况。在适当的地方使用已经存在的许可执行访问检查。 +通常,你应该力求建立拥有尽量少许可的应用,直至满足你的安全需要。建立一个新的许可对于大多数应用相对少见,因为[系统定义的许可](http://developer.android.com/reference/android/Manifest.permission.html)覆盖很多情况。在适当的地方使用已经存在的许可执行访问检查。 -如果你必须建立一个新的许可,考虑是否你能使用[签名许可](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel)完成你的任务。签名许可对用户是透明的并且只允许相同开发者签名的应用访问同应用执行许可检查一样。如果你建立一个[危险的许可](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel),那么用户需要决定是否安装这个应用。这会使其他开发着困惑,也使用户困惑。 +如果必须建立一个新的许可,考虑能否使用[signature protection level](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel)来完成你的任务。签名许可对用户是透明的并且只允许相同开发者签名的应用访问,与应用执行许可检查一样。如果你建立一个[dagerous protction level](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel),那么用户需要决定是否安装这个应用。这会使其他开发者困惑,也使用户困惑。 -如果你建立一个危险的许可,那么会有非常多的复杂情况需要你考虑: +如果你要建立一个危险的许可,则会有多种复杂情况需考虑: -* 许可必须有一个字符串简短的表述给用户他们将要要求做出的安全策略 -* 许可字符必须做很多语言的国际化 -* 用户也许由于对一个许可风险的困惑或者知晓而选择不安装应用 +* 对于用户将要做出的安全决定,许可需要用字符串对其进行简短的表述。 +* 许可字符串必须保证语言的国际化。 +* 用户可能对一个许可感到困惑或者知晓风险而选择不安装应用 +* 当许可的创造未安装的时候,应用可能要求许可。 -上面每一个因素都都应用开发者提出一个重要的非技术的挑战,这也是我们劝阻使用危险许可的原因。 +上面每一个因素都为应用开发者带来了重要的非技术挑战,同时也使用户感到困惑,这也是我们不建议使用危险许可的原因。 ## 使用网络 -网络交易具有很高的安全风险,因为它涉及到传送私人的数据。人们对移动设备的隐私关注日益加深,特别是当设备进行网络交易时,因此app采用最好的方式保护用户诗句是非常重要的。 +网络交易具有很高的安全风险,因为它涉及到传送私人的数据。人们对移动设备的隐私关注日益加深,特别是当设备进行网络交易时,因此应用采取最佳方式保护用户数据安全极为重要。 ### 使用IP网络 -android上面的网络与Linux环境上的差别不是很大。主要考虑的是保证对敏感数据使用适当的协议,比如使用[HTTPS进行网络传输](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)。我们在任何服务器支持HTTPS的地方更愿意使用HTTPS而不是HTTP,因为移动设备频繁连接不安全的网络,比如公共的WiFi热点。 +android下的网络与Linux环境下的差别并不大。主要考虑的是确保对敏感数据采用了适当的协议,比如使用[HTTPS进行网络传输](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)。我们在任何支持HTTPS的服务器上更愿意使用HTTPS而不是HTTP,因为移动设备频繁连接不安全的网络,比如公共WiFi热点。 -认证的、加密的socket级别的通信可以使用[SSLSocket](http://developer.android.com/reference/javax/net/ssl/SSLSocket.html)类轻松的实现。根据Android设备使用WiFi连接不安全网络的频率,对于所有应用来说,使用安全网络是强烈被支持的。 +认证加密socket级别的通信可通过使用[SSLSocket](http://developer.android.com/reference/javax/net/ssl/SSLSocket.html)类轻松实现。由于Android设备使用WiFi连接不安全网络的频率,对于所有应用来说,使用安全网络是极力鼓励支持的。 -我们见过一些应用使用[本地网络](http://en.wikipedia.org/wiki/Localhost)端口处理敏感的IPC。我们不鼓励这种方法因为这些接口是可以被设备上的其他应用访问的。取而代之,在可以认证的地方使用一个android IPC机制,例如[Service](http://developer.android.com/reference/android/app/Service.html)(比使用回环还糟的是绑定INADDR_ANY,因为你的应用也许收到任何地方来的请求。我们也已经见识过了)。 +我们发现部分应用使用[localhost](http://en.wikipedia.org/wiki/Localhost)端口处理敏感的IPC。不鼓励这种方法是因为这些接口可被设备上的其他应用访问。相反,你应该在可认证的地方使用android IPC机制,例如[Service](http://developer.android.com/reference/android/app/Service.html)(比使用回环还糟的是绑定INADDR_ANY,因为你的应用可能收到来自任何地方来的请求,我们也已经见识过了)。 -一个有必要重复的常见的议题是,保证你不信任从HTTP或者其他不安全协议下载的数据。这包括在[WebView](http://developer.android.com/reference/android/webkit/WebView.html)中的输入验证和相对于http的任何响应。 +一个有必要重复的常见议题是,确保不信任从HTTP或者其他不安全协议下载的数据。这包括在[WebView](http://developer.android.com/reference/android/webkit/WebView.html)中的输入验证和对于http的任何响应。 ### 使用电话网络 -SMS是Android开发者使用最频繁的电话协议。开发者应该记住这个协议主要是设计为用户与用户之间的交流,它并不适用一些应用的目的。由于SMS的限制,我们强烈建议使用[Google Cloud Messaging](http://developer.android.com/google/gcm/index.html)(GCM)和IP网络发送数据消息给设备。 +SMS协议是Android开发者使用最频繁的电话协议,主要为用户与用户之间的通信设计,但对于想要传送数据的应用来说并不合适。由于SMS的限制性,我们强烈建议使用[Google Cloud Messaging](http://developer.android.com/google/gcm/index.html)(GCM)和IP网络从web服务器发送数据消息给用户设备应用。 -很多开发者没有意识到SMS在网络上或者设备上不是加密的或者牢固验证的。尤其是,任何SMS接收者应该预料到恶意用户也许已经给你的应用发送了SMS:不要指望未验证的SMS数据执行敏感操作。你也应该注意到SMS在网络上也许会遭到冒名顶替并且/或者拦截,在Android设备本身上面,SMS消息是通过广播intent传递的,所以他们也许会被其他拥有[READ_SMS](http://developer.android.com/reference/android/Manifest.permission.html#READ_SMS)许可的应用截获。 +很多开发者没有意识到SMS在网络上或者设备上是不加密的,也没有牢固验证。特别是任何SMS接收者应该预料到恶意用户也许已经给你的应用发送了SMS:不要指望未验证的SMS数据执行敏感操作。你也应该注意到SMS在网络上也许会遭到冒名顶替并且/或者拦截,对于Android设备本身,SMS消息是通过广播intent传递的,所以他们也许会被其他拥有[READ_SMS](http://developer.android.com/reference/android/Manifest.permission.html#READ_SMS)许可的应用截获。 ## 输入验证 -不管应用运行在什么平台上,功能不完善的输入验证是最常见的影响应用安全问题之一。Android有平台级别的对策,用于减少应用的公开输入验证问题,你应该在可能的地方使用这些功能。同样需要注意的是,安全类型语言的选择倾向去减少输入验证问题的可能。我们强烈建议使用Android SDK建立你的应用。 +无论应用运行在什么平台上,功能不完善的输入验证是最常见的影响应用安全问题之一。Android有平台级别的对策,用于减少应用的公开输入验证问题,你应该在可能的地方使用这些功能。同样需要注意的是,选择类型安全的语言能减少输入验证问题。 -如果你使用native代码,那么任何从文件读取的数据,通过网络接收的,或者通过IPC接收的都有可能引入安全问题。最常见的问题是[缓存溢出](http://en.wikipedia.org/wiki/Buffer_overflow),[释放后使用](http://en.wikipedia.org/wiki/Double_free#Use_after_free),和[off-by-one](http://en.wikipedia.org/wiki/Off-by-one_error)错误。Android提供一些技术比如ASLR和DEP减少这些错误的可利用性,但是他们没有解决基本的问题。小心处理指针和管理缓存可以预防这些问题。 +如果你使用native代码,那么任何从文件读取的,通过网络接收的,或者通过IPC接收的数据都有可能引发安全问题。最常见的问题是[buffer overflows](http://en.wikipedia.org/wiki/Buffer_overflow),[use after free](http://en.wikipedia.org/wiki/Double_free#Use_after_free),和[off-by-one](http://en.wikipedia.org/wiki/Off-by-one_error)。Android提供安全机制比如ASLR和DEP以减少这些漏洞的可利用性,但是没有解决基本的问题。小心处理指针和管理缓存可以预防这些问题。 -动态,基于语言的字符串,比如JavaScript和SQL,都常遭受由转义字符和[脚本注入](http://en.wikipedia.org/wiki/Code_injection)带来的输入验证问题。 +动态、基于字符串的语言,比如JavaScript和SQL,都常受到由转义字符和[脚本注入](http://en.wikipedia.org/wiki/Code_injection)带来的输入验证问题。 -如果你使用提交到SQL Database或者Content Provider查询中数据,SQL也许会是个问题。最好的防御是使用参数化的查询,同ContentProviders中讨论的那样。限制权限为只读或者只写可以减少SQL注入潜在危害。 +如果你使用提交到SQL Database或者Content Provider的数据,SQL注入也许是个问题。最好的防御是使用参数化的查询,就像ContentProviders中讨论的那样。限制权限为只读或者只写可以减少SQL注入的潜在危害。 -如果你不能使用上面提到的安全功能,我们强烈建议使用结构严谨的数据格式并且验证符合期望的格式。黑名单策略与替换危险字符是一种有效的策略,这些技术在实践中是易错并且当错误可能发生的时候应该尽量避免。 +如果你不能使用上面提到的安全功能,我们强烈建议使用结构严谨的数据格式并且验证符合期望的格式。黑名单策略与替换危险字符是有效的,但这些技术在实践中是易错的并且当错误可能发生的时候应该尽量避免。 ## 处理用户数据 -一般来说,最好的处理方法是最小化反问敏感或个人数据的API使用。如果你有对数据的访问并且可以避免存储或者传输信息,那就不要存储或者传输数据。最后,考虑如果有一种你的应用逻辑可能被实现为使用hash或者不可逆形式的数据的方法。例如,你的应用也许使用一个email地址的hash作为主键,避免传输或存储email地址,这减少无意暴露数据的机会,并且它也能减少攻击者尝试利用你的应用的机会。 +通常来说,处理用户数据安全最好的方法是最小化获取敏感数据用户个人数据的API使用。如果你对数据进行访问并且可以避免存储或传输,那就不要存储和传输数据。最后,思考是否有一种应用逻辑可能被实现为使用hash或者不可逆形式的数据。例如,你的应用也许使用一个email地址的hash作为主键,避免传输或存储email地址,这减少无意间泄漏数据的机会,并且也能减少攻击者尝试利用你的应用的机会。 -如果你的应用访问私人数据,比如密码或者用户名,记住司法权也许要求你提供一个你使用和存储这些数据的隐私策略的解释。所以采用最小化访问用户数据的安全最佳实践也许也是单纯的顺从。 +如果你的应用访问私人数据,比如密码或者用户名,记住司法也许要求你提供一个使用和存储这些数据的隐私策略的解释。所以遵守最小化访问用户数据最佳的安全实践也许只是简单的服从。 -你也应该考虑你应用是否会疏忽的暴露个人信息给其他方,比如广告第三方组件或者你应用使用的第三方服务。如果你不知道为什么一个组件或者服务请求个人信息,那么就不要提供给它。一般来说说,通过减少你应用中访问个人信息,将会减少这个区域潜在的问题。 +你也应该考虑你应用是否会疏忽暴露个人信息给其他方,比如广告第三方组件或者你应用使用的第三方服务。如果你不知道为什么一个组件或者服务请求个人信息,那么就不要提供给它。通常来说,通过减少应用访问个人信息,会减少这个区域潜在的问题。 -如果必须访问敏感数据,评估这个信息是否必须要传到服务器,或者是否可以被客户端操作。考虑客户端上使用敏感数据运行的任何代码避免传输用户数据 -保证你不会无意中通过过渡自由的IPC,world writable文件,或者网络socket暴露用户数据给其他设备上的应用。这是一个再次授权的特别的例子,在[请求权限](http://developer.android.com/training/articles/security-tips.html#RequestingPermissions)章节中讨论。 +如果必须访问敏感数据,评估这个信息是否必须要传到服务器,或者是否可以被客户端操作。考虑客户端上使用敏感数据运行的任何代码,避免传输用户数据 +确保不会无意间通过过渡自由的IPC、world writable文件、或网络socket暴露用户数据给其他设备上的应用。这里有一个泄漏权限保护数据的特别例子,在[Requesting Permissions](http://developer.android.com/training/articles/security-tips.html#RequestingPermissions)章节中讨论。 -如果请求全球唯一标识符,建立一个大的,唯一的数字并保存它。不要使用电话标识,比如与个人信息相关的电话号码或者IMEI。这个话题在[Android Developer Blog](http://android-developers.blogspot.com/2011/03/identifying-app-installations.html)中有更详细的讨论,应用开发这应该谨慎的把log写到机器上。 +如果需要GUID,建立一个大的、唯一的数字并保存它。不要使用电话标识,比如与个人信息相关的电话号码或者IMEI。这个话题在[Android Developer Blog](http://android-developers.blogspot.com/2011/03/identifying-app-installations.html)中有更详细的讨论。 -在Android中,log是共享资源,一个带有[READ_LOGS](http://developer.android.com/reference/android/Manifest.permission.html#READ_LOGS)许可的应用可以访问。即使电话log数据是临时的并且在重启之后会擦除,不恰当的记录用户信息也会无意的泄漏用户数据给其他应用。 +应用开发者应谨慎的把log写到机器上。在Android中,log是共享资源,一个带有[READ_LOGS](http://developer.android.com/reference/android/Manifest.permission.html#READ_LOGS)许可的应用可以访问。即使电话log数据是临时的并且在重启之后会擦除,不恰当地记录用户信息会无意间泄漏用户数据给其他应用。 ## 使用WebView -因为[WebView](http://developer.android.com/reference/android/webkit/WebView.html)能包含HTML和JavaScript浏览网络内容,不恰当的使用会引入常见的web安全问题,比如[跨站脚本攻击](http://en.wikipedia.org/wiki/Cross_site_scripting)(JavaScript注入)。Android包含一些机制通过限制WebView的能力到你应用请求的功能最小化来减少这些潜在问题的范围。 +因为[WebView](http://developer.android.com/reference/android/webkit/WebView.html)能包含HTML和JavaScript浏览网络内容,不恰当的使用会引入常见的web安全问题,比如[跨站脚本攻击](http://en.wikipedia.org/wiki/Cross_site_scripting)(JavaScript注入)。Android采取一些机制通过限制WebView的能力到应用请求功能最小化来减少这些潜在的问题。 -如果你的应用没有在WebView内直接使用JavaScript,不要调用[setJavaScriptEnabled()](http://developer.android.com/reference/android/webkit/WebSettings.html#setJavaScriptEnabled(boolean))。我们见过这个方法在简单的代码中执行,也许会导致在产品应用中改变用途:所以如果必要的化移除它。默认的,WebView不执行JavaScript,所以跨站脚本攻击不可能产生。 +如果你的应用没有在WebView内直接使用JavaScript,不要调用[setJavaScriptEnabled()](http://developer.android.com/reference/android/webkit/WebSettings.html#setJavaScriptEnabled(boolean)。某些样本代码使用这种方法,可能会导致在产品应用中改变用途:所以如果不需要的话移除它。默认情况下WebView不执行JavaScript,所以跨站脚本攻击不会产生。 -使用[addJavaScriptInterface()](http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String))要特别的小心,因为它允许JavaScript执行通常保留给Android应用的操作。只把addJavaScriptInterface()暴露给可靠的输入源。如果不受信任的输入是被允许的,不受信任的JavaScript也许会执行Android方法。总的来说,我们建议只把addJavaScriptInterface()暴露给你应用apk内包含的JavaScript。 +使用[addJavaScriptInterface()](http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)要特别的小心,因为它允许JavaScript执行通常保留给Android应用的操作。只把[addJavaScriptInterface()](http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)暴露给可靠的输入源。如果不受信任的输入是被允许的,不受信任的JavaScript也许会执行Android方法。总得来说,我们建议只把[addJavaScriptInterface()](http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)暴露给你应用内包含的JavaScript。 -如果你的应用通过WebView访问敏感数据,你也许想要使用[clearCache()](http://developer.android.com/reference/android/webkit/WebView.html#clearCache(boolean))方法来删除任何存储到本地的文件。服务端的header,比如no-cache,能用于指示应用不应该缓存特定的内容。 +如果你的应用通过WebView访问敏感数据,你也许想要使用[clearCache()](http://developer.android.com/reference/android/webkit/WebView.html#clearCache(boolean)方法来删除任何存储到本地的文件。服务端的header,比如no-cache,能用于指示应用不应该缓存特定的内容。 ### 处理证书 -一般来说,我们建议请求用户证书频率最小化:使得钓鱼攻击更明显,并且降低其成功的可能。取而代之使用授权令牌然后刷新它。 +通常来说,我们建议请求用户证书频率最小化--使得钓鱼攻击更明显,并且降低其成功的可能。取而代之使用授权令牌然后刷新它。 -可能的情况下,用户名和密码不应该存储到设备上,取而代之,使用用户提供的用户名和密码执行初始认证,然后使用一个短暂的,特定服务的授权令牌。可以被多个应用访问的service应该使用[AccountManager](http://developer.android.com/reference/android/accounts/AccountManager.htmls)访问。 +可能的情况下,用户名和密码不应该存储到设备上,而使用用户提供的用户名和密码执行初始认证,然后使用一个短暂的、特定服务的授权令牌。可以被多个应用访问的service应该使用[AccountManager](http://developer.android.com/reference/android/accounts/AccountManager.htmls)访问。 如果可能的话,使用AccountManager类来执行基于云的服务并且不把密码存储到设备上。 使用AccountManager获取[Account](http://developer.android.com/reference/android/accounts/Account.html)之后,进入任何证书前检查[CREATOR](http://developer.android.com/reference/android/accounts/Account.html#CREATOR),这样你就不会因为疏忽而把证书传递给错误的应用。 -如果证书只是用于你建立的应用,那么你能使用[checkSignature()](http://developer.android.com/reference/android/content/pm/PackageManager.html#checkSignatures(int, int))验证访问AccountManager的应用。另一种选择,如果一个应用要使用证书,你可以使用一个[KeyStore](http://developer.android.com/reference/java/security/KeyStore.html)来储存。 +如果证书只是用于你创建的应用,那么你能使用[checkSignature()](http://developer.android.com/reference/android/content/pm/PackageManager.html#checkSignatures(int, int)验证访问AccountManager的应用。或者,如果一个应用要使用证书,你可以使用[KeyStore](http://developer.android.com/reference/java/security/KeyStore.html)来储存。 ## 使用密码学 -除了提供数据隔离之外,支持完整的文件系统加密,并且提供安全交流通道。Android提供大量加密算法来保护数据。 +除了采取数据隔离之外,支持完整的文件系统加密,并且提供安全交流通道。Android提供大量加密算法来保护数据。 -一般来说,尝试使用最高级别的以存在framework的实现能支持你的用例,如果你需要安全的从一个已知的位置取回一个文件,一个简单的HTTPS URI也许就足够了,并且这部分不要求任何加密知识。如果你需要一个安全隧道,考虑使用[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)或者[SSLSocket](http://developer.android.com/reference/javax/net/ssl/SSLSocket.html),要比使用你自己的协议好。 +通常来说,尝试使用最高级别的已存在framework的实现来支持,如果你需要安全的从一个已知的位置取回一个文件,一个简单的HTTPS URI也许就足够了,并且这部分不要求任何加密知识。如果你需要一个安全隧道,考虑使用[HttpsURLConnection](http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html)或者[SSLSocket](http://developer.android.com/reference/javax/net/ssl/SSLSocket.html)要比使用你自己的协议好。 -如果你发现你的确需要实现你自己的协议,我们强烈建议你不要实现你自己的加密算法。使用已经存在的加密算法,比如[Cipher](http://developer.android.com/reference/javax/crypto/Cipher.html)类中提供的AES或者RSA的实现。 +如果你发现你的确需要实现你自己的协议,我们强烈建议你不要自己实现加密算法。使用已经存在的加密算法,比如[Cipher](http://developer.android.com/reference/javax/crypto/Cipher.html)类中提供的AES或者RSA。 -使用一个安全的随机数生成器([SecureRandom](http://developer.android.com/reference/java/security/SecureRandom.html))来初始化加密的key([KeyGenerator](http://developer.android.com/reference/javax/crypto/KeyGenerator.html))。使用一个不受由安全随机数生成器生成的key严重削弱算法的优点,而且有能允许离线攻击。 +使用一个安全的随机数生成器([SecureRandom](http://developer.android.com/reference/java/security/SecureRandom.html))来初始化加密的key([KeyGenerator](http://developer.android.com/reference/javax/crypto/KeyGenerator.html))。使用一个不安全随机数生成器生成的key严重削弱算法的优点,而且可能遭到离线攻击。 -如果你需要存储一个key来重复使用,使用类似于[KeyStore](http://developer.android.com/reference/java/security/KeyStore.html)的机制,提供一种机制长期储存和检索加密的key。 +如果你需要存储一个key来重复使用,使用类似于[KeyStore](http://developer.android.com/reference/java/security/KeyStore.html)的机制,提供长期储存和检索加密key的功能。 ## 使用进程间通信 一些Android应用试图使用传统的Linux技术实现IPC,比如网络socket和共享文件。我们强烈鼓励使用Android系统IPC功能,比如[Intent](http://developer.android.com/reference/android/content/Intent.html),[Binder](http://developer.android.com/reference/android/os/Binder.html),[Messenger](http://developer.android.com/reference/android/os/Messenger.html)和[BroadcastReceiver](http://developer.android.com/reference/android/content/BroadcastReceiver.html)。Android IPC机制允许你为每一个IPC机制验证连接到你的IPC和设置安全策略的应用的身份。 -很多安全元素通过IPC机制共享。Broadcast Receiver, Activitie,和Service都在应用的manifest中声明。如果你的IPC机制不打算给其他应用使用,设置`android:exported`属性为false。这对由同一个UID内多个进程应用,或者你在开发后期决定不想通过IPC暴露功能但是你又不想重写代码是很有用的。 +很多安全元素通过IPC机制共享。Broadcast Receiver, Activitie,和Service都在应用的manifest中声明。如果你的IPC机制不打算给其他应用使用,设置`android:exported`属性为false。这对于同一个UID内包含多个进程的应用,或者在开发后期决定不想通过IPC暴露功能并且不想重写代码的时候非常有用。 -如果你的IPC打算让别的应用访问,你可以通过使用Permission标记设置一个安全策略。如果IPC是相同开发者应用间的,使用[签名级的许可](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel)更好一些。 +如果你的IPC打算让别的应用访问,你可以通过使用Permission标记设置一个安全策略。如果IPC是使用同一个密钥签名的独立的应用间的,使用[signature](http://developer.android.com/guide/topics/manifest/permission-element.html#plevel)更好一些。 ### 使用意图 -Intent是Android中异步IPC机制的首选。根据你应用的需求,你也许使用[sendBroadcast()](http://developer.android.com/reference/android/content/Context.html#sendBroadcast(android.content.Intent)),[sendOrderedBroadcast()](http://developer.android.com/reference/android/content/Context.html#sendOrderedBroadcast(android.content.Intent, java.lang.String))或者直接的intent来指定一个应用组件。 +Intent是Android中异步IPC机制的首选。根据你应用的需求,你也许使用[sendBroadcast()](http://developer.android.com/reference/android/content/Context.html#sendBroadcast(android.content.Intent),[sendOrderedBroadcast()](http://developer.android.com/reference/android/content/Context.html#sendOrderedBroadcast(android.content.Intent, java.lang.String)或者直接的intent来指定一个应用组件。 注意,有序广播可以被接收者“消费”,所以他们也许不会被发送到所有的应用中。 -如果你打算在必须发送给一个指定的receiver的地方发送一个intent,这个intent必须被直接的发送给这个receiver。 +如果你要发送一个intent给指定的receiver,这个intent必须被直接的发送给这个receiver。 + +intent的发送者能在发送的时候验证接受者是否有一个许可指定了一个non-Null Permission。只有有那个许可的应用才会收到这个intent。如果广播intent内的数据是敏感的,你应该考虑使用许可来保证恶意应用没有恰当的许可无法注册接收那些消息。这种情况下,可以考虑直接执行这个receiver而不是发起一个广播。 -intent的发送者能在发送的时候验证接受者是否有一个许可指定了一个non-Null Permission。只有有那个许可的应用才会收到这个intent。如果在广播intent内的数据是敏感的,你应该考虑使用一个许可来保证恶意应用没有恰当的许可无法注册接收那些消息。那种环境下,你也许也考虑直接执行这个receiver而不是发起一个广播。 +注意:intent过滤器不能作为安全特性--组件可被intent显式调用,可能会没有符合intent过滤器的数据。你应该在intent接收器内执行输入验证,确认对于调用接收者,服务、或活动来说格式正确合理。 ### 使用服务 -[Service](http://developer.android.com/reference/android/app/Service.html)经常被用于为其他应用提供功能供其使用。每一个service类必须在它的包的AndroidManifest.xml中有相应的声明。 +[Service](http://developer.android.com/reference/android/app/Service.html)经常被用于为其他应用提供服务。每个service类必须在它的manifest文件进行相应的声明。 + +默认情况下,Service不能被导出和被其他应用执行。如果你加入了任何intent过滤器到服务蛇呢光明中,那么它默认为可以被导出。最好明确声明[android:exported](http://developer.android.com/guide/topics/manifest/service-element.html#exported)元素来确定它按照你设想的运行。可以使用[android:permission](http://developer.android.com/guide/topics/manifest/service-element.html#prmsn)保护Service。这样做,其他应用在他们自己的manifest文件中将需要声明相应的[](http://developer.android.com/guide/topics/manifest/uses-permission-element.html)元素来启动、停止或者绑定到这个service上。 + +一个Service可以使用许可保护单独的IPC调用,在执行调用前通过调用[checkCallingPermission()](http://developer.android.com/reference/android/content/Context.html#checkCallingPermission(java.lang.String)来实现。我们建议使用manifest中声明的许可,因为那些是不容易监管的。 -默认的,Service被导出并且可以被其他应用执行。可以在manifest文件中的标记使用[android:permission](http://developer.android.com/guide/topics/manifest/service-element.html#prmsn)保护Service。这样做,其他应用在他们自己的manifest文件中将需要声明一个相应的元素来启动,停止或者绑定到这个service上。 +### 使用binder和messenger接口 -一个Service可以保护单独的具有许可的IPC调用它,在执行那个调用的实现之前,通过调用[checkCallingPermission()](http://developer.android.com/reference/android/content/Context.html#checkCallingPermission(java.lang.String))实现保护。我们一般建议使用manifest中声明的许可,因为那些是不容易监管的 +在Android中,[Binders](http://developer.android.com/reference/android/os/Binder.html)和[Messenger](http://developer.android.com/reference/android/os/Messenger.html)是RPC-style IPC的首选机制。必要的话,他们提供一个定义明确的接口,促进彼此的端点认证。 -### 使用binder和AIDL接口 +我们强烈鼓励在一定程度上设计不要求指定许可检查的接口。Binder和[Messenger](http://developer.android.com/reference/android/os/Messenger.html)不在应用的manifest中声明,因此你不能直接在Binder上应用声明的许可。它们在应用的manifest中继承许可声明,[Service](http://developer.android.com/reference/android/app/Service.html)或者[Activity](http://developer.android.com/reference/android/app/Activity.html)内实现了许可。如果你打算创建一个接口,在一个指定binder接口上要求认证和/或者访问控制,这些控制必须在Binder和[Messenger](http://developer.android.com/reference/android/os/Messenger.html)的接口中明确添加代码。 -在Android中,[Binders](http://developer.android.com/reference/android/os/Binder.html)是RPC-style IPC的首选机制。必要的话,他们提供一个定义明确的接口,促进彼此的端点认证。 -我们强烈鼓励在一定程度上设计不要求接口指定许可检查的接口。Binder不在应用的manifest中声明,并且因此你不能直接在Binder上应用声明的许可。Binder继承在应用在manifest中[Service](http://developer.android.com/reference/android/app/Service.html)或者[Activity](http://developer.android.com/reference/android/app/Activity.html)声明的,Service或者Activity内实现了的许可。如果你打算建立一个接口,在一个指定binder接口上要求验证并且(或者)要求访问控制,这些控制必须在接口中清楚的在代码中添加。 +如果提供一个需要访问控制的接口,使用[checkCallingPermission()](http://developer.android.com/reference/android/content/Context.html#checkCallingPermission(java.lang.String)来验证调用者是否拥有必要的许可。由于你的应用的id已经被传递到别的接口,因此代表调用者访问一个Service之前这尤其重要。如果调用一个Service提供的接口,如果你没有对给定的Service访问许可,[bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int)请求也许会失败。如果调用你自己的应用提供的本地接口,使用[clearCallingIdentity()](http://developer.android.com/reference/android/os/Binder.html#clearCallingIdentity()来进行内部安全检查是有用的。 -如果提供一个需要访问控制的接口,使用[checkCallingPermission()](http://developer.android.com/reference/android/content/Context.html#checkCallingPermission(java.lang.String))来验证Binder的主叫者是否拥有必要的许可。因为你的应用的id已经被传递到别的interface,所以代表访问一个Service之前这尤其重要。如果执行一个service提供的接口,如果你没有对给定的service的访问许可,[bindService()](http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int))请求也许会失败。如果调用一个你自己应用提供的本地的接口,使用[clearCallingIdentity()](http://developer.android.com/reference/android/os/Binder.html#clearCallingIdentity())来消除内部的安全检查也行是有用的。 +更多关于用服务运行IPC的信息,参见[Bound Services](http://developer.android.com/guide/components/bound-services.html) ### 利用广播接收机 [Broadcast receivers](http://developer.android.com/reference/android/content/BroadcastReceiver.html)是用来处理通过[intent](http://developer.android.com/reference/android/content/Intent.html)发起的异步请求。 -默认的,receiver是导出的并且可以被其他任何应用执行。如果你的BroadcastReceiver打算让其他应用使用,你也许想要在应用的manifest文件中使用元素对receiver应用安全许可。这将阻止没有恰当许可的应用发送intent给这个BroadcastReceiver。 +默认情况下,receiver是导出的,并且可以被任何其他应用执行。如果你的[BroadcastReceiver](http://developer.android.com/reference/android/content/BroadcastReceiver.html)打算让其他应用使用,你也许想在应用的manifest文件中使用[](http://developer.android.com/guide/topics/manifest/receiver-element.html)元素对receiver使用安全许可。这将阻止没有恰当许可的应用发送intent给这个[BroadcastReceiver](http://developer.android.com/reference/android/content/BroadcastReceiver.html)。 ## 动态加载代码 -我们强烈不鼓励从应用apk文件外加载代码。这样做显著增加了应用泄漏的可能,取决于代码注入或者代码篡改,也增加了版本管理和应用测试的复杂性。最终,它会使得不可验证一个应用的行为,所以它也许在一些环境下被进制。 +我们极为不鼓励从应用文件外加载代码。由于代码注入或者代码篡改这样做显著增加了应用暴露的可能,同事也增加了版本管理和应用测试的复杂性。最终可能造成无法验证应用的行为,因此在某些环境下应该被限制。 -如果你的应用确实动态加载了代码,最重要的事情是记住运行动态加载的代码与应用apk具有相同的安全许可。用户决定安装你的应用是基于你的id,他们期望你提供任何在应用内运行的代码,包括动态加载的代码与动态加载代码结合的重要的安全风险是代码需要代码需要来自可信资源。 +如果你的应用确实动态加载了代码,最重要的事情是记住运行动态加载的代码与应用具有相同的安全许可。用户决定安装你的应用是基于你的id,他们期望你提供任何运行在应用内部的代码,包括动态加载的代码。 -如果这个模块是之间在你的apk中包含,那么他们不能被其他应用修改,不论代码是本地库或者是使用DexClassLoader加载的类这都是事实。我们见过很多应用实例尝试从不安全的位置加载代码,比如从网络上通过非加密的协议下载或者从world writable位置(比如外部存储)。这些位置会允许网络上的人在传输过程中修改其内容,或者允许用户设备上的另一个应用修改其内容。 +动态加载代码主要的风险在于代码来源于可确认的源头。 +如果这个模块是之间直接包含在你的应用中,那么它们不能被其他应用修改,不论代码是本地库或者是使用[DexClassLoader](http://developer.android.com/reference/dalvik/system/DexClassLoader.html)加载的类这都是事实。我们见过很多应用实例尝试从不安全的地方加载代码,比如从网络上通过非加密的协议或者从world writable位置(比如外部存储)下载数据。这些地方会允许网络上其他人在传输过程中修改其内容,或者允许用户设备上的其他应用修改其内容。 ## 在虚拟机器安全性 -编写运行在虚拟机的安全代码是一个精心研究的话题,很多问题并不特指在Android上。 -相比尝试重新讲解这些话题,我们推荐你熟悉已有的文献。 +Dalvik是安卓的运行时虚拟机(VM).Dalvik是特别为安卓建立的,但许多其他虚拟机相关的安全代码的也适用于安卓。一般来说,你不应该关心与自己有关的虚拟机的安全问题。你的应用程序在一个安全的沙盒环境下运行,所以系统上的其他进程无法访问你的代码或私人数据。 + +如果你想更深入了解虚拟机的安全问题,我们建议您熟悉一些现有文献的主题。推荐两个比较流行的资源: + * [http://www.securingjava.com/toc.html](http://www.securingjava.com/toc.html) * [https://www.owasp.org/index.php/Java_Security_Resources](https://www.owasp.org/index.php/Java_Security_Resources) -这个文档集中于Android专有的并/或者与其他环境不同地方。对于有在其他环境上的VM编程经验开发者,这有这有两个普遍的问题也许对于编写Android应用来说有些不同: +这个文档集中于安卓与其他VM环境不同地方。对于有在其他环境下有VM编程经验开发者来说,这里有两个普遍的问题可能对于编写Android应用来说有些不同: -* 一些虚拟机,比如JVM或者.net,担任一个安全的边界作用,代码与底层操作系统能力相隔离。在Android上,Dalvik VM不是一个安全边界:应用沙箱是在系统级别实现的,所以Dalvik可以在同一个应用与native代码相互操作没有任何约束。 +* 一些虚拟机,比如JVM或者.net,担任一个安全的边界作用,代码与底层操作系统隔离。在Android上,Dalvik VM不是一个安全边界:应用沙箱是在系统级别实现的,所以Dalvik可以在同一个应用与native代码相互操作,没有任何安全约束。 -* 已知的手机上的存储限制,对来发者来说,想要建立模块化应用和使用动态类加载是很常见的。当这么做的时候,要考虑两个资源:一个是你在哪里恢复你的应用逻辑,另一个是你在哪里存储它们。不要从未验证的资源使用动态类加载器,比如不安全的网络资源或者外部存储,因为那些代码可能被修改为包含恶意的行为。 +* 已知的手机上的存储限制,对来发者来说,想要建立模块化应用和使用动态类加载是很常见的。要这么做的时候需要考虑两个资源:一是在哪里恢复你的应用逻辑,二是在哪里存储它们。不要从未验证的资源使用动态类加载器,比如不安全的网络资源或者外部存储,因为那些代码可能被修改为包含恶意行为。 -## 在本地代码的安全 +## 本地代码的安全 一般来说,对于大多数应用开发,我们鼓励开发者使用Android SDK而不是使用[Android NDK](http://developer.android.com/tools/sdk/ndk/index.html) 的native代码。编译native代码的应用更为复杂,移植性差,更容易包含常见的内存崩溃错误,比如缓冲区溢出。 -Android使用Linux内核编译并且与Linux开发相似,如果你打算使用native代码,安全最佳实践尤其有用。这篇文档讨论这些所有的最佳实践实在太短了,但是最受欢迎的资源之一是“Secure Programming for Linux and Unix HOWTO”,在这里可以找到[http://www.dwheeler.com/secure-programs +Android使用Linux内核编译并且与Linux开发相似,如果你打算使用native代码,安全策略尤其有用。这篇文档讨论的最佳策略实在太少了,但最受欢迎的资源之一“Secure Programming for Linux and Unix HOWTO”,在这里可以找到[http://www.dwheeler.com/secure-programs Android](http://www.dwheeler.com/secure-programs)。 -和大多数Linux环境之前的一个重要区别是应用沙箱。在Android中,所有的应用运行在应用沙箱中,包括那些用native代码编写的应用。在最基本的级别中,对于开发者来说,一种考虑它的好的办法与Linux相似,知道每一个应用被分配一个具有非常有限权限的唯一UID。这里讨论的比[Android Security Overview](http://source.android.com/tech/security/index.html)中更细节化,你应该熟悉应用许可,即使你使用的是native代码。 - -下一篇:[使用HTTPS与SSL](security-ssl.html) +与大多数Linux环境的一个重要区别是应用沙箱。在Android中,所有的应用运行在应用沙箱中,包括用native代码编写的应用。在最基本的级别中,与Linux相似,对于开发者来说最好的方式是知道每个应用被分配一个权限非常有限的唯一UID。这里讨论的比[Android Security Overview](http://source.android.com/tech/security/index.html)中更细节化,你应该熟悉应用许可,即使你使用的是native代码。