Setting Up a Mobile Testing Environment
Setting Up a Mobile Testing Environment
Creating a comprehensive testing environment is essential for thorough security assessment.
iOS Testing Environment Setup:
// iOS - Security testing helper class
import Foundation
import Network
class SecurityTestingHelper {
// Network monitoring for security testing
class NetworkMonitor {
private let monitor = NWPathMonitor()
private let queue = DispatchQueue(label: "SecurityTesting")
func startMonitoring() {
monitor.pathUpdateHandler = { path in
self.logNetworkActivity(path)
self.detectProxyUsage(path)
}
monitor.start(queue: queue)
}
private func detectProxyUsage(_ path: NWPath) {
// Check for proxy configuration (useful for testing)
if let proxySettings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as? [String: Any] {
if let httpProxy = proxySettings[kCFNetworkProxiesHTTPProxy as String] as? String,
!httpProxy.isEmpty {
print("HTTP Proxy detected: \(httpProxy)")
}
if let httpsProxy = proxySettings[kCFNetworkProxiesHTTPSProxy as String] as? String,
!httpsProxy.isEmpty {
print("HTTPS Proxy detected: \(httpsProxy)")
}
}
}
}
// Certificate validation testing
func testCertificatePinning(for host: String) -> Bool {
var result = false
let semaphore = DispatchSemaphore(value: 0)
let url = URL(string: "https://\(host)")!
let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
let task = session.dataTask(with: url) { _, _, error in
result = error == nil
semaphore.signal()
}
task.resume()
semaphore.wait()
return result
}
// Test data protection levels
func testDataProtection() -> [String: Bool] {
var results: [String: Bool] = [:]
let testData = "Sensitive test data".data(using: .utf8)!
let documentsPath = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
// Test different protection levels
let protectionLevels: [(String, FileProtectionType)] = [
("Complete", .complete),
("CompleteUnlessOpen", .completeUnlessOpen),
("CompleteUntilFirstUserAuthentication", .completeUntilFirstUserAuthentication),
("None", .none)
]
for (name, protection) in protectionLevels {
let fileURL = documentsPath.appendingPathComponent("test_\(name).dat")
do {
try testData.write(to: fileURL)
try FileManager.default.setAttributes(
[.protectionKey: protection],
ofItemAtPath: fileURL.path
)
// Verify protection level
let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path)
let actualProtection = attributes[.protectionKey] as? FileProtectionType
results[name] = actualProtection == protection
// Clean up
try FileManager.default.removeItem(at: fileURL)
} catch {
results[name] = false
}
}
return results
}
}
Android Testing Environment:
// Android - Security testing utilities
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import java.security.MessageDigest
import java.security.cert.Certificate
import java.util.concurrent.TimeUnit
import okhttp3.*
class SecurityTestingUtils(private val context: Context) {
// Test certificate pinning implementation
fun testCertificatePinning(url: String, expectedPins: List<String>): TestResult {
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(url)
.build()
return try {
val response = client.newCall(request).execute()
val certificates = response.handshake?.peerCertificates
val actualPins = certificates?.map { cert ->
val publicKey = cert.publicKey
val publicKeyBytes = publicKey.encoded
val digest = MessageDigest.getInstance("SHA-256").digest(publicKeyBytes)
"sha256/" + android.util.Base64.encodeToString(digest, android.util.Base64.NO_WRAP)
} ?: emptyList()
val pinningValid = actualPins.any { it in expectedPins }
TestResult(
passed = pinningValid,
message = if (pinningValid) "Certificate pinning valid" else "Certificate pinning failed",
details = mapOf(
"expected_pins" to expectedPins,
"actual_pins" to actualPins
)
)
} catch (e: Exception) {
TestResult(
passed = false,
message = "Connection failed: ${e.message}",
details = mapOf("error" to e.toString())
)
}
}
// Test for insecure data storage
fun testDataStorage(): List<TestResult> {
val results = mutableListOf<TestResult>()
// Check SharedPreferences encryption
results.add(testSharedPreferencesEncryption())
// Check database encryption
results.add(testDatabaseEncryption())
// Check external storage usage
results.add(testExternalStorageUsage())
// Check for sensitive data in logs
results.add(testLogLeakage())
return results
}
private fun testSharedPreferencesEncryption(): TestResult {
val prefs = context.getSharedPreferences("test_prefs", Context.MODE_PRIVATE)
val testKey = "test_sensitive_data"
val testValue = "sensitive_value_12345"
// Store test data
prefs.edit().putString(testKey, testValue).apply()
// Check if data is encrypted on disk
val prefsFile = context.filesDir.parentFile?.resolve("shared_prefs/test_prefs.xml")
val fileContent = prefsFile?.readText() ?: ""
val isEncrypted = !fileContent.contains(testValue)
// Clean up
prefs.edit().remove(testKey).apply()
return TestResult(
passed = isEncrypted,
message = if (isEncrypted) "SharedPreferences appear encrypted" else "SharedPreferences not encrypted",
details = mapOf("file_path" to prefsFile?.absolutePath)
)
}
// Test for debugging and development artifacts
fun testDebuggingArtifacts(): List<TestResult> {
val results = mutableListOf<TestResult>()
// Check if debuggable
val isDebuggable = context.applicationInfo.flags and
android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE != 0
results.add(TestResult(
passed = !isDebuggable,
message = if (!isDebuggable) "App is not debuggable" else "App is debuggable",
details = mapOf("debuggable" to isDebuggable)
))
// Check for backup enabled
val allowBackup = context.applicationInfo.flags and
android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP != 0
results.add(TestResult(
passed = !allowBackup,
message = if (!allowBackup) "Backup is disabled" else "Backup is enabled",
details = mapOf("allow_backup" to allowBackup)
))
// Check for test keys
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNING_CERTIFICATES)
.signingInfo.apkContentsSigners
} else {
@Suppress("DEPRECATION")
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)
.signatures
}
val hasTestKeys = signatures.any { signature ->
signature.toCharsString().contains("CN=Android Debug")
}
results.add(TestResult(
passed = !hasTestKeys,
message = if (!hasTestKeys) "Production keys used" else "Debug keys detected",
details = mapOf("has_test_keys" to hasTestKeys)
))
return results
}
data class TestResult(
val passed: Boolean,
val message: String,
val details: Map<String, Any?> = emptyMap()
)
}