Secure Text Entry — 什么是 Android 中的输入保护与密码

作者: IT Sectr 发布日期: 2026-07-07 阅读时间: 9 分钟

Secure Text Entry — 是 Android 中的一种文本输入模式,在此模式下输入的字符以隐藏形式(点或星号)显示,而不是实际字符。此模式用于密码、PIN码、CVV码和其他机密信息的输入字段。Secure Text Entry 不仅可以防止数据被视觉拦截,还可以防止通过屏幕截图、屏幕录制和自动更正功能泄露数据。根据 Android Security Best Practices (2026),隐藏输入是处理用户身份验证数据的应用程序的强制要求。

要点

  • Secure Text Entry — 隐藏输入模式,在输入密码时显示点而不是字符。
  • 激活 — 通过 XML 中的 inputType="textPassword" 或代码中的 TYPE_TEXT_VARIATION_PASSWORD。
  • 保护 — 防止视觉拦截、屏幕截图以及通过自动更正/词典泄露。
  • TransformationMethod — Android 机制,将字符显示替换为隐藏形式。
  • 可见性切换 — 通过 TextInputLayout 中的 endIconMode="password_toggle" 显示密码。

Android 中的 Secure Text Entry 是什么

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 类来绘制掩码符号。掩码符号的大小与字段的字体大小一致,从而防止通过单词长度泄露信息 — 所有字符在宽度上看起来完全相同。这对于防止基于输入数据长度分析的攻击非常重要。

xml
<!-- 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" />

Secure Text Entry 如何工作

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)。

kotlin
// 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),显示密码的图标应始终可见,而不仅仅是在字段获得焦点时。用户应能够随时检查密码。图标不应在特定时间后自动隐藏密码 — 仅在用户操作时。

xml
<!-- 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>

Secure Text Entry 的程序化配置

在 Kotlin 中程序化设置 Secure Text Entry 通过设置 inputType 或通过 setTransformationMethod() 完成。这两种方法都会产生相同的结果,但在细节上有所不同。设置 inputType 还会配置自动更正和建议,而 setTransformationMethod 仅影响字符的显示。

通过 TextInputLayout 切换密码的示例

kotlin
// 程序化切换密码可见性
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))
}

自定义 TransformationMethod

kotlin
// 自定义掩码 — 仅显示最后 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

Secure Text Entry 的最佳实践

Android 开发人员在使用隐藏输入时应遵循几个 关键实践。首先,始终对密码字段使用带有 endIconMode="password_toggle" 的 TextInputLayout — 这是 Material Design 的现代标准。其次,不要禁用显示密码的能力 — 用户应该能够检查他们输入了什么,尤其是在带有小键盘的移动设备上。

Secure Text Entry 还需要正确配置自动填充。对于密码字段,务必指定 autofillHints="password" 或 autofillHints="newPassword",以便密码管理器能够正确填充和保存数据。对于新密码确认字段,使用 autofillHints="newPassword",以免系统建议用当前保存的密码填充它。

  • TextInputLayout — 使用带有 endIconMode 的 Material Design TextInputLayout 用于可见性切换。
  • Autofill — 添加 autofillHints="password" 和 importantForAutofill="yes" 以支持管理器。
  • 清除 — 在应用程序最小化时清除密码字段(onPause/onStop)。
  • 长度 — 不要过于严格地限制密码长度(最少 8 个字符,没有硬性最大值)。
  • 生物识别 — 提供生物识别登录(BiometricPrompt)作为手动输入密码的替代方案。
kotlin
// 带有自动填充和清除功能的完整密码字段
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 之间有什么区别?

textPassword 隐藏所有输入字符并禁用自动更正。textVisiblePassword 也禁用自动更正,但以开放形式显示字符。textVisiblePassword 用于切换密码可见性时 — 这不是普通的 text,因为自动更正和建议出于安全原因保持禁用状态。

能否为密码掩码创建自定义符号?

可以,创建一个实现 TransformationMethod 接口的自定义类。重写 getTransformation() 方法,返回带有所需替换字符的 CharSequence。通过 editText.transformationMethod = CustomPasswordTransformation() 设置它。记住,该符号应该是等宽字体以确保安全。

为什么不建议禁用显示密码的能力?

在具有小屏幕和触摸键盘的移动设备上,输入密码时出错的可能性明显高于台式机。检查输入的密码的能力可以减少输入错误的数量并减少密码恢复请求的数量,从而改善用户体验并减轻支持负担。

如何保护密码免受应用程序屏幕截图的影响?

在 Activity 或 Dialog 窗口上设置 FLAG_SECURE 标志:window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)。这将阻止整个窗口的屏幕截图和屏幕录制。但请注意,这也将禁用普通内容的屏幕截图,这可能是不希望的。

autofill 对密码字段如何工作?

从 Android 8.0(API 26)开始,Autofill Framework 通过 inputType 自动检测密码字段。为了正常工作,设置 importantForAutofill="yes" 并指定 autofillHints("password"、"newPassword"、"emailAddress")。Gboard 和密码管理器使用这些属性来建议保存的密码。

总结

  • Secure Text Entry — 隐藏输入模式,通过 PasswordTransformationMethod 将字符替换为掩码符号(点)。
  • 激活 — 通过 inputType="textPassword" 或程序化设置 TYPE_TEXT_VARIATION_PASSWORD。
  • PasswordTransformationMethod — Android 内置类,在不更改 Editable 的情况下替换字符显示。
  • 可见性切换 — 通过带有 endIconMode="password_toggle" 和 TYPE_TEXT_VARIATION_VISIBLE_PASSWORD 的 TextInputLayout 实现。
  • 保护 — 防止 shoulder surfing、屏幕截图(使用 FLAG_SECURE)、自动更正和词典存储。
  • Autofill — 为支持密码管理器,添加 importantForAutofill 和 autofillHints="password"。
  • 最佳实践 — 使用 TextInputLayout,在最小化时清除字段,不要过于严格地限制密码长度。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读