Unmasked Password Fields

ID

html.missing_password_field_masking

Severity

high

Resource

Information Leak

Language

Html

Tags

CWE:549, NIST.SP.800-53, OWASP:2021:A4, PCI-DSS:6.5.4, PCI-DSS:6.5.6

Description

This rule identifies HTML input fields that appear to collect passwords or other secrets but use type="text" instead of type="password". Password fields must be properly masked on-screen to prevent shoulder surfing attacks and ensure browsers recognize them as sensitive credential fields.

Rationale

Using type="text" for password input fields creates critical security and usability vulnerabilities:

Shoulder Surfing Attacks: Unmasked password fields display credentials in plain text on-screen, allowing anyone nearby (in offices, cafes, public spaces, or even via video calls) to read passwords directly. This is one of the most common and easily exploitable security weaknesses.

Browser Security Features Bypassed: Modern browsers provide enhanced security for password fields including: - Password strength meters and warnings for weak passwords - Credential autofill and password manager integration - Automatic detection of credential harvesting (anti-phishing) - Special handling in browser history and autocomplete - Protection from cross-site scripting attacks targeting credentials

When type="text" is used, browsers cannot apply these protections, leaving users vulnerable.

Screen Recording and Screenshots: Unmasked passwords appear in: - Screen sharing sessions during remote meetings - Automated screenshots by monitoring tools - Screen recording malware - Debugging logs and error reports - Browser developer tools network inspection

Compliance Violations: Security standards explicitly require password masking: - PCI-DSS requires masking of sensitive authentication data - NIST SP 800-63B recommends password masking as default - OWASP Authentication guidelines mandate proper password field types - Various privacy regulations require protection of authentication credentials

User Trust and Expectations: Users expect password fields to be masked. Unmasked fields: - Violate user expectations and reduce trust - May cause users to avoid the application entirely - Create liability if credentials are compromised - Damage brand reputation when discovered

Consider the following vulnerable code:

<!-- Passwords displayed in plain text -->
<input type="text" name="password" placeholder="Enter password" />

<input type="text" name="user_passwd" placeholder="Password" />

<input type="text" id="pwd" placeholder="Your password" />

<input type="text" name="secret_key" placeholder="API Secret" />

<!-- Multilingual examples -->
<input type="text" name="contraseña" placeholder="Contraseña" />
<input type="text" name="mot_de_passe" placeholder="Mot de passe" />
<input type="text" name="passwort" placeholder="Passwort" />
<input type="text" name="senha" placeholder="Senha" />

Remediation

Always use type="password" for input fields that collect passwords, secrets, API keys, or any other sensitive credentials. This ensures proper masking and enables browser security features.

Vulnerable Patterns:

<!-- Plain text password fields - VULNERABLE -->
<input type="text" name="password" />
<input type="text" name="user_password" />
<input type="text" name="passwd" />
<input type="text" name="pwd" />
<input type="text" id="password_field" />
<input type="text" name="secret" />
<input type="text" name="api_secret" />

<!-- Multilingual variations - VULNERABLE -->
<input type="text" name="contraseña" /> <!-- Spanish -->
<input type="text" name="mot_de_passe" /> <!-- French -->
<input type="text" name="passwort" /> <!-- German -->
<input type="text" name="senha" /> <!-- Portuguese -->
<input type="text" name="wachtwoord" /> <!-- Dutch -->

Secure Patterns:

<!-- Properly masked password fields -->
<input type="password"
       name="password"
       autocomplete="current-password"
       required />

<input type="password"
       name="new_password"
       autocomplete="new-password"
       required />

<input type="password"
       name="confirm_password"
       autocomplete="new-password"
       required />

<!-- API secrets and keys -->
<input type="password"
       name="api_secret"
       autocomplete="off" />

<input type="password"
       name="secret_key"
       autocomplete="off" />

Enhanced Security Implementation:

<form action="/login" method="POST" autocomplete="on">
  <!-- Username field with proper autocomplete -->
  <label for="username">Username</label>
  <input type="text"
         id="username"
         name="username"
         autocomplete="username"
         required />

  <!-- Password field with masking and proper autocomplete -->
  <label for="password">Password</label>
  <input type="password"
         id="password"
         name="password"
         autocomplete="current-password"
         minlength="12"
         required />

  <!-- Optional: Show password toggle (secure implementation) -->
  <label>
    <input type="checkbox" id="show-password" />
    Show password
  </label>

  <button type="submit">Sign In</button>
</form>

<script>
  // Optional: Secure password visibility toggle
  document.getElementById('show-password').addEventListener('change', function() {
    const passwordField = document.getElementById('password');
    passwordField.type = this.checked ? 'text' : 'password';
  });
</script>

Password Field Best Practices:

  1. Always use type="password" for credentials

  2. Set proper autocomplete attributes:

    • autocomplete="current-password" for login forms

    • autocomplete="new-password" for registration/password change

    • autocomplete="off" for API secrets that shouldn’t be saved

  3. Implement password requirements:

    • minlength attribute for minimum password length

    • required attribute for mandatory fields

    • pattern attribute for complexity requirements (optional)

  4. Consider password visibility toggle:

    • Allow users to temporarily unmask passwords

    • Implement as user-controlled checkbox/button

    • Ensure toggle state resets on page navigation

  5. Avoid common mistakes:

    • Never store passwords in localStorage/sessionStorage

    • Never log passwords in console or analytics

    • Never pre-fill password fields from URL parameters

    • Never disable paste functionality (users need it for password managers)

Autocomplete Attribute Reference:

  • current-password: For login forms (existing password)

  • new-password: For registration and password change forms (new password)

  • off: Disables autocomplete (use for API keys/secrets, not user passwords)

  • username: For username fields (helps password managers associate credentials)

Testing Your Implementation:

  1. Verify password masking in all browsers

  2. Test password manager integration (autofill should work)

  3. Check that browser password strength indicators appear

  4. Ensure passwords don’t appear in browser autocomplete suggestions for text fields

  5. Verify passwords are hidden in browser DevTools network tab (HTTPS required)

Configuration

This detector uses a configurable pattern to identify password-related field names:

Default Pattern:(?:^|[\s&?=])(password|passwd|pwd|pass(?![a-zA-Z0-9])|secret|clave|secreto|contraseña|motdepasse|mdp|passwort|geheim|senha|segredo|wachtwoord)(=)?