To protect Android and iOS apps from keylogging malware, cut the number of secrets people type, harden every sensitive field, and watch for the components that can read input. On Android, the main threat is not a classic keylogger but malware that abuses accessibility services to read screens and keystrokes, so check enabled services and mark sensitive views. Both platforms let third-party keyboards see what is typed in ordinary fields. iOS replaces them with the system keyboard for secure text entry, and Android can ask keyboards not to learn. This guide covers Android and iOS separately.
Where typed secrets leak
Map one journey, such as sign-in or a payment confirmation. Note where text enters the app, which component receives it and where it goes next. On mobile, typed input can leak through four routes:
| Route | Android | iOS |
|---|---|---|
| Accessibility abuse | A malicious accessibility service reads view text and input events | No equivalent API for third-party apps |
| Third-party keyboard | An input method sees every key in fields it serves | A keyboard extension sees ordinary fields; with Full Access it can use the network |
| Hooked app process | Injected code reads fields or memory, usually on rooted devices | Injected code reads fields or memory, usually on jailbroken devices |
| The app itself | Logs, analytics, crash reports and clipboard | Logs, analytics, crash reports and clipboard |
Close the last route first. No raw input, clipboard content or token should reach logs or analytics. It is the most common leak and the easiest to fix.
Android: accessibility malware
Banking trojans on Android commonly ask the user to enable an accessibility service. Once enabled, the service can read the text of on-screen views, observe input events and perform taps. That gives the attacker keystrokes without a keyboard.
Check enabled services
AccessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) returns the services that are currently enabled. For each one you can read its package, its declared capabilities and whether it declares isAccessibilityTool, the flag for services built to assist people with disabilities. Compare the list with what you expect: screen readers and switch access are legitimate; a newly installed app with no assistive purpose is a strong risk signal. Re-check when the app returns to the foreground, because malware is often enabled mid-session.
Mark sensitive views
On Android 14 (API level 34) and later, call View.setAccessibilityDataSensitive(View.ACCESSIBILITY_DATA_SENSITIVE_YES), or set android:accessibilityDataSensitive in the layout, on password fields, PIN pads and confirmation screens. Only services that declare isAccessibilityTool can then read or act on those views. Genuine screen readers keep working; a malicious service that does not declare the flag is cut out of that view.
Know the platform barrier
Android 13 added restricted settings. For many apps installed outside an app store, such as an APK downloaded through a browser, the system blocks accessibility and notification listener access until the user explicitly allows restricted settings in the app’s info screen. Attackers now coach victims through that step, so treat the barrier as friction, not protection.
For the full control, see Android accessibility malware. Overlays that trick users into typing into a fake screen are covered on Android overlay attack detection.
Android: input hardening
- Password fields. Use
inputType="textPassword"(ornumberPassword) on the actual field. It conceals characters and keyboards treat it as sensitive, but the keyboard still receives every key. - No learning. Add
EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNINGtoimeOptionson sensitive fields. It asks the keyboard not to store typing history or update its language model. It is a request; a malicious keyboard can ignore it. - No suggestions. Use
textNoSuggestionson fields such as account numbers or one-time codes so values do not appear in suggestion strips. - In-app PIN pads. For PINs and transaction codes, a custom keypad drawn by the app keeps digits away from third-party keyboards. Mark it sensitive for accessibility, give each key a content description for screen readers, and set
filterTouchesWhenObscuredso taps are refused when another window covers it. - Framework fields. In Flutter and React Native, confirm in the release build that the rendered native field carries the intended input type and flags.
iOS: keyboards and secure entry
iOS has no system-wide accessibility API for third-party apps, so the focus is the keyboard and the app process.
- Secure text entry. Set
isSecureTextEntryon password and PIN fields. iOS replaces any custom keyboard with the system keyboard while that field is active. - Reject keyboard extensions. For high-risk apps, implement
application(_:shouldAllowExtensionPointIdentifier:)in the app delegate and returnfalsefor.keyboard. All third-party keyboards are then replaced by the system keyboard in your app. Test the effect on people who rely on a language or assistive keyboard before you ship it. - Full Access. A keyboard extension needs the user to grant Full Access before it can use the network. A keyboard with Full Access in a non-secure field can send what it sees.
- Autofill. Set
textContentTypeto.password,.newPasswordor.oneTimeCodeso password managers and AutoFill work, which reduces typing. - Ordinary fields. For account numbers and similar values in non-secure fields, set
autocorrectionTypeandspellCheckingTypeto.no.
Separate guidance for each platform is on Android app security and iOS app security.
Detect, then respond
Otukan detects accessibility services and overlays that can read or drive your app’s screens on Android, and detects hooking and tampering in the app process on Android and on iOS. The evidence goes to your backend with the request it concerns, and the app applies the response you set for each flow: observe, warn, restrict or terminate. For the product view, see keylogging detection.
Base the response on the action at stake:
- Weak evidence (an unfamiliar keyboard in an ordinary field): record it, change nothing for the user.
- Medium evidence (a non-assistive accessibility service enabled during a payment): step up with a factor that is not typed, such as a biometric or a passkey.
- Strong evidence (hooking in the app process during sign-in): pause the sensitive action and offer a supported route.
Never ask the person to re-type a secret while the risky condition is still present. Explain the action without accusing them.
Test the journey
- Use a dedicated device, a test account, a non-production backend and synthetic credentials.
- Run the journey clean, with the system keyboard, a screen reader and a password manager. Record the outcome.
- Enable an authorised test accessibility service that reads view text. Confirm sensitive views are hidden from it from Android 14, and that the app reports it.
- On iOS, install a test keyboard extension with Full Access and confirm it is replaced in secure fields and, if you reject extensions, in every field.
- Remove the test conditions and repeat. No stale restriction should remain.
Release checklist
- Logs, analytics and crash reports contain no typed input or clipboard content.
- Android sensitive views are marked accessibility-data-sensitive.
- Sensitive Android fields use the right input type and no-learning flag.
- iOS secure fields use secure text entry; the keyboard extension policy is a recorded decision.
- Screen readers, switch access and password managers complete the journey.
- Each platform’s test record names the build, OS version and tester.
Further reading
- Create an accessibility service — Android Developers
- View.setAccessibilityDataSensitive — Android Developers
- Specify the input method type — Android Developers
- Learn about restricted settings — Android Help
- Creating a custom keyboard — Apple Developer Documentation
- isSecureTextEntry — Apple Developer Documentation