Skip to content
Back to skills

Kmp Security

ASecurity

Mobile app security for Kotlin Multiplatform beyond Android-only R8 obfuscation — certificate/SSL pinning (expect/actual, no cross-platform Ktor support exists natively), root/jailbreak/tamper detection via freeRASP's real KMP variant, encrypted local storage via KSafe, iOS/ Kotlin-Native release-binary symbol stripping, and an OWASP Mobile Top 10 2024 coverage map across this collection. Does NOT cover Android-specific R8/ProGuard obfuscation — that's kmp-proguard-r8's own scope, cross- refe...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 2, 2026
ai-agentsrustgoswiftkotlintestingdebugginggitapibackendsecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 2, 2026

npx -y skills add ronjunevaldoz/kmp-agent-skills --skill kmp-security --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Kmp Security?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Kmp Security
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ronjunevaldoz-kmp-security/badge)](https://www.skillsdirectory.com/skills/ronjunevaldoz-kmp-security)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: kmp-security
description: >
  Mobile app security for Kotlin Multiplatform beyond Android-only R8
  obfuscation — certificate/SSL pinning (expect/actual, no cross-platform
  Ktor support exists natively), root/jailbreak/tamper detection via
  freeRASP's real KMP variant, encrypted local storage via KSafe, iOS/
  Kotlin-Native release-binary symbol stripping, and an OWASP Mobile Top 10
  2024 coverage map across this collection. Does NOT cover Android-specific
  R8/ProGuard obfuscation — that's kmp-proguard-r8's own scope, cross-
  referenced here rather than duplicated. Does NOT cover secrets-in-source
  scanning (gitleaks pre-commit) — that's kmp-setup-hooks Option F.
license: Apache-2.0
metadata:
  author: kmp-agent-skills
  last-updated: '2026-08-24'
  keywords:
    - security
    - certificate pinning
    - SSL pinning
    - root detection
    - jailbreak detection
    - tamper detection
    - freeRASP
    - RASP
    - secure storage
    - encrypted storage
    - KSafe
    - OWASP Mobile Top 10
    - binary stripping
    - symbol stripping
    - reverse engineering
---

## When to Use This Skill

Use when you need to:
- Pin a server's certificate/public key so the app rejects a MITM proxy even
  with a trusted root CA installed on the device
- Detect a rooted/jailbroken device, an active debugger, or Frida/Xposed
  hooking at runtime
- Store a token/credential encrypted at rest, not in plain `SharedPreferences`/`NSUserDefaults`
- Strip debug symbols from a release iOS/Kotlin-Native binary
- Check this collection's coverage against the real OWASP Mobile Top 10

Do NOT use this skill for Android-specific obfuscation/minification — load
`kmp-proguard-r8` instead, this skill cross-references it rather than
duplicating it.

**Trigger keywords:** certificate pinning, SSL pinning, public key pinning,
root detection, jailbreak detection, tamper detection, freeRASP, RASP,
runtime application self-protection, secure storage, encrypted storage,
KSafe, OWASP Mobile Top 10, binary stripping, symbol stripping, strip debug
symbols, reverse engineering, Frida detection, MITM.

**Freshness rule:** freeRASP's KMP variant and KSafe are both actively
developed, real third-party libraries, not part of JetBrains' own KMP
release train — recheck their current versions before pinning; OWASP Mobile
Top 10 was last released in 2024 (still current as of this writing, verified
directly against owasp.org, not assumed).

---

## Recommendation First

**freeRASP (Talsec)** for root/jailbreak/tamper detection — real, dedicated
KMP variant (`com.aheaditec.talsec.security:freeRASP_KMP`), one shared
codebase across Android+iOS instead of hand-rolling two `expect`/`actual`
detectors. **KSafe** for encrypted local storage — encryption on by default,
a single code path with no `expect`/`actual` needed, and built-in
root/jailbreak WARN/BLOCK policy on top. **Certificate pinning has no native
cross-platform Ktor support** — verified, not assumed: it requires
`expect`/`actual`, OkHttp's `CertificatePinner` on the Android engine and a
`NSURLSessionDelegate` challenge handler on the Darwin (iOS) engine.

Why: detection alone is never sufficient on its own — every client-side
check is bypassable by a sufficiently motivated attacker. The real posture
is layered: client-side detection (freeRASP) raises the cost of tampering,
pinning + encrypted storage protect data in transit and at rest, and
anything security-critical (entitlement checks, purchase validation) still
gets verified server-side — a rooted device can manipulate any purely local
verification logic.

---

## Certificate / SSL Pinning

Ktor's client engines differ per platform, so pinning is genuinely
`expect`/`actual`, not a shared Ktor plugin:

```kotlin
// commonMain
expect fun createPinnedHttpClient(pins: List<String>): HttpClient
```

```kotlin
// androidMain — OkHttp engine, native CertificatePinner support
actual fun createPinnedHttpClient(pins: List<String>): HttpClient {
    val certificatePinner = CertificatePinner.Builder()
        .add("api.example.com", *pins.toTypedArray())   // "sha256/AAAA...=" format
        .build()
    return HttpClient(OkHttp) {
        engine { preconfigured = OkHttpClient.Builder().certificatePinner(certificatePinner).build() }
    }
}
```

```kotlin
// iosMain — Darwin engine, NSURLSessionDelegate challenge handling
actual fun createPinnedHttpClient(pins: List<String>): HttpClient {
    return HttpClient(Darwin) {
        engine {
            handleChallenge { _, _, challenge, completionHandler ->
                // Compare the server's public key hash against `pins` here;
                // cancel the challenge (reject) on mismatch instead of trusting
                // the system's CA store — see kmp-expect-actual for the platform
                // dispatch pattern this expect/actual pair follows.
            }
        }
    }
}
```

**Test with a broken pin first** — configure an intentionally wrong hash,
confirm the connection is rejected, then switch to the real pin. Confirms the
pinning logic actually runs before trusting it in production. Do this on a
trusted network, not through a MITM proxy tool (Charles/Fiddler) — those are
exactly what pinning should reject.

---

## Root / Jailbreak / Tamper Detection — freeRASP

```kotlin
// build.gradle.kts, commonMain
dependencies {
    implementation("com.aheaditec.talsec.security:freeRASP_KMP:1.1.0")
}
```

```kotlin
// commonMain — SecurityManager.kt
object SecurityManager {
    private val config = freeraspConfig(
        watcherMail = "security@example.com",
        androidConfig = AndroidConfig(
            packageName = "com.example.app",
            certificateHashes = listOf("mVr/qQLO8DKTwqlL+B1qigl9NoBnbiUs8b4c2Ewcz0k="),
        ),
        iosConfig = IOSConfig(
            bundleIds = listOf("com.example.app"),
            teamId = "YOUR_TEAM_ID",
        ),
        isProd = true,
        killOnBypass = true,
    )

    suspend fun start(scope: CoroutineScope) {
        FreeraspKMP.threatEvents.onEach { event ->
            when (event) {
                is FreeRaspEvent.PrivilegedAccess -> handleRootOrJailbreak()
                is FreeRaspEvent.AppIntegrity -> handleRepackaging()
                is FreeRaspEvent.Debug -> handleDebuggerAttached()
                else -> Unit
            }
        }.flowOn(Dispatchers.IO).launchIn(scope)
        FreeraspKMP.start(config)
    }
}
```

Detects rooting/jailbreak (Magisk, Dopamine), Frida/Xposed hooking,
repackaging/tampering, and emulators — call `SecurityManager.start()` once
from app entry (`LaunchedEffect` in the root composable, or platform
`onCreate`/`AppDelegate` equivalent). iOS needs `TalsecRuntime.xcframework`/
`TalsecBridge.xcframework` linked in Xcode with "Embed & Sign"; Android needs
the manifest permissions its integration guide lists (`DETECT_SCREEN_CAPTURE`
among them) — both are one-time setup steps, not runtime code.

**Layer detection methods, don't rely on one.** File-system checks alone are
easily hooked with Frida/Substrate at the Kotlin/Swift layer — freeRASP's own
value is combining multiple signals (file system, API behavior, native-level
checks) instead of one easily-bypassed check.

---

## Secure Local Storage — KSafe

```kotlin
// build.gradle.kts
dependencies {
    implementation("eu.anifantakis:ksafe:3.0.0")
    implementation("eu.anifantakis:ksafe-compose:3.0.0")   // optional — Compose state integration
}
```

```kotlin
// Encrypted by default, property-delegate pattern
var authInfo by ksafe(AuthInfo())   // AuthInfo must be @Serializable

// Reads hit a hot in-memory cache after first load; writes happen async in the background

val ksafe = KSafe(
    context = context,
    securityPolicy = KSafeSecurityPolicy(
        rootedDevice = SecurityAction.WARN,       // IGNORE | WARN | BLOCK
        debuggerAttached = SecurityAction.BLOCK,
        onViolation = { violation -> analytics.log("security: ${violation.name}") },
    ),
)
```

One code path, no `expect`/`actual` needed for the storage layer itself —
KSafe owns the platform dispatch (Android Keystore-backed encryption,
iOS Keychain) internally. Prefer this over hand-rolling
`EncryptedSharedPreferences`/raw `Keychain` calls per platform unless the
project has a reason KSafe doesn't cover.

---

## Release Binary Protection

**Android**: R8/ProGuard obfuscation — load `kmp-proguard-r8`, this skill
doesn't duplicate it.

**iOS/Kotlin-Native**: strip debug symbols from the release `.framework`.
Verified Xcode Archive build settings, not the dev-build defaults:

```
Deployment Postprocessing = Yes
Strip Linked Product = Yes
Additional Strip Flags = -rSTx
```

`-T` strips Swift symbols specifically; the combination applies to any
bundled `.framework`, including the Kotlin/Native-produced one — debugging
information and descriptive function names in an unstripped binary make
reverse engineering meaningfully easier. Most crash-reporting tools (Sentry,
Crashlytics) support uploading symbols separately for stack-trace
symbolication, so stripping the shipped binary doesn't cost you crash
diagnosis — same tradeoff `kmp-proguard-r8` already documents for Android's
`mapping.txt`.

---

## Server-Side Verification — the Principle Underneath All of This

A rooted/jailbroken device can manipulate any client-side check, including
freeRASP's own signals if the attacker is determined enough. The real
security boundary for anything valuable (entitlements, purchase validation,
sensitive data access) is server-side verification — client-side detection
raises the cost of an attack, it doesn't replace backend validation. See
`kmp-in-app-purchases`'s server-side receipt validation for the concrete
example this collection already applies the same principle to.

---

## OWASP Mobile Top 10 (2024) — Coverage Map

Verified directly against owasp.org, not assumed:

| Category | Covered by |
|---|---|
| M1: Improper Credential Usage | `kmp-flavor-environment` (secrets never in source), this skill's KSafe section |
| M2: Inadequate Supply Chain Security | `kmp-setup-hooks` (gitleaks), `kmp-ci-github-actions` |
| M3: Insecure Authentication/Authorization | `kmp-ktor-auth-service`, `kmp-biometric-auth` |
| M4: Insufficient Input/Output Validation | `kmp-form-validation` |
| M5: Insecure Communication | This skill's Certificate/SSL Pinning section |
| M6: Inadequate Privacy Controls | `kmp-legal-docs` |
| M7: Insufficient Binary Protections | This skill's Release Binary Protection + Root/Jailbreak sections, `kmp-proguard-r8` |
| M8: Security Misconfiguration | `kmp-ci-github-actions`, `kmp-code-quality` (Detekt gates) |
| M9: Insecure Data Storage | This skill's Secure Local Storage section |
| M10: Insufficient Cryptography | This skill's KSafe section (delegates crypto to the library, doesn't hand-roll it) |

