react-native-device-defense 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -1
- package/android/build.gradle +1 -1
- package/android/src/main/cpp/device-security.cpp +281 -7
- package/android/src/main/java/com/devicedefense/DeviceSecurityModule.kt +126 -2
- package/android/src/main/java/com/devicedefense/EmulatorDetection.kt +29 -18
- package/android/src/main/java/com/devicedefense/NativeSecurityCheck.kt +43 -1
- package/android/test-gradle.gradle +3 -0
- package/lib/commonjs/NativeDeviceSecurity.js.map +1 -1
- package/lib/commonjs/api.js +137 -2
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/components/SecurityBlockedScreen.js +12 -2
- package/lib/commonjs/components/SecurityBlockedScreen.js.map +1 -1
- package/lib/commonjs/hooks/useDeviceSecurity.js +13 -7
- package/lib/commonjs/hooks/useDeviceSecurity.js.map +1 -1
- package/lib/module/NativeDeviceSecurity.js.map +1 -1
- package/lib/module/api.js +137 -2
- package/lib/module/api.js.map +1 -1
- package/lib/module/components/SecurityBlockedScreen.js +12 -2
- package/lib/module/components/SecurityBlockedScreen.js.map +1 -1
- package/lib/module/hooks/useDeviceSecurity.js +14 -8
- package/lib/module/hooks/useDeviceSecurity.js.map +1 -1
- package/lib/typescript/NativeDeviceSecurity.d.ts +7 -0
- package/lib/typescript/NativeDeviceSecurity.d.ts.map +1 -1
- package/lib/typescript/api.d.ts +29 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/components/SecurityBlockedScreen.d.ts.map +1 -1
- package/lib/typescript/hooks/useDeviceSecurity.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +30 -1
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/NativeDeviceSecurity.ts +9 -0
- package/src/api.ts +143 -0
- package/src/components/SecurityBlockedScreen.tsx +10 -0
- package/src/hooks/useDeviceSecurity.ts +14 -11
- package/src/types.ts +36 -1
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- 🎣 **Frida/Xposed Detection** - Detect common hooking frameworks
|
|
10
10
|
- 🐛 **Anti-Debug** - Detect debugger attachment
|
|
11
11
|
- 📱 **Emulator Detection** - Detect Android emulators
|
|
12
|
+
- 🔐 **SSL Pinning Detection** - Native C++ SSL security checks to prevent MITM attacks
|
|
12
13
|
- 🛡️ **App Integrity Check** - Verify app signature and tampering
|
|
13
14
|
- 🔐 **Block on Security Threat** - Automatically block app when security issues detected
|
|
14
15
|
|
|
@@ -22,6 +23,17 @@ yarn add react-native-device-defense
|
|
|
22
23
|
|
|
23
24
|
## Android Setup
|
|
24
25
|
|
|
26
|
+
### React Native 0.60+ (Autolinking)
|
|
27
|
+
|
|
28
|
+
No manual setup required! Just install and rebuild:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
yarn add react-native-device-defense
|
|
32
|
+
npx react-native run-android
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### React Native < 0.60 (Manual Linking)
|
|
36
|
+
|
|
25
37
|
1. Add to `android/settings.gradle`:
|
|
26
38
|
|
|
27
39
|
```gradle
|
|
@@ -135,6 +147,49 @@ DeviceSecurity.blockOnSecurityThreat({
|
|
|
135
147
|
});
|
|
136
148
|
```
|
|
137
149
|
|
|
150
|
+
### SSL Security Check
|
|
151
|
+
|
|
152
|
+
Prevent MITM (Man-in-the-Middle) attacks with native SSL security checks:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import DeviceSecurity from 'react-native-device-defense';
|
|
156
|
+
|
|
157
|
+
// Quick SSL security check
|
|
158
|
+
const hasSSLIssue = DeviceSecurity.hasSSLSecurityIssue();
|
|
159
|
+
|
|
160
|
+
if (hasSSLIssue) {
|
|
161
|
+
console.log('SSL security issue detected!');
|
|
162
|
+
// Block sensitive operations or show warning
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Detailed SSL security status
|
|
166
|
+
const sslStatus = await DeviceSecurity.getSSLSecurityStatus();
|
|
167
|
+
|
|
168
|
+
console.log({
|
|
169
|
+
hasSSLValidationBypass: sslStatus.hasSSLValidationBypass,
|
|
170
|
+
hasSSLPinningBypass: sslStatus.hasSSLPinningBypass,
|
|
171
|
+
hasProxyConfiguration: sslStatus.hasProxyConfiguration, // Potential MITM
|
|
172
|
+
hasModifiedSSLLibraries: sslStatus.hasModifiedSSLLibraries,
|
|
173
|
+
hasCertificateTampering: sslStatus.hasCertificateTampering,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Check for specific SSL threats
|
|
177
|
+
if (DeviceSecurity.hasSSLPinningBypass()) {
|
|
178
|
+
// SSL pinning bypass tools detected (Frida, Xposed, etc.)
|
|
179
|
+
Alert.alert('Security Warning', 'SSL pinning bypass detected. Your connection may not be secure.');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (DeviceSecurity.hasProxyConfiguration()) {
|
|
183
|
+
// Proxy configured - potential MITM attack
|
|
184
|
+
console.warn('Proxy configuration detected - possible MITM');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (DeviceSecurity.hasCertificateTampering()) {
|
|
188
|
+
// Excessive user certificates detected
|
|
189
|
+
Alert.alert('Security Warning', 'Device certificates have been modified.');
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
138
193
|
## API Reference
|
|
139
194
|
|
|
140
195
|
### Methods
|
|
@@ -143,6 +198,7 @@ DeviceSecurity.blockOnSecurityThreat({
|
|
|
143
198
|
|--------|---------|-------------|
|
|
144
199
|
| `isDeviceSecure()` | `Promise<boolean>` | Check if device is secure (no threats) |
|
|
145
200
|
| `getSecurityStatus()` | `Promise<SecurityStatus>` | Get detailed security status |
|
|
201
|
+
| `getSSLSecurityStatus()` | `Promise<SSLSecurityStatus>` | Get detailed SSL security status |
|
|
146
202
|
| `blockOnSecurityThreat(options)` | `void` | Block app when security threat detected |
|
|
147
203
|
| `isRooted()` | `boolean` | Check if device is rooted (synchronous) |
|
|
148
204
|
| `hasFrida()` | `boolean` | Check if Frida is present |
|
|
@@ -150,6 +206,12 @@ DeviceSecurity.blockOnSecurityThreat({
|
|
|
150
206
|
| `hasMagisk()` | `boolean` | Check if Magisk is present |
|
|
151
207
|
| `isDebuggable()` | `boolean` | Check if app is debuggable |
|
|
152
208
|
| `isEmulator()` | `boolean` | Check if running on emulator |
|
|
209
|
+
| `hasSSLValidationBypass()` | `boolean` | Check if SSL validation is bypassed |
|
|
210
|
+
| `hasSSLPinningBypass()` | `boolean` | Check for SSL pinning bypass tools |
|
|
211
|
+
| `hasProxyConfiguration()` | `boolean` | Check if proxy is configured (MITM risk) |
|
|
212
|
+
| `hasModifiedSSLLibraries()` | `boolean` | Check if SSL libraries are modified |
|
|
213
|
+
| `hasCertificateTampering()` | `boolean` | Check for certificate tampering |
|
|
214
|
+
| `hasSSLSecurityIssue()` | `boolean` | Comprehensive SSL security check |
|
|
153
215
|
|
|
154
216
|
### Types
|
|
155
217
|
|
|
@@ -168,6 +230,22 @@ interface SecurityStatus {
|
|
|
168
230
|
hasMagisk: boolean;
|
|
169
231
|
isDebuggable: boolean;
|
|
170
232
|
isEmulator: boolean;
|
|
233
|
+
// SSL security fields
|
|
234
|
+
hasSSLValidationBypass: boolean;
|
|
235
|
+
hasSSLPinningBypass: boolean;
|
|
236
|
+
hasProxyConfiguration: boolean;
|
|
237
|
+
hasModifiedSSLLibraries: boolean;
|
|
238
|
+
hasCertificateTampering: boolean;
|
|
239
|
+
hasSSLSecurityIssue: boolean;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
interface SSLSecurityStatus {
|
|
243
|
+
hasSSLValidationBypass: boolean;
|
|
244
|
+
hasSSLPinningBypass: boolean;
|
|
245
|
+
hasProxyConfiguration: boolean;
|
|
246
|
+
hasModifiedSSLLibraries: boolean;
|
|
247
|
+
hasCertificateTampering: boolean;
|
|
248
|
+
hasSSLSecurityIssue: boolean;
|
|
171
249
|
}
|
|
172
250
|
|
|
173
251
|
type SecurityThreat =
|
|
@@ -177,7 +255,12 @@ type SecurityThreat =
|
|
|
177
255
|
| 'magisk_detected'
|
|
178
256
|
| 'debugger_detected'
|
|
179
257
|
| 'emulator_detected'
|
|
180
|
-
| 'system_props_modified'
|
|
258
|
+
| 'system_props_modified'
|
|
259
|
+
| 'ssl_validation_bypass'
|
|
260
|
+
| 'ssl_pinning_bypass'
|
|
261
|
+
| 'proxy_configuration'
|
|
262
|
+
| 'modified_ssl_libraries'
|
|
263
|
+
| 'certificate_tampering';
|
|
181
264
|
```
|
|
182
265
|
|
|
183
266
|
## Configuration
|
|
@@ -219,6 +302,14 @@ Add to `android/app/proguard-rules.pro`:
|
|
|
219
302
|
- Generic device features
|
|
220
303
|
- Genymotion, Nox, BlueStacks detection
|
|
221
304
|
|
|
305
|
+
### SSL Security Detection (Native C++)
|
|
306
|
+
- SSL validation bypass detection
|
|
307
|
+
- SSL pinning bypass tools detection (Frida, Xposed, Substrate)
|
|
308
|
+
- Proxy configuration detection (MITM risk)
|
|
309
|
+
- Modified SSL libraries detection
|
|
310
|
+
- Certificate tampering detection
|
|
311
|
+
- User-installed CA certificate monitoring
|
|
312
|
+
|
|
222
313
|
## License
|
|
223
314
|
|
|
224
315
|
MIT
|
package/android/build.gradle
CHANGED
|
@@ -83,7 +83,7 @@ dependencies {
|
|
|
83
83
|
implementation 'com.facebook.react:react-native:+'
|
|
84
84
|
|
|
85
85
|
// RootBeer for root detection
|
|
86
|
-
implementation 'com.scottyab:rootbeer:0.1.
|
|
86
|
+
implementation 'com.scottyab:rootbeer-lib:0.1.1'
|
|
87
87
|
|
|
88
88
|
testImplementation 'junit:junit:4.13.2'
|
|
89
89
|
testImplementation 'org.mockito:mockito-core:5.3.1'
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
#include <unistd.h>
|
|
8
8
|
#include <android/log.h>
|
|
9
9
|
#include <stdexcept>
|
|
10
|
+
#include <openssl/opensslv.h>
|
|
11
|
+
#include <openssl/x509.h>
|
|
12
|
+
#include <openssl/pem.h>
|
|
13
|
+
#include <openssl/err.h>
|
|
10
14
|
|
|
11
15
|
#define LOG_TAG "DeviceSecurityNative"
|
|
12
16
|
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
|
@@ -237,6 +241,234 @@ static bool checkFridaInMaps() {
|
|
|
237
241
|
return false;
|
|
238
242
|
}
|
|
239
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Check for SSL validation bypass in system properties
|
|
246
|
+
*/
|
|
247
|
+
static bool checkSSLValidationBypass() {
|
|
248
|
+
const std::vector<std::string> propFiles = {
|
|
249
|
+
"/system/build.prop",
|
|
250
|
+
"/vendor/build.prop",
|
|
251
|
+
"/default.prop"
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
for (const auto& propFile : propFiles) {
|
|
255
|
+
if (!fileExists(propFile)) continue;
|
|
256
|
+
|
|
257
|
+
std::ifstream file(propFile);
|
|
258
|
+
std::string line;
|
|
259
|
+
|
|
260
|
+
while (std::getline(file, line)) {
|
|
261
|
+
// Check for SSL validation bypass indicators
|
|
262
|
+
if (line.find("ssl.untrusted=0") != std::string::npos) {
|
|
263
|
+
LOGD("Found SSL validation bypass in %s", propFile.c_str());
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
if (line.find("ro.debuggable") != std::string::npos &&
|
|
267
|
+
line.find("1") != std::string::npos) {
|
|
268
|
+
// Debuggable builds may have SSL validation bypassed
|
|
269
|
+
LOGD("Device is debuggable, SSL may be bypassed");
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Check for common SSL pinning bypass tools and frameworks
|
|
280
|
+
*/
|
|
281
|
+
static bool checkSSLPinningBypass() {
|
|
282
|
+
// Check for known SSL pinning bypass tools in /proc/self/maps
|
|
283
|
+
std::ifstream mapsFile("/proc/self/maps");
|
|
284
|
+
std::string line;
|
|
285
|
+
|
|
286
|
+
const std::vector<std::string> bypassLibraries = {
|
|
287
|
+
"libssl-bypass",
|
|
288
|
+
" Frida",
|
|
289
|
+
"xposed",
|
|
290
|
+
"substrate",
|
|
291
|
+
"magisk",
|
|
292
|
+
"r0puse",
|
|
293
|
+
"ssl-pin bypass",
|
|
294
|
+
"trustmekit"
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
while (std::getline(mapsFile, line)) {
|
|
298
|
+
std::string lowerLine = line;
|
|
299
|
+
std::transform(lowerLine.begin(), lowerLine.end(), lowerLine.begin(), ::tolower);
|
|
300
|
+
|
|
301
|
+
for (const auto& lib : bypassLibraries) {
|
|
302
|
+
if (lowerLine.find(lib) != std::string::npos) {
|
|
303
|
+
LOGD("Found SSL pinning bypass tool: %s", lib.c_str());
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Check for common SSL bypass apps
|
|
310
|
+
const std::vector<std::string> bypassApps = {
|
|
311
|
+
"/data/data/de.robv.android.xposed.installer",
|
|
312
|
+
"/data/data/com.sensei.withakemon",
|
|
313
|
+
"/data/data/com.solid.pinkyscan",
|
|
314
|
+
"/data/data/jp.co.cyberagent.android.deviceauthorization"
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
for (const auto& app : bypassApps) {
|
|
318
|
+
if (directoryExists(app)) {
|
|
319
|
+
LOGD("Found SSL bypass app: %s", app.c_str());
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Check for proxy configuration that could intercept SSL traffic
|
|
329
|
+
*/
|
|
330
|
+
static bool checkProxyConfiguration() {
|
|
331
|
+
// Check for HTTP proxy in system properties
|
|
332
|
+
const std::vector<std::string> propFiles = {
|
|
333
|
+
"/system/build.prop",
|
|
334
|
+
"/vendor/build.prop"
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
for (const auto& propFile : propFiles) {
|
|
338
|
+
if (!fileExists(propFile)) continue;
|
|
339
|
+
|
|
340
|
+
std::ifstream file(propFile);
|
|
341
|
+
std::string line;
|
|
342
|
+
|
|
343
|
+
while (std::getline(file, line)) {
|
|
344
|
+
if (line.find("http.proxy") != std::string::npos ||
|
|
345
|
+
line.find("https.proxy") != std::string::npos) {
|
|
346
|
+
LOGD("Found proxy configuration in %s", propFile.c_str());
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Check for proxy environment variables
|
|
353
|
+
if (getenv("http_proxy") != nullptr || getenv("https_proxy") != nullptr) {
|
|
354
|
+
LOGD("Found proxy environment variables");
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Check for modified SSL libraries
|
|
363
|
+
*/
|
|
364
|
+
static bool checkModifiedSSLLibraries() {
|
|
365
|
+
// Check if SSL libraries are from unexpected locations
|
|
366
|
+
std::ifstream mapsFile("/proc/self/maps");
|
|
367
|
+
std::string line;
|
|
368
|
+
|
|
369
|
+
const std::vector<std::string> trustedPaths = {
|
|
370
|
+
"/system/lib/libssl",
|
|
371
|
+
"/system/lib64/libssl",
|
|
372
|
+
"/apex/com.android.conscrypt/lib",
|
|
373
|
+
"/data/app/", // App's own lib path
|
|
374
|
+
"/com.android.conscrypt"
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
while (std::getline(mapsFile, line)) {
|
|
378
|
+
if (line.find("libssl") != std::string::npos ||
|
|
379
|
+
line.find("libcrypto") != std::string::npos) {
|
|
380
|
+
|
|
381
|
+
// Check if library is from trusted path
|
|
382
|
+
bool fromTrustedPath = false;
|
|
383
|
+
for (const auto& trusted : trustedPaths) {
|
|
384
|
+
if (line.find(trusted) != std::string::npos) {
|
|
385
|
+
fromTrustedPath = true;
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (!fromTrustedPath && line.find("r-xp") != std::string::npos) {
|
|
391
|
+
// Library is executable but not from trusted path
|
|
392
|
+
LOGD("Found potentially modified SSL library: %s", line.c_str());
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Check for certificate tampering
|
|
403
|
+
*/
|
|
404
|
+
static bool checkCertificateTampering() {
|
|
405
|
+
// Check for user-installed CA certificates
|
|
406
|
+
const std::vector<std::string> certPaths = {
|
|
407
|
+
"/data/misc/keychain/cacerts-added",
|
|
408
|
+
"/system/etc/security/cacerts"
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
for (const auto& certPath : certPaths) {
|
|
412
|
+
if (directoryExists(certPath)) {
|
|
413
|
+
DIR* dir = opendir(certPath.c_str());
|
|
414
|
+
if (dir != nullptr) {
|
|
415
|
+
struct dirent* entry;
|
|
416
|
+
int certCount = 0;
|
|
417
|
+
|
|
418
|
+
while ((entry = readdir(dir)) != nullptr) {
|
|
419
|
+
if (entry->d_type == DT_REG) {
|
|
420
|
+
certCount++;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
closedir(dir);
|
|
425
|
+
|
|
426
|
+
// Too many user certificates might indicate tampering
|
|
427
|
+
if (certCount > 100) {
|
|
428
|
+
LOGD("Suspicious number of certificates: %d", certCount);
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Comprehensive SSL security check
|
|
440
|
+
*/
|
|
441
|
+
static bool checkSSLSecurity() {
|
|
442
|
+
bool hasIssue = false;
|
|
443
|
+
|
|
444
|
+
if (checkSSLValidationBypass()) {
|
|
445
|
+
LOGD("SSL validation bypass detected");
|
|
446
|
+
hasIssue = true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (checkSSLPinningBypass()) {
|
|
450
|
+
LOGD("SSL pinning bypass detected");
|
|
451
|
+
hasIssue = true;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (checkProxyConfiguration()) {
|
|
455
|
+
LOGD("Proxy configuration detected");
|
|
456
|
+
hasIssue = true;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (checkModifiedSSLLibraries()) {
|
|
460
|
+
LOGD("Modified SSL libraries detected");
|
|
461
|
+
hasIssue = true;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (checkCertificateTampering()) {
|
|
465
|
+
LOGD("Certificate tampering detected");
|
|
466
|
+
hasIssue = true;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return hasIssue;
|
|
470
|
+
}
|
|
471
|
+
|
|
240
472
|
/**
|
|
241
473
|
* Main root detection function
|
|
242
474
|
*/
|
|
@@ -269,45 +501,87 @@ static bool performRootDetection() {
|
|
|
269
501
|
return false;
|
|
270
502
|
}
|
|
271
503
|
|
|
504
|
+
// Updated JNI functions with new package name
|
|
272
505
|
extern "C" JNIEXPORT jboolean JNICALL
|
|
273
|
-
|
|
506
|
+
Java_com_devicedefense_NativeSecurityCheck_isRooted(JNIEnv* env, jobject /* this */) {
|
|
274
507
|
return performRootDetection() ? JNI_TRUE : JNI_FALSE;
|
|
275
508
|
}
|
|
276
509
|
|
|
277
510
|
extern "C" JNIEXPORT jboolean JNICALL
|
|
278
|
-
|
|
511
|
+
Java_com_devicedefense_NativeSecurityCheck_hasDangerousBinaries(JNIEnv* env, jobject /* this */) {
|
|
279
512
|
return checkDangerousBinaries() ? JNI_TRUE : JNI_FALSE;
|
|
280
513
|
}
|
|
281
514
|
|
|
282
515
|
extern "C" JNIEXPORT jboolean JNICALL
|
|
283
|
-
|
|
516
|
+
Java_com_devicedefense_NativeSecurityCheck_hasSuspiciousSystemProperties(JNIEnv* env, jobject /* this */) {
|
|
284
517
|
return checkSystemProperties() ? JNI_TRUE : JNI_FALSE;
|
|
285
518
|
}
|
|
286
519
|
|
|
287
520
|
extern "C" JNIEXPORT jboolean JNICALL
|
|
288
|
-
|
|
521
|
+
Java_com_devicedefense_NativeSecurityCheck_hasHookFramework(JNIEnv* env, jobject /* this */) {
|
|
289
522
|
return checkFridaInMaps() ? JNI_TRUE : JNI_FALSE;
|
|
290
523
|
}
|
|
291
524
|
|
|
292
525
|
extern "C" JNIEXPORT jboolean JNICALL
|
|
293
|
-
|
|
526
|
+
Java_com_devicedefense_NativeSecurityCheck_isDebuggerAttached(JNIEnv* env, jobject /* this */) {
|
|
294
527
|
return checkTracerPid() ? JNI_TRUE : JNI_FALSE;
|
|
295
528
|
}
|
|
296
529
|
|
|
530
|
+
// New SSL security functions
|
|
531
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
532
|
+
Java_com_devicedefense_NativeSecurityCheck_hasSSLValidationBypass(JNIEnv* env, jobject /* this */) {
|
|
533
|
+
return checkSSLValidationBypass() ? JNI_TRUE : JNI_FALSE;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
537
|
+
Java_com_devicedefense_NativeSecurityCheck_hasSSLPinningBypass(JNIEnv* env, jobject /* this */) {
|
|
538
|
+
return checkSSLPinningBypass() ? JNI_TRUE : JNI_FALSE;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
542
|
+
Java_com_devicedefense_NativeSecurityCheck_hasProxyConfiguration(JNIEnv* env, jobject /* this */) {
|
|
543
|
+
return checkProxyConfiguration() ? JNI_TRUE : JNI_FALSE;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
547
|
+
Java_com_devicedefense_NativeSecurityCheck_hasModifiedSSLLibraries(JNIEnv* env, jobject /* this */) {
|
|
548
|
+
return checkModifiedSSLLibraries() ? JNI_TRUE : JNI_FALSE;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
552
|
+
Java_com_devicedefense_NativeSecurityCheck_hasCertificateTampering(JNIEnv* env, jobject /* this */) {
|
|
553
|
+
return checkCertificateTampering() ? JNI_TRUE : JNI_FALSE;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
extern "C" JNIEXPORT jboolean JNICALL
|
|
557
|
+
Java_com_devicedefense_NativeSecurityCheck_hasSSLSecurityIssue(JNIEnv* env, jobject /* this */) {
|
|
558
|
+
return checkSSLSecurity() ? JNI_TRUE : JNI_FALSE;
|
|
559
|
+
}
|
|
560
|
+
|
|
297
561
|
extern "C" JNIEXPORT jstring JNICALL
|
|
298
|
-
|
|
562
|
+
Java_com_devicedefense_NativeSecurityCheck_getSecurityStatus(JNIEnv* env, jobject /* this */) {
|
|
299
563
|
bool isRooted = performRootDetection();
|
|
300
564
|
bool hasDangerousBins = checkDangerousBinaries();
|
|
301
565
|
bool hasSuspiciousProps = checkSystemProperties();
|
|
302
566
|
bool hasHook = checkFridaInMaps();
|
|
303
567
|
bool hasDebugger = checkTracerPid();
|
|
568
|
+
bool hasSSLIssue = checkSSLSecurity();
|
|
569
|
+
bool hasSSLBypass = checkSSLPinningBypass();
|
|
570
|
+
bool hasProxy = checkProxyConfiguration();
|
|
571
|
+
bool hasModifiedSSL = checkModifiedSSLLibraries();
|
|
572
|
+
bool hasCertTampering = checkCertificateTampering();
|
|
304
573
|
|
|
305
574
|
std::string json = "{";
|
|
306
575
|
json += "\"isRooted\":" + std::string(isRooted ? "true" : "false") + ",";
|
|
307
576
|
json += "\"hasDangerousBins\":" + std::string(hasDangerousBins ? "true" : "false") + ",";
|
|
308
577
|
json += "\"hasSuspiciousProps\":" + std::string(hasSuspiciousProps ? "true" : "false") + ",";
|
|
309
578
|
json += "\"hasHookFramework\":" + std::string(hasHook ? "true" : "false") + ",";
|
|
310
|
-
json += "\"isDebuggerAttached\":" + std::string(hasDebugger ? "true" : "false");
|
|
579
|
+
json += "\"isDebuggerAttached\":" + std::string(hasDebugger ? "true" : "false") + ",";
|
|
580
|
+
json += "\"hasSSLSecurityIssue\":" + std::string(hasSSLIssue ? "true" : "false") + ",";
|
|
581
|
+
json += "\"hasSSLValidationBypass\":" + std::string(hasSSLBypass ? "true" : "false") + ",";
|
|
582
|
+
json += "\"hasProxyConfiguration\":" + std::string(hasProxy ? "true" : "false") + ",";
|
|
583
|
+
json += "\"hasModifiedSSLLibraries\":" + std::string(hasModifiedSSL ? "true" : "false") + ",";
|
|
584
|
+
json += "\"hasCertificateTampering\":" + std::string(hasCertTampering ? "true" : "false");
|
|
311
585
|
json += "}";
|
|
312
586
|
|
|
313
587
|
return env->NewStringUTF(json.c_str());
|
|
@@ -133,6 +133,107 @@ class DeviceSecurityModule(reactContext: ReactApplicationContext) :
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// ===== SSL Security Methods =====
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if SSL validation has been bypassed
|
|
140
|
+
*/
|
|
141
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
142
|
+
fun hasSSLValidationBypass(): Boolean {
|
|
143
|
+
return try {
|
|
144
|
+
NativeSecurityCheck.hasSSLValidationBypass()
|
|
145
|
+
} catch (e: Exception) {
|
|
146
|
+
Log.e(NAME, "Error checking SSL validation bypass", e)
|
|
147
|
+
false
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if SSL pinning bypass tools are present
|
|
153
|
+
*/
|
|
154
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
155
|
+
fun hasSSLPinningBypass(): Boolean {
|
|
156
|
+
return try {
|
|
157
|
+
NativeSecurityCheck.hasSSLPinningBypass()
|
|
158
|
+
} catch (e: Exception) {
|
|
159
|
+
Log.e(NAME, "Error checking SSL pinning bypass", e)
|
|
160
|
+
false
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Check if proxy is configured (potential MITM)
|
|
166
|
+
*/
|
|
167
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
168
|
+
fun hasProxyConfiguration(): Boolean {
|
|
169
|
+
return try {
|
|
170
|
+
NativeSecurityCheck.hasProxyConfiguration()
|
|
171
|
+
} catch (e: Exception) {
|
|
172
|
+
Log.e(NAME, "Error checking proxy configuration", e)
|
|
173
|
+
false
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check if SSL libraries have been modified
|
|
179
|
+
*/
|
|
180
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
181
|
+
fun hasModifiedSSLLibraries(): Boolean {
|
|
182
|
+
return try {
|
|
183
|
+
NativeSecurityCheck.hasModifiedSSLLibraries()
|
|
184
|
+
} catch (e: Exception) {
|
|
185
|
+
Log.e(NAME, "Error checking modified SSL libraries", e)
|
|
186
|
+
false
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check if certificates have been tampered with
|
|
192
|
+
*/
|
|
193
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
194
|
+
fun hasCertificateTampering(): Boolean {
|
|
195
|
+
return try {
|
|
196
|
+
NativeSecurityCheck.hasCertificateTampering()
|
|
197
|
+
} catch (e: Exception) {
|
|
198
|
+
Log.e(NAME, "Error checking certificate tampering", e)
|
|
199
|
+
false
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Comprehensive SSL security check
|
|
205
|
+
*/
|
|
206
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
207
|
+
fun hasSSLSecurityIssue(): Boolean {
|
|
208
|
+
return try {
|
|
209
|
+
NativeSecurityCheck.hasSSLSecurityIssue()
|
|
210
|
+
} catch (e: Exception) {
|
|
211
|
+
Log.e(NAME, "Error checking SSL security", e)
|
|
212
|
+
false
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get detailed SSL security status
|
|
218
|
+
*/
|
|
219
|
+
@ReactMethod
|
|
220
|
+
fun getSSLSecurityStatus(promise: Promise) {
|
|
221
|
+
try {
|
|
222
|
+
val sslStatus = JSONObject().apply {
|
|
223
|
+
put("hasSSLValidationBypass", NativeSecurityCheck.hasSSLValidationBypass())
|
|
224
|
+
put("hasSSLPinningBypass", NativeSecurityCheck.hasSSLPinningBypass())
|
|
225
|
+
put("hasProxyConfiguration", NativeSecurityCheck.hasProxyConfiguration())
|
|
226
|
+
put("hasModifiedSSLLibraries", NativeSecurityCheck.hasModifiedSSLLibraries())
|
|
227
|
+
put("hasCertificateTampering", NativeSecurityCheck.hasCertificateTampering())
|
|
228
|
+
put("hasSSLSecurityIssue", NativeSecurityCheck.hasSSLSecurityIssue())
|
|
229
|
+
}
|
|
230
|
+
promise.resolve(sslStatus.toString())
|
|
231
|
+
} catch (e: Exception) {
|
|
232
|
+
Log.e(NAME, "Error getting SSL security status", e)
|
|
233
|
+
promise.reject("SSL_STATUS_ERROR", e.message)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
136
237
|
/**
|
|
137
238
|
* Get comprehensive security status
|
|
138
239
|
*/
|
|
@@ -146,6 +247,13 @@ class DeviceSecurityModule(reactContext: ReactApplicationContext) :
|
|
|
146
247
|
val isDebuggable = debugDetection.isDebuggable()
|
|
147
248
|
val isEmulator = emulatorDetection.isEmulator()
|
|
148
249
|
|
|
250
|
+
// SSL security checks
|
|
251
|
+
val hasSSLValidationBypass = NativeSecurityCheck.hasSSLValidationBypass()
|
|
252
|
+
val hasSSLPinningBypass = NativeSecurityCheck.hasSSLPinningBypass()
|
|
253
|
+
val hasProxyConfiguration = NativeSecurityCheck.hasProxyConfiguration()
|
|
254
|
+
val hasModifiedSSLLibraries = NativeSecurityCheck.hasModifiedSSLLibraries()
|
|
255
|
+
val hasCertificateTampering = NativeSecurityCheck.hasCertificateTampering()
|
|
256
|
+
|
|
149
257
|
val threats = mutableListOf<String>()
|
|
150
258
|
|
|
151
259
|
if (rootResult.isRooted) {
|
|
@@ -161,6 +269,11 @@ class DeviceSecurityModule(reactContext: ReactApplicationContext) :
|
|
|
161
269
|
if (hasMagisk) threats.add("magisk_detected")
|
|
162
270
|
if (isDebuggable) threats.add("debugger_detected")
|
|
163
271
|
if (isEmulator) threats.add("emulator_detected")
|
|
272
|
+
if (hasSSLValidationBypass) threats.add("ssl_validation_bypass")
|
|
273
|
+
if (hasSSLPinningBypass) threats.add("ssl_pinning_bypass")
|
|
274
|
+
if (hasProxyConfiguration) threats.add("proxy_configuration")
|
|
275
|
+
if (hasModifiedSSLLibraries) threats.add("modified_ssl_libraries")
|
|
276
|
+
if (hasCertificateTampering) threats.add("certificate_tampering")
|
|
164
277
|
|
|
165
278
|
val securityStatus = JSONObject().apply {
|
|
166
279
|
put("isSecure", threats.isEmpty())
|
|
@@ -176,6 +289,14 @@ class DeviceSecurityModule(reactContext: ReactApplicationContext) :
|
|
|
176
289
|
put("hasMagisk", hasMagisk)
|
|
177
290
|
put("isDebuggable", isDebuggable)
|
|
178
291
|
put("isEmulator", isEmulator)
|
|
292
|
+
// SSL security fields
|
|
293
|
+
put("hasSSLValidationBypass", hasSSLValidationBypass)
|
|
294
|
+
put("hasSSLPinningBypass", hasSSLPinningBypass)
|
|
295
|
+
put("hasProxyConfiguration", hasProxyConfiguration)
|
|
296
|
+
put("hasModifiedSSLLibraries", hasModifiedSSLLibraries)
|
|
297
|
+
put("hasCertificateTampering", hasCertificateTampering)
|
|
298
|
+
put("hasSSLSecurityIssue", hasSSLValidationBypass || hasSSLPinningBypass ||
|
|
299
|
+
hasProxyConfiguration || hasModifiedSSLLibraries || hasCertificateTampering)
|
|
179
300
|
put("details", JSONObject().apply {
|
|
180
301
|
put("emulatorType", emulatorDetection.getEmulatorType())
|
|
181
302
|
val nativeLibLoaded = try {
|
|
@@ -206,7 +327,8 @@ class DeviceSecurityModule(reactContext: ReactApplicationContext) :
|
|
|
206
327
|
!hookDetection.hasXposed() &&
|
|
207
328
|
!hookDetection.hasMagisk() &&
|
|
208
329
|
!debugDetection.isDebuggable() &&
|
|
209
|
-
!emulatorDetection.isEmulator()
|
|
330
|
+
!emulatorDetection.isEmulator() &&
|
|
331
|
+
!NativeSecurityCheck.hasSSLSecurityIssue()
|
|
210
332
|
|
|
211
333
|
promise.resolve(isSecure)
|
|
212
334
|
} catch (e: Exception) {
|
|
@@ -233,13 +355,15 @@ class DeviceSecurityModule(reactContext: ReactApplicationContext) :
|
|
|
233
355
|
val hasMagisk = hookDetection.hasMagisk()
|
|
234
356
|
val isDebuggable = debugDetection.isDebuggable()
|
|
235
357
|
val isEmulator = emulatorDetection.isEmulator()
|
|
358
|
+
val hasSSLIssue = NativeSecurityCheck.hasSSLSecurityIssue()
|
|
236
359
|
|
|
237
360
|
val hasThreat = rootResult.isRooted ||
|
|
238
361
|
hasFrida ||
|
|
239
362
|
hasXposed ||
|
|
240
363
|
hasMagisk ||
|
|
241
364
|
isDebuggable ||
|
|
242
|
-
isEmulator
|
|
365
|
+
isEmulator ||
|
|
366
|
+
hasSSLIssue
|
|
243
367
|
|
|
244
368
|
if (hasThreat) {
|
|
245
369
|
Log.w(NAME, "Security threat detected, blocking app")
|