Sensitive value copied to the system clipboard
ID |
dart.privacy.clipboard_sensitive_data |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
medium |
Remediation Effort |
low |
Resource |
Privacy |
Language |
Dart |
Description
A sensitive value (token, password, OTP, recovery phrase / mnemonic / seed phrase, PIN, API key, JWT) is written to the system clipboard via Clipboard.setData(ClipboardData(text: …)). The clipboard is shared across all apps on the device and persists indefinitely.
Rationale
The system clipboard is global and unprotected:
-
Any other app on the device can read it. Adware, keyboard apps, screen-time / parental-control utilities, accessibility services — all routinely read clipboard contents on every paste event.
-
iOS Universal Clipboard / Handoff syncs the clipboard across the user’s Apple devices — your password is now on their laptop, iPad, and Apple Watch.
-
Android 12+ shows a toast on every clipboard read, confirming the leak but not preventing it.
-
The clipboard persists indefinitely unless explicitly cleared. A token copied at 9am is still readable at 9pm by every app launched in between.
Copying a long-lived credential to the clipboard is therefore a trust-handoff to every app the user happens to run. For short-lived material (an OTP that the user must paste into a banking page within 30 seconds), copying is sometimes the only reasonable UX — but clear the clipboard after a short timeout.
The following code illustrates a vulnerable pattern detected by this rule:
class CopySecrets {
Future<void> copyToken(String token) async {
// VULNERABLE: Sensitive value copied to the system clipboard
await Clipboard.setData(ClipboardData(text: token));
}
Future<void> copyApiKey(String apiKey) async {
await Clipboard.setData(ClipboardData(text: apiKey));
}
Future<void> copyRecoveryPhrase(String recoveryPhrase) async {
await Clipboard.setData(ClipboardData(text: recoveryPhrase));
}
}
Remediation
Avoid copying credentials. Prefer:
-
In-app reveal: show the value with a tap-and-hold gesture that does not put it on the clipboard.
-
Share Sheet limited to a chosen target: lets the user paste into one specific app without the clipboard middleman.
-
For short-lived OTPs / codes: copy, then clear after 30 s with
Clipboard.setData(const ClipboardData(text: '')).
import 'package:flutter/services.dart';
// Vulnerable
await Clipboard.setData(ClipboardData(text: token));
await Clipboard.setData(ClipboardData(text: 'Your recovery phrase: $mnemonic'));
// Fixed: short-lived OTP only, with auto-clear
await Clipboard.setData(ClipboardData(text: otp));
Timer(const Duration(seconds: 30), () async {
await Clipboard.setData(const ClipboardData(text: ''));
});
References
-
OWASP Top 10 2025 - A01 : Broken Access Control.
-
Flutter Clipboard — API docs — Reference for the clipboard API
-
Talsec — OWASP Mobile Top 10 for Flutter M6 — Inadequate privacy controls — clipboard leakage scenarios