Every category has a real home somewhere in this collection — this table
exists so a reviewer can find it without re-deriving the mapping.

---

## Common Anti-Patterns

- **Trusting client-side root/jailbreak detection as the actual security
  boundary** — it's a cost-raising signal, not a guarantee; verify anything
  valuable server-side too.
- **Pinning against a leaf certificate instead of a public key or
  intermediate CA** — a leaf cert rotates on renewal and silently breaks the
  app; pin the public key (SPKI hash) or an intermediate that survives
  rotation.
- **No pin-rotation plan** — shipping a pinned app with no way to update the
  pins (a remote config fallback, or a hard app-store update requirement)
  means a legitimate certificate rotation locks users out.
- **Hand-rolling `EncryptedSharedPreferences`/raw `Keychain` calls per
  platform** when KSafe already covers the common case with less code and
  matching encryption guarantees on both.
- **Assuming R8 obfuscation covers iOS too** — it doesn't; the release
  binary needs its own, separate stripping step.
- **A single un-layered detection check** (just a `su` binary file check, for
  example) — easily defeated; combine multiple signals, or use a library that
  already does (freeRASP).

---

## Testing

```kotlin
class SecurityPolicyTest {
    @Test
    fun `WARN action logs but does not throw`() = runTest {
        var logged: String? = null
        val policy = KSafeSecurityPolicy(
            rootedDevice = SecurityAction.WARN,
            onViolation = { logged = it.name },
        )
        // Fake a rooted-device signal through the policy's own violation hook —
        // don't test against a real rooted device in CI.
        policy.onViolation(SecurityViolation.ROOTED_DEVICE)
        assertEquals("ROOTED_DEVICE", logged)
    }
}
```

