在本章中,我们将介绍以下配方:
- 安全地存储数据
- 实施身份验证控制
- 保护传输中的数据
- 安全地使用 Android 和 iOS 平台组件
- 保护第三方代码和组件
- 采用逆向工程保护
移动应用通常是控制消费者物联网的关键。无论是智能家居设备还是互联车辆,移动应用都是一个理想的攻击目标,并保持安全。在第 5 章利用物联网移动应用中,从攻击性的角度涵盖了移动应用的利用。本章将提供移动应用安全防御控制,用于防范常见攻击向量。值得注意的是,本章在谈到移动安全最佳实践时并非详尽无遗,因为本章仅就这一主题编写了完整的书籍。鼓励参考补充阅读,以便更深入地理解本章所述的某些控制措施和最佳实践。在适当的情况下,整个食谱中都会给出 Android 和 iOS 的示例。根据 OWASP 的移动安全项目(https://www.owasp.org/index.php/Projects/OWASP_Mobile_Security_Project_-_Top_Ten_Mobile_Controls ),前十大移动控件包括:
-
识别和保护敏感数据。
-
保护身份验证凭据。
-
保护传输中的数据。
-
正确实现用户身份验证、授权和会话管理。
-
确保后端 API(服务)和平台(服务器)的安全。
-
确保与第三方服务和应用的数据集成。
-
特别注意收集和储存用户数据的收集和使用许可。
-
实施控制以防止未经授权访问付费资源。
-
确保移动应用的安全分发/供应。
-
仔细检查代码的任何运行时解释是否存在错误。
本章将讨论与常见物联网应用用例相关的几个前面提到的移动安全控制。
移动应用中的敏感数据因物联网设备的性质而异。许多设备可以在移动设备上存储个人数据、收集个人数据、患者健康信息(PHI)、信用卡信息,并存储帐户凭据,以验证到物联网设备。泄露的凭据或长期会话令牌可能会对智能门锁和连接的车辆产生严重影响。这些敏感数据必须通过适当的控制和验证加以保护。很多时候,敏感数据会无意中暴露给在移动设备上运行的用于操作系统进程间通信(IPC的第三方应用。此外,移动设备在旅行中丢失、被盗或被扣押的情况并不少见。在这些情况下,应用必须采用适当的安全控制来保护敏感数据,并使获取数据变得更加困难。在此配方中,我们将讨论安全存储敏感数据的方法。
在此配方中,SQLCipher 将用于演示一种安全数据库存储方法。
可以从以下网页下载 SQLCipher:
https://www.zetetic.net/sqlcipher/
Android 和 iOS 平台都有本地方法来安全存储敏感数据。对于 Android,敏感数据可以存储在密钥库中。对于 iOS,敏感数据可以存储在钥匙链中。需要注意的是,如果一台设备是根设备或越狱设备,Android 的密钥库和 iOS 的密钥链内容可能会被转储。虽然,如果安卓设备具有可信执行环境(TEE)或安全元素(SE),操作系统无法直接访问密钥库,保存的数据也无法访问。除了可用于安全存储数据的本机平台 API 之外,第三方库还可用于加密磁盘或整个 SQLite 数据库(如 SQLCipher)上的数据。SQLCipher 可用于 Android 和 iOS,如果将 SQLite 数据库用于 IoT 设备,则应使用 SQLCipher 安全地存储数据。
- 要在 Android 应用中使用 SQLCipher,我们需要创建一个活动,初始化 SQLCipher 数据库,并将数据保存在相应的数据库表和列中,如以下示例所示:
public class SQLCipherExampleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
InitSQLCipher();
}
private void InitSQLCipher() {
SQLiteDatabase.loadLibs(this);
File databaseFile = getDatabasePath("EncStorage.db");
databaseFile.mkdirs();
databaseFile.delete();
SQLiteDatabase secureDatabase = SQLiteDatabase.openOrCreateDatabase(databaseFile, "PacktDB", null);
secureDatabase.execSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR,Password VARCHAR);");
secureDatabase.execSQL("INSERT INTO Accounts VALUES('PacktUser','EncPassword');");
secureDatabase.close();
}
}
- 前面的示例中未包括的一个重要步骤是建立 PRAGMA 键。此 PRAGMA 密钥是 SQLCipher 数据库的加密密钥,应在运行时为每个用户和设备在应用初始化期间生成。PRAGMA 密钥应具有足够的熵,并且不应硬编码到应用中或存储在非硬件支持的存储位置(例如,安全元素)。
利用人员经常使用的设置和配置的一个常见安卓不安全存储位置是SharedPreferences.xml
。除非使用第三方包装器加密首选项的值,否则保存在SharedPreferences.xml
中的数据是明文可读的。
对于 iOS,数据不应存储在应用容器中的文件或明文 plist 文件中。根据应用运行的上下文,密钥链应用于具有正确密钥链 API 属性的所有凭证和令牌数据。例如,如果应用不在后台运行,请使用限制性最强的属性,如kSecAttrAccessibleWhenUnlockedThisDeviceOnly
,以防止 iTunes 或kSecAttrAccessibleWhenUnlocked
备份钥匙链项目。如果应用需要在前台运行,请使用kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
属性。
- 在存储数据时,Android 和 iOS 都可以遵循一些最佳实践。常见的最佳做法包括:
1.尽可能不要存储敏感数据。
2.仅存储应用功能所需的数据。
3.避免将敏感数据存储在缓存、外部存储器(SD 卡)或临时文件中。
4.不要将敏感数据记录到磁盘或控制台。
5.禁用敏感输入字段的键盘缓存。
6.限制备份应用数据。
7.如果敏感数据存储在磁盘上,请加密其内容并将数据存储在防篡改位置,如安全元件。
8.确保应用在使用后以及不再需要时清除内存中的敏感数据。
9 确保禁用敏感文本字段的剪贴板。
有时,平台安全 API(如密钥库和密钥链)可能不足以确保敏感数据的机密性和完整性。在这些情况下,建议使用应用级加密增强保护,然后将加密数据存储在平台的安全存储位置。
- 有关钥匙链的更多信息,请参阅苹果钥匙链服务编程指南(https://developer.apple.com/library/content/documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html#/ )。
- 有关钥匙链的更多信息,请参阅Android 利用者文档(https://developer.android.com/training/articles/keystore.html )。
- 有关使用 SQLCipher 的更多信息,请参阅SQLCipher 的 API 利用者文档(https://www.zetetic.net/sqlcipher/sqlcipher-api/ )。
移动应用的身份验证可以从服务器端和客户端进行。物联网移动应用可以使用这两种设计模式,尽管在生产中实施时,每种模式都有自己的风险考虑。本节将讨论其中一些风险以及服务器端和客户端身份验证的最佳实践设计实现。
安全认证用户的一般应用原则也适用于移动应用。一个很好的参考是 OWASP 的认证备忘表(https://www.owasp.org/index.php/Authentication_Cheat_Sheet )。常见的身份验证控制和最佳做法包括:
- 正确的密码强度控制
- 密码长度
- 10 个字符或更多
- 密码复杂性策略
- 1 个大写、1 个小写、1 个数字、1 个特殊字符,不允许使用 2 个连续字符,例如 222
- 强制密码历史
- 不允许使用最后三个密码(密码重复使用)
- 密码长度
- 仅通过加密通信(TLS)传输凭据
- 通过
HTTP POST
主体发送凭证
- 通过
- 重新验证用户的敏感功能
- 更改密码
- 更改帐户密码
- 不断变化的安全问题
- 共享相机源
- 解锁车辆
- 确保身份验证错误消息不会泄露潜在的敏感信息
- 正确的错误响应如下:用户名和/或密码无效
- 确保记录身份验证功能以检测登录失败
- 防止自动暴力攻击
- 使用验证码或类似工具
- 速率限制可疑登录尝试
- 在给定阈值后临时锁定帐户,并通过电子邮件发送帐户地址
- 确保存在多因素身份验证,并在登录时以及使用逐步身份验证访问资源时强制执行。两因素法包括:
- 除密码外的用户已知值
- 通过电子邮件或短信发送的一次性密码(OTP或代码
- 除了用户密码外,还带有 OTP 的物理令牌
上述各项适用于 web 应用、混合移动应用,甚至本机移动应用。以下各项是要在应用中实施的特定于移动设备的身份验证最佳做法:
- 如果使用生物识别技术,请确保使用密钥库和密钥链,而不是基于事件的
- 会话在服务器端无效
- 应用列出上次登录活动,并允许用户阻止设备
- 避免使用设备 UUID、IP 地址、MAC 地址和 IMEI 进行身份验证或授权
- 在移动应用之外使用第三方 OTP 应用(例如,Google 或 Salesforce authenticator)
Android 特定的身份验证实践如下所示:
- 使用 Android 的指纹管理器类(时 https://developer.android.com/reference/android/hardware/fingerprint/FingerprintManager.html ),使用具有非对称密钥对的
KeyGenerator
类。使用非对称密钥对的示例见https://github.com/googlesamples/android-AsymmetricFingerprintDialog 。 - 在 Android Nougat API 24 中引入,使用
setInvalidatedByBiometricEnrollment
(boolean invalidateKey
方法使在移动设备上检索钥匙时产生的新指纹无效。 - 应用应利用 SafetyNet reCAPTCHA API 保护身份验证免受基于机器人的暴力尝试。
要使用 SafteyNet reCAPTCHA API,必须采取以下步骤:
- 如果尚未配置,请添加 SafetyNet API 依赖项和 Google Play 服务。例如,在项目构建梯度文件中包含 compile
com.google.android.gms:play-services-safetynet:11.4.2
,如下所示:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion '25.0.0'
defaultConfig {
applicationId "jakhar.aseem.diva"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled enabled
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jni.srcDirs = []
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:design:23.1.0'
compile 'com.google.android.gms:play-services-safetynet:11.4.2'
}
- 必须通过
verifyWithRecaptcha()
(发出调用验证请求的请求 https://developers.google.com/android/reference/com/google/android/gms/safetynet/SafetyNetClient#verifyWithRecaptcha(java.lang.String)。此请求必须包含 API 站点密钥作为参数,并且必须覆盖onSuccess()
和onFailure()
方法。下面的代码片段显示了如何调用 Android 利用者指南(提供的此方法 https://developer.android.com/training/safetynet/recaptcha.html#send-请求:
public void onClick(View v) {
SafetyNet.getClient(this).verifyWithRecaptcha(YOUR_API_SITE_KEY)
.addOnSuccessListener((Executor) this,
new OnSuccessListener<SafetyNetApi.RecaptchaTokenResponse>() {
@Override
public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response) {
// Indicates communication with reCAPTCHA service was
// successful.
String userResponseToken = response.getTokenResult();
if (!userResponseToken.isEmpty()) {
// Validate the user response token using the
// reCAPTCHA siteverify API.
}
}
})
.addOnFailureListener((Executor) this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ApiException) {
// An error occurred when communicating with the
// reCAPTCHA service. Refer to the status code to
// handle the error appropriately.
ApiException apiException = (ApiException) e;
int statusCode = apiException.getStatusCode();
Log.d(TAG, "Error: " + CommonStatusCodes
.getStatusCodeString(statusCode));
} else {
// A different, unknown type of error occurred.
Log.d(TAG, "Error: " + e.getMessage());
}
}
});
}
- 通过
SafetyNetApi.RecaptchaTokenResult.getTokenResult()
验证响应令牌。一个JSON HTTP
响应示例如下所示:
{
"success": true|false,
"challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
"apk_package_name": string, // the package name of the app where the reCAPTCHA was solved
"error-codes": [...] // optional
}
- 接下来,必须添加逻辑来处理故障和错误。SafetyNet reCAPTCHA API 使用七种状态代码:
1.RECAPTCHA_INVALID_SITEKEY
2.RECAPTCHA_INVALID_KEYTYPE
3.RECAPTCHA_INVALID_PACKAGE_NAME
4.UNSUPPORTED_SDK_VERSION
5.TIMEOUT
6.NETWORK_ERROR
7.ERROR
有关每个状态代码的详细信息,请参见以下参考页:
下面列出了特定于 iOS 的身份验证实践:
- 将应用的秘密存储在特定应用的访问控制密钥链列表中。使用钥匙链和触摸 ID 的示例代码片段可以在的苹果利用者文档中找到 https://developer.apple.com/library/content/samplecode/KeychainTouchID/Listings/KeychainTouchID_AAPLKeychainTestsViewController_m.html 。
- 确保从
LAContext.evaluatedPolicyDomainState
读取应用,以检查evaluatedPolicyDomainState
值是否已更改,表明登记的触摸 ID 指纹已更改。 - 不允许通过
kSecAttrSynchronizable
将钥匙链同步到 iCloud,除非应用功能需要。
Touch-ID 是验证用户身份的常用方法;但是,有几种方法和工具可以绕过仅使用本地身份验证框架的应用。如前所述,使用密钥链 ACL 可防止攻击者在运行时重写LAContextevaluatePolicy:localizedReason:reply
方法或修补应用本身。
- 有关 iOS 钥匙链服务的更多信息,请参阅钥匙链服务编程指南(https://developer.apple.com/library/content/documentation/Security/Conceptual/keychainServConcepts/02concepts/concepts.html
- 有关使用指纹 API 对远程服务器进行身份验证的更多信息,请访问 Android 的利用者博客(https://android-developers.googleblog.com/2015/10/new-in-android-samples-authenticating.html )
- 请参阅 SafetyNet reCAPTCHA API 上的以下 Android 利用者页面(https://developer.android.com/training/safetynet/recaptcha.html )
确保物联网移动应用的端到端通信是一个众所周知的难题。通常,数据通过明文协议(如 HTTP 或 UDP(SIP))泄漏,以便将音频传输到移动应用。有时,物联网制造商被发现向仅通过 HTTP 进行通信的第三方泄漏数据,或对分析服务(如内容识别或崩溃报告分析服务)使用不太安全的加密配置。保护传输中的数据的目标是确保移动应用、物联网设备和 API 端点之间交换的数据的机密性和完整性。移动应用必须使用 TLS 为网络通信设置安全加密通道,并配置适当的密码套件。对于智能锁或联网车辆等设备,这是必须的。此配方将涵盖为物联网移动应用保护传输中数据所需遵循的最佳实践。
为移动应用保护传输中的数据具有共同的要求和最佳做法。保护传输中数据的最佳做法包括但不限于以下内容:
- 使用平台支持的最新 TLS 和密码套件配置
- 验证服务器 X.509 证书
- 验证证书主机名
- 仅接受由可信证书颁发机构(包括公共 CA 和内部可信 CA)签名的证书
- 不允许自签名证书
- 仅与受信任证书和/或公钥的 Pin 连接
Android 和 iOS 的实现方式各不相同。两种平台都可以使用本机加密 API;但是,第三方包装库也可用,但可能不具备证书固定等功能。
在前面的示例中,Android 应用使用 CA(可信证书)创建密钥库,该密钥库初始化 TrustManager,其任务是仅验证密钥库中的证书:
- 创建线程安全的
KeyPinStore
类(公共静态同步):
public class KeyPinStore {
private static KeyPinStore instance = null;
private SSLContext sslContext = SSLContext.getInstance("TLS");
public static synchronized KeyPinStore getInstance() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException{
if (instance == null){
instance = new KeyPinStore();
}
return instance;
}
- 加载应用的资产目录中的 CA:
private KeyPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException{
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// randomCA.crt should be in the Assets directory
InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("TrustedCompanyCA.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
- 使用指定的受信任 CA 创建密钥库:
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
- 创建 TrustManager 以验证密钥库中的 CA:
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
- 创建使用我们的 TrustManager 的 SSL 内容:
sslContext.init(null, tmf.getTrustManagers(), null);
}
public SSLContext getContext(){
return sslContext;
}
}
- 告诉 URLConnection 在与应用的 API 端点通信时使用 SSLContext 中的 SocketFactory:
URL url = new URL("https://example.com/rest/apiEndpoint");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
Google 发布了一个名为 nogotofail 的工具,可以帮助确保 TLS/SSL 配置正确到位。nogotofail 不仅可以检查配置,还可以确保不使用易受攻击的 TLS/SSL 协议,并且可以了解通过 MITM 技术从客户端设备发送的数据。要了解更多关于 nogotofail 的信息,请访问项目的 GitHub 页面https://github.com/google/nogotofail 。
类似的操作可用于在 iOS 中锁定证书和/或证书的公钥指纹。钉扎通过NSURLConnectionDelegate
执行,其中connection:canAuthenticateAgainstProtectionSpace:
和connection:didReceiveAuthenticationChallenge:.
必须在connection:didReceiveAuthenticationChallenge:
内执行,并调用SecTrustEvaluate
执行 X509 验证检查。OWASP 提供的示例 iOS 固定应用可作为部署此类检入应用时的参考。可通过以下链接下载示例程序:
https://www.owasp.oimg/9/9a/Pubkey-pin-ios.zip
除了所有应用在使用 TLS 时应遵循的一般最佳实践之外,iOS 还有一项新功能,利用者可以利用这项新功能,并且在将来提交给苹果的应用商店时需要使用这项新功能(https://developer.apple.com/news/?id=12212016b )。此功能称为应用传输安全(ATS),在 iOS 9 中引入,默认启用。ATS 要求应用使用 TLSv1.2 和完美前向保密(PFS以及特定密码套件通过 HTTPS 进行通信。如果某个应用不符合最低要求,将不允许连接到 iOS 应用。这对于所有物联网设备来说都很好;然而,有一些方法可以绕过 ATS。具体来说,利用者可以使用Info.plist
文件中的NSAllowsArbitraryLoads
配置完全禁用 ATS,如下图所示:
不幸的是,由于缺乏密码学和/或 PKI 知识,这在物联网应用中非常常见。ATS 还能够为每个域或全局提供异常,而不是完全禁用 ATS。以下是可应用于以下配置的例外情况的非详尽列表:
- 禁用 PFS(
NSExceptionRequiresForwardSecrecy
) - 禁用媒体的 ATS(
NSAllowsArbitraryLoadsForMedia
- 允许通过 HTTP 进行不安全的连接(
NSExceptionAllowsInsecureHTTPLoads
) - 降低最低 TLS 版本(
NSExceptionMinimumTLSVersion
- 允许连接到本地域(
NSAllowsLocalNetworking
)
苹果公司提供了一个名为 nscurl 的工具来检查应用传输安全问题。Nscurl 可通过执行以下命令来使用:
$ nscurl --ats-diagnostics https://www.packtpub.com
苹果正在做出有希望的改变,以影响利用者,确保数据在传输过程中的安全。如前所述,提交至应用商店的所有应用都必须支持 ATS,具体时间由苹果公司宣布。
- OWASP 提供的示例 Android 公钥锁定应用可通过以下 URL 下载:
https://www.owasp.oimg/1/1f/Pubkey-pin-android.zip
- 有关 ATS 要求的更多信息,请参阅以下 Apple 利用者指南:
当物联网移动应用执行或从第三方应用检索命令时,内部平台 API 用于进程间通信(IPC)。IPC 可用于集成应用,以拨打费用跟踪应用、第三方服务应用(如 IFTTT)或个人助理(如亚马逊的 Alexa)的电话。Android 等平台提供了丰富的 IPC 功能,而 iOS 只提供了几个选项。大多数物联网应用使用平台和硬件功能与物理世界进行交互,从而在对手成功利用漏洞时产生更大的影响。在本食谱中,我们将讨论如何围绕 IPC 使用安全控制,以及如何以安全的方式使用平台 API。
与来自移动平台的应用的命令交互是一种强大的功能。如果没有适当的安全保护,未经授权的应用可能会劫持命令并访问非预期方接收的数据。使用平台 API 时,应考虑以下做法:
- 除非这些机制得到适当保护,否则不要通过 IPC 导出敏感功能。
- 必要时,应对来自外部来源和用户的输入进行验证和消毒。这包括通过用户界面、IPC 机制(如意图、自定义 URL 处理程序和网络源)接收的数据。
- WebView 应配置为仅允许所需的最小协议处理程序集,如 HTTPS,并禁用其他危险处理程序,如
file://
、tel://
、sms://
和app-id://
。 - 将 IPC 调用限制为受信任应用的白名单。
- 要在本地或远程加载的白名单网页和 URL 处理程序。
- 仅请求应用功能所需的最低权限集。
- 应该验证通过 WebView 公开的本机方法是否只呈现应用沙箱中的 JavaScript。
- 除非明确要求,否则 WebView 应禁用 JavaScript。
- 序列化应仅使用安全的序列化 API,并进行加密签名。
列出的大多数实践可以应用于 Android 和 iOS 平台;但是,应根据应用的功能审查特定注意事项,如 Android 权限、自定义权限和保护级别。
以下是启动MAIN_ACTIVITY``Activity
时所需的名为IOT_COOKBOOK_ACTIVITY
的自定义权限示例。
- 第一个代码块使用标签标签和关于
Activity
的描述定义新权限。接下来,根据授予的权限类型设置保护级别。一旦定义了权限,就可以通过在应用的AndroidManifest.xml
文件中指定 uses 权限来在组件上强制该权限。在下面的示例中,第二个块是我们将使用定义的权限限制的组件。可以通过添加android:permission
属性来强制:
<permission android:name="com.packtpub.cookbook.permission.IOT_COOKBOOK_ACTIVITY"
android:label="Start main Activity in packtpub"
android:description="Allow only apps signed with the same certificate to launch this Activity."
android:protectionLevel="signature" />
<activity android:name="MAIN_ACTIVITY"
android:permission="com.packtpub.cookbook.permission.IOT_COOKBOOK_ACTIVITY">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
- 现在,新权限
IOT_COOKBOOK_ACTIVTY
已经创建,应用可以使用AndroidManifest.xml
文件中的 uses 权限标记请求它。在这种情况下,必须是使用相同证书签名的应用才能启动MAIN_ACTIVITY
:
<uses-permission android:name="com.example.myapp.permission.IOT_COOKBOOK_ACTIVITY"/>
在引入自定义权限和保护级别时,最好参考 Android 的利用人员文档。所有安卓权限均可在安卓利用者文档中找到 https://developer.android.com/guide/topics/permissions/requesting.html 。
在 iOS 应用中,由于 iOS 的封闭生态系统,权限不适用。然而,iOS 和 Android 共享网络视图,这使得网页可以加载到应用中。与 web 应用类似,恶意代码可以在 web 浏览器中执行,包括 WebView 中的浏览器。在减少 IOT 应用的攻击面时,这一点很重要。
- 以下代码片段说明了如何在 iOS 应用中禁用 WKWebView 中的 JavaScript:
#import "ViewController.h"
#import <WebKit/WebKit.h>
@interface ViewController ()<WKNavigationDelegate,WKUIDelegate>
@property(strong,nonatomic) WKWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
NSURL *url = [NSURL URLWithString:@"https://www.packtpub.com/"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
WKPreferences *pref = [[WKPreferences alloc] init];
[pref setJavaScriptEnabled:NO];
[pref setJavaScriptCanOpenWindowsAutomatically:NO];
WKWebViewConfiguration *conf = [[WKWebViewConfiguration alloc] init];
[conf setPreferences:pref];
_webView = [[WKWebView alloc]initWithFrame:CGRectMake(self.view.frame.origin.x,85, self.view.frame.size.width, self.view.frame.size.height-85) configuration:conf] ;
[_webView loadRequest:request];
[self.view addSubview:_webView];
}
- 对于 Android 应用,禁用 JavaScript 是通过配置 WebView 的
WebSettings
来完成的,如下所示。如果不需要,应配置其他设置,例如禁用文件系统访问、关闭插件和关闭地理位置:
WebView webview = new WebView(this);
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(false);
webView.getSettings().setPluginState(WebSettings.PluginState.OFF);
webView.getSettings().setAllowFileAccess(false);
webView.getSettings().setGeolocationEnabled(false);
setContentView(webview);
webview.loadUrl("https://www.packetpub.com/");
考虑到最低特权和安全深度原则,应用应仅为所需的业务功能使用和公开平台组件。根据经验,从第三方应用发送和检索的任何数据都应被视为不可信,并进行适当的验证。
与所有软件一样,移动应用大量使用第三方库和包装器来执行诸如发出 HTTP 请求或加密对象之类的功能。这些库还可能在应用中引入弱点,暴露机密信息或影响应用本身的完整性。考虑到这一点,应审查第三方代码的漏洞,在适用的情况下进行更新和测试。这对于依赖第三方混合框架和库来发送、接收和保存数据的混合应用尤其如此。本食谱将讨论确保第三方代码不会将漏洞引入物联网应用的方法。
在第 8 章、固件安全最佳实践中,讨论了使用 NSP 和 Retire.js 扫描 JavaScript 库的方法,这些方法仍然可以应用于移动应用。为确保第三方代码不会在移动应用中引入安全漏洞,应考虑以下建议:
- 使用 nsp、Retirejs 和依赖性检查(等工具,持续清点库和框架的版本及其依赖性 https://github.com/jeremylong/DependencyCheck )
- 为移动应用中使用的所有组件和第三方软件创建物料清单
- 通过分析工具持续监控漏洞数据库(如 NVD)中已使用组件中的漏洞,以实现流程自动化
- 分析第三方库,确保它们在运行时被调用,并删除应用函数不需要的函数
- 确保混合框架使用最新版本
- 监控 hybrid framework 版本和博客,以确保未使用具有漏洞的已知组件
- 修补事件框架中易受攻击的库利用人员不合并上游库
- 监控利用的开放源代码存储库的安全问题和顾虑
- 确保混合框架插件在使用前已检查过安全缺陷
- 利用较新的 Android 版本 API 利用新引入的功能(苹果强制 iOS 更新)
- 查看 Android 和 iOS 安全公告,了解平台漏洞和新的安全功能
最常见的移动混合框架之一是 Apache 的 Cordova。可通过以下命令为 iOS 和 Android 更新 Cordova:
cordova platform update ios
cordova platform update android@<version number>
Cordova 以成为研究人员的目标而闻名,并且经常在 Android 和 iOS 的新版本中包含安全更新。Cordova 的发行说明可通过其位于的博客找到 https://cordova.apache.org/blog/ 。寻找尚未发布的 bug 的一个好地方是框架的 bug 跟踪系统,如 Cordova 的(https://issues.apache.org/jira/projects/CB/summary )。您将惊奇地看到修复、报告和关闭的 bug 数量。例如,使用的另一个流行的混合框架是 Xamarin。Xamarin 的 credential manager 使用了一个硬编码的 Android 密钥库密码,从 2014 年 4 月起,帐户凭据就有被泄露的风险,直到 2016 年底被修复。可以通过查看项目的 GitHub 存储库找到此信息 https://github.com/xamarin/Xamarin.Auth/issues/55 。
- Google 拍摄 Google Play 中使用的设备的快照,并将这些数据发布到仪表板上,以帮助确定支持不同设备的优先级(https://developer.android.com/about/dashboards/index.html )
- 谷歌每个月都会发布安卓安全公告,列出公告、CVE 漏洞及其严重性和缓解措施。安卓安全公告可在上找到 https://source.android.com/security/bulletin/ 。
- 苹果每年都会发布一份 iOS 安全指南,详细介绍新 iOS 版本的平台安全功能和新的安全控制功能。iOS 安全指南可在找到 https://www.apple.com/business/docs/iOS_Security_Guide.pdf 。
当有用户体验(UX)的内部和外包团队的代码库、特定功能集(如在应用启动期间查找设备、确保规则设置正确执行)时,编写安全代码可能会很困难,以及其他方面,如确保应用更新不会对网络中的物联网设备产生负面影响。由于一个应用如此复杂,攻击者必然会发现漏洞并规避安全控制。是的,这对于任何软件来说都是不可避免的,尽管有技术可以使反向工程更难让攻击者破坏应用并窃取公司的知识产权(IP)。
这些技术可以内置到应用逻辑中,以防止运行时修改、通过混淆应用类对应用二进制文件进行静态分析以及对数据进行分割,从而为潜在的危害做好准备。需要注意的是,应用仍然需要将安全控制构建到应用中,而不是用第三方软件保护取代控制。此配方将介绍一些做法,使应用更能抵御攻击。这些做法不仅使应用更具弹性,而且有助于作为应用反滥用系统一部分的深入防御。
实施应用反向工程控制和代码修改技术时,应遵循以下实践:
- 应用应通过提醒用户或终止应用来检测并响应根设备或越狱设备
- 类和方法的模糊处理应用于构建,以通过动态分析阻止消除模糊
- 只要可能,硬件支持的进程隔离优先于模糊处理
- 应用应防止调试,并防止连接调试器
- 应用应该检测反向工程工具和框架的存在
- 应用应在模拟环境中运行时进行检测并做出适当响应
- 生产构建应该剥离符号
- 生产版本不应包含调试代码或可调试功能,如
android:debuggable="false"
- Android 应用可以使用 SafetyNet 认证 API 兼容性检查来确保应用未被未知源修改
- 应使用 SafetyNet 验证应用 API 检查设备上是否安装了任何潜在有害的应用(https://developer.android.com/training/safetynet/verify-apps.html )
iOS 应用可以查找常见的基于越狱文件的检查,如以下列表(https://github.com/OWASP/owasp-mstg/blob/master/Document/0x06j-Testing-Resiliency-Against-Reverse-Engineering.md :
/Applications/Cydia.app
/Applications/FakeCarrier.app
/Applications/Icy.app
/Applications/IntelliScreen.app
/Applications/MxTube.app
/Applications/RockApp.app
/Applications/SBSettings.app
/Applications/WinterBoard.app
/Applications/blackra1n.app
/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist
/Library/MobileSubstrate/DynamicLibraries/Veency.plist
/Library/MobileSubstrate/MobileSubstrate.dylib
/System/Library/LaunchDaemons/com.ikey.bbot.plist
/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist
/bin/bash
/bin/sh
/etc/apt
/etc/ssh/sshd_config
/private/var/lib/apt
/private/var/lib/cydia
/private/var/mobile/Library/SBSettings/Themes
/private/var/stash
/private/var/tmp/cydia.log
/usr/bin/sshd
/usr/libexec/sftp-server
/usr/libexec/ssh-keysign
/usr/sbin/sshd
/var/cache/apt
/var/lib/apt
/var/lib/cydia
此外,iOS 应用可以尝试执行根级别的系统 API 调用,或者通过将数据写入应用沙箱之外的文件来检查文件权限,以检测设备是否越狱。
Android 应用可以使用类似的方法来检查常见的根设备文件,并尝试以根用户身份执行命令。常用根文件和应用的列表如下所示(https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05j-Testing-Resiliency-Against-Reverse-Engineering.md :
/system/xbin/busybox
/sbin/su
/system/bin/su
/system/xbin/su
/data/local/su
/data/local/xbin/su
com.thirdparty.superuser
eu.chainfire.supersu
com.noshufou.android.su
com.koushikdutta.superuser
com.zachspong.temprootremovejb
com.ramdroid.appquarantine
此外,检查定制的 Android ROM 版本可以指示一个根设备,尽管这不是一个确定的方法。
应使用多种检查和防御方法来确保恢复力。总体目标是确保攻击者不能篡改、修改代码、执行运行时修改和反向工程应用包以防止滥用。在应用启动时和整个运行时,可以将前面的几个实践引入到应用的逻辑中。商业解决方案可用于执行一些早期列出的实践和更多;但是,在集成到应用之前,应该对其进行审查。
要了解移动应用反向工程和未经授权的代码修改的风险,请参考 OWASP 的反向工程和代码修改预防项目https://www.owasp.org/index.php/OWASP_Reverse_Engineering_and_Code_Modification_Prevention_Project 。该项目在描述技术和业务风险用例以及补充缓解建议方面做得很好。
- 有关通过 SafetyNet API 请求兼容性检查的更多信息,请访问以下 Android 利用者页面(https://developer.android.com/training/safetynet/attestation.html#cts-勾选。