Secure Text Entry — 是 Android 中的一种文本输入模式,在此模式下输入的字符以隐藏形式(点或星号)显示,而不是实际字符。此模式用于密码、PIN码、CVV码和其他机密信息的输入字段。Secure Text Entry 不仅可以防止数据被视觉拦截,还可以防止通过屏幕截图、屏幕录制和自动更正功能泄露数据。根据 Android Security Best Practices (2026),隐藏输入是处理用户身份验证数据的应用程序的强制要求。
要点
Secure Text Entry(受保护的文本输入)— 是一种 Android 功能,它将每个输入字符的显示替换为掩码符号,通常是点 (•) 或星号 (*)。这可以防止从肩后窥视密码(shoulder surfing)并将输入的数据隐藏起来不让外人看到。该功能也称为密码掩码,是移动应用程序中的标准做法。
与 iOS 中 Secure Text Entry 是 UITextField 的单独设置不同,在 Android 中,此功能通过带有 PASSWORD 变体的 inputType 属性激活。当 EditText 收到 inputType="textPassword" 时,它会自动应用 PasswordTransformationMethod — 负责替换字符的类。每输入一个新字符,EditText 就会立即通过 TransformationMethod 接口机制将其替换为掩码符号。
根据 Android Source Code (AOSP, 2026),PasswordTransformationMethod 的实现使用 TextPaint 类来绘制掩码符号。掩码符号的大小与字段的字体大小一致,从而防止通过单词长度泄露信息 — 所有字符在宽度上看起来完全相同。这对于防止基于输入数据长度分析的攻击非常重要。
<!-- Basic Secure Text Entry example -->
@+id/etPassword
android:layout_width="match_parent"
android:hint="Enter password"
android:inputType="textPassword"
android:maxLength="64"
android:importantForAutofill="yes" />
Android 中的 Secure Text Entry 机制基于 android.text.method 包中定义的 TransformationMethod 接口。PasswordTransformationMethod — 是一个内置实现,它重写 getTransformation() 方法,返回用于显示的替换字符序列。同时,Editable 中的原始文本保持不变 — 仅改变其视觉表示。
当用户输入一个字符时,EditText 调用 dispatchEvent 并将字符传递给 InputConnection,后者将其添加到 Editable。然后 EditText 调用当前 TransformationMethod 的 getTransformation() 方法,该方法接收原始 CharSequence 并返回带有掩码符号的 SpannableString。这个 SpannableString 将代替原始文本显示在屏幕上。关键特性 — 转换本身不会改变 Editable 的内容,因此在调用 getText().toString() 时返回的是实际输入的文本。
根据 Android Developers Reference (2026),PasswordTransformationMethod 为每个字符添加一个 MetricAffectingSpan,它将字形替换为点。这些 span 不会影响文本长度,因此即使在隐藏显示的情况下,光标和输入位置也能正确保持。还有 VisiblePasswordTransformationMethod,它会短暂显示最后输入的字符(类似于 iOS)。
// PasswordTransformationMethod - 内置掩码
editText.transformationMethod = PasswordTransformationMethod.getInstance()
// VisiblePasswordTransformationMethod - 显示最后一个字符
editText.transformationMethod = VisiblePasswordTransformationMethod.getInstance()
切换可见性(password visibility toggle)— 是一种允许用户在提交表单前暂时显示输入的密码以进行检查的功能。在 Android 中,此功能通过带有 app:endIconMode="password_toggle" 属性的 TextInputLayout 实现。点击眼睛图标时,字段在 textPassword 和 textVisiblePassword 之间切换。
TextInputLayout 中的 password_toggle 实现会自动更改内部 EditText 的 inputType 并动画化图标(睁眼/闭眼)。需要记住的是,在此切换过程中,光标位置可能会重置,因此 TextInputLayout 会在模式更改后保存和恢复位置。此外,切换到可见密码时,自动更正和建议不会开启 — 使用 TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,而不是普通的 text。
根据 Material Design Guidelines (2025),显示密码的图标应始终可见,而不仅仅是在字段获得焦点时。用户应能够随时检查密码。图标不应在特定时间后自动隐藏密码 — 仅在用户操作时。
<!-- TextInputLayout with password toggle -->
@+id/tilPassword
android:layout_width="match_parent"
app:endIconMode="password_toggle"
app:passwordToggleTint="@color/primary">
@+id/etPassword
android:inputType="textPassword"
android:hint="Password" />
</com.google.android.material.textfield.TextInputLayout>
在 Kotlin 中程序化设置 Secure Text Entry 通过设置 inputType 或通过 setTransformationMethod() 完成。这两种方法都会产生相同的结果,但在细节上有所不同。设置 inputType 还会配置自动更正和建议,而 setTransformationMethod 仅影响字符的显示。
// 程序化切换密码可见性
fun togglePasswordVisibility(til: TextInputLayout) {
val editText = til.editText ?: return
val currentType = editText.inputType
val isPassword = (currentType and
InputType.TYPE_TEXT_VARIATION_PASSWORD) != 0
if (isPassword) {
editText.inputType = InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
} else {
editText.inputType = InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_PASSWORD
}
// 恢复光标位置
val cursorPos = editText.selectionStart
editText.setSelection(maxOf(0, cursorPos))
}
// 自定义掩码 — 仅显示最后 4 个字符
class PartialPasswordTransformation : TransformationMethod {
override fun getTransformation(source: CharSequence?,
view: View?): CharSequence {
val text = source?.toString() ?: ""
if (text.length <= 4) return text
val dots = "\u2022".repeat(text.length - 4)
return dots + text.substring(text.length - 4)
}
override fun onFocusChanged(view: View?,
sourceText: CharSequence?,
focused: Boolean,
direction: Int,
previouslyFocusedRect: android.graphics.Rect?) {}
}
Secure Text Entry 在多个层面提供保护。视觉掩码可防止从肩后窥视密码(shoulder surfing)——在公共场合最流行的密码窃取方法。禁用自动更正可防止密码存储在用户词典中,从而排除通过预测性输入泄露的可能性。防止屏幕截图(如果启用了 FLAG_SECURE)可阻止其他应用程序捕获字段内容。
然而,Secure Text Entry 并非完全保护。系统级别的输入拦截攻击(键盘记录)可以从 InputConnection 获取实际字符。具有无障碍服务的恶意应用程序可以读取 EditText 的内容,即使字符在屏幕上被隐藏。此外,在向服务器传输数据时的 SSL 嗅探可能会泄露密码,如果不使用带有正确证书验证的 HTTPS。
根据 OWASP Mobile Security Testing Guide (2025),为了在 Android 中最大限度地保护密码,建议:对关键字段使用 inputType="textPassword" + 禁用 autofill;不要在内存中保存密码超过必要时间;在应用程序最小化时清除密码字段(onPause/onStop);即使在短暂显示密码时也要使用掩码。
| 保护级别 | Secure Text Entry | 额外措施 |
|---|---|---|
| 视觉拦截 | 字符掩码 | Biometric auth,避免 shoulder surfing |
| 屏幕截图 | 部分(取决于实现) | Window 上的 FLAG_SECURE |
| 键盘记录 | 不保护 | 使用具有安全输入的 IME |
| 自动更正/词典 | 已禁用 | 默认对 password 禁用 |
| 网络拦截 | 不保护 | 带证书固定的 HTTPS |
Android 开发人员在使用隐藏输入时应遵循几个 关键实践。首先,始终对密码字段使用带有 endIconMode="password_toggle" 的 TextInputLayout — 这是 Material Design 的现代标准。其次,不要禁用显示密码的能力 — 用户应该能够检查他们输入了什么,尤其是在带有小键盘的移动设备上。
Secure Text Entry 还需要正确配置自动填充。对于密码字段,务必指定 autofillHints="password" 或 autofillHints="newPassword",以便密码管理器能够正确填充和保存数据。对于新密码确认字段,使用 autofillHints="newPassword",以免系统建议用当前保存的密码填充它。
// 带有自动填充和清除功能的完整密码字段
class SecurePasswordField {
private fun setupPasswordField(til: TextInputLayout) {
val editText = til.editText ?: return
editText.inputType = InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_PASSWORD
editText.setAutofillHints(View.AUTOFILL_HINT_PASSWORD>)
editText.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
}
fun clearOnPause(editText: EditText) {
editText.text?.clear()
}
}
常见问题
textPassword 隐藏所有输入字符并禁用自动更正。textVisiblePassword 也禁用自动更正,但以开放形式显示字符。textVisiblePassword 用于切换密码可见性时 — 这不是普通的 text,因为自动更正和建议出于安全原因保持禁用状态。
可以,创建一个实现 TransformationMethod 接口的自定义类。重写 getTransformation() 方法,返回带有所需替换字符的 CharSequence。通过 editText.transformationMethod = CustomPasswordTransformation() 设置它。记住,该符号应该是等宽字体以确保安全。
在具有小屏幕和触摸键盘的移动设备上,输入密码时出错的可能性明显高于台式机。检查输入的密码的能力可以减少输入错误的数量并减少密码恢复请求的数量,从而改善用户体验并减轻支持负担。
在 Activity 或 Dialog 窗口上设置 FLAG_SECURE 标志:window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)。这将阻止整个窗口的屏幕截图和屏幕录制。但请注意,这也将禁用普通内容的屏幕截图,这可能是不希望的。
从 Android 8.0(API 26)开始,Autofill Framework 通过 inputType 自动检测密码字段。为了正常工作,设置 importantForAutofill="yes" 并指定 autofillHints("password"、"newPassword"、"emailAddress")。Gboard 和密码管理器使用这些属性来建议保存的密码。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。