Certificate pinning and freeRASP integration are best verified manually per
the intentional-broken-pin technique above — both depend on real platform
TLS/OS-level behavior that a JVM unit test can't meaningfully fake.

---

## Related Skills

- `kmp-proguard-r8` — Android obfuscation/minification; this skill's iOS/Native
  counterpart, cross-referenced not duplicated
- `kmp-flavor-environment` — build-time secrets injection, the M1 credential-usage half
- `kmp-setup-hooks` — gitleaks pre-commit secrets scanning (Option F)
- `kmp-network-layer` — the Ktor client this skill's pinning wraps
- `kmp-in-app-purchases` — the server-side receipt validation example this skill's
  "server-side verification" principle points to
- `kmp-biometric-auth` — device keystore-backed credential unlock, adjacent to KSafe

---

## Output Style

When asked about mobile security, respond in this order:
1. which of the five areas actually applies (pinning / RASP / storage /
   binary stripping / OWASP mapping) — don't dump all five for a narrow ask
2. the real library/API for that area, with its actual coordinates
3. code snippet
4. the layered/server-side-verification caveat if the ask implies
   client-side detection is the whole solution

---

## Changelog

| Date | Change |
|---|---|
| 2026-08-24 | Initial release. User asked whether code security and obfuscation were covered — confirmed obfuscation existed (`kmp-proguard-r8`, Android-only) but certificate pinning, root/jailbreak/tamper detection, secure storage as an owned topic, and iOS/Native binary stripping were all real, zero-coverage gaps. Verified every library/API before writing: Ktor has no native cross-platform pinning (real, `expect`/`actual` required); freeRASP ships a real dedicated KMP variant with real Gradle coordinates and config shape; KSafe is a real KMP encrypted-storage library with real Maven coordinates; OWASP Mobile Top 10's current version is the real 2024 release (M1-M10 verified against owasp.org); Xcode's real Archive-time strip settings for a Kotlin/Native `.framework`. Added an OWASP Mobile Top 10 coverage map cross-referencing where each category is actually handled across this collection. |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.