SSH Password Authentication Enabled
Checks whether SSH accepts passwords instead of only keys.
- Why It Matters
- Internet-facing SSH receives constant automated password guessing. Public-key authentication cannot be guessed, phished as easily, or reused from another breached site.
- How It Detects
- Reads PasswordAuthentication and KbdInteractiveAuthentication (ChallengeResponseAuthentication on older versions) from the effective sshd configuration. Either set to 'yes' fails. A risky value set inside a Match block in the config files fails as a conditional finding, with the Match condition in the evidence.
- How to Fix
- Install your public key for each admin user (ssh-copy-id), confirm key login works in a second session, then set PasswordAuthentication no and KbdInteractiveAuthentication no in /etc/ssh/sshd_config and reload sshd.
- How to Verify
sshd -T | grep -Ei 'passwordauthentication|kbdinteractive' should show 'no' for both.- CVSS v3.1 Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N- Control Category
- Identity and Authentication: CIS Controls v8 5, 6; NIST CSF 2.0 PR.AA
- Fix Effort
- Low
- CIS Controls v8 Safeguard
- 4.1
- NIST CSF 2.0 Subcategory
- PR.PS-01
- CWE
- CWE-307, CWE-521
- NIST SP 800-53 Rev. 5
- IA-2(1), IA-5
- Benchmark Topic
- SSH server configuration: authentication methods (topic only, no benchmark text)
- References
- OpenBSD, sshd_config(5) manual page
NIST SP 800-63B-4, Digital Identity Guidelines: Authentication and Authenticator Management
Source Code
The complete file that defines this check. Run it with openvapt scan --only HOST-SSH-002, or read it offline with openvapt explain HOST-SSH-002.
internal/checks/host/ssh_password_auth.go · Lines 1-72View on GitHub at This Commit
| 1 | package host |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | |
| 6 | "github.com/openvapt/openvapt/internal/check" |
| 7 | "github.com/openvapt/openvapt/internal/checks/collect" |
| 8 | "github.com/openvapt/openvapt/internal/sys" |
| 9 | ) |
| 10 | |
| 11 | func init() { |
| 12 | check.Register(&check.Check{ |
| 13 | ID: "HOST-SSH-002", |
| 14 | Title: "SSH Password Authentication Enabled", |
| 15 | Domain: check.DomainHost, |
| 16 | Severity: check.Medium, |
| 17 | CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N", |
| 18 | Summary: "Checks whether SSH accepts passwords instead of only keys.", |
| 19 | Rationale: "Internet-facing SSH receives constant automated password guessing. Public-key authentication cannot be guessed, phished as easily, or reused from another breached site.", |
| 20 | Detection: "Reads PasswordAuthentication and KbdInteractiveAuthentication (ChallengeResponseAuthentication on older versions) from the effective sshd configuration. Either set to 'yes' fails. A risky value set inside a Match block in the config files fails as a conditional finding, with the Match condition in the evidence.", |
| 21 | Fix: "Install your public key for each admin user (ssh-copy-id), confirm key login works in a second session, then set PasswordAuthentication no and KbdInteractiveAuthentication no in /etc/ssh/sshd_config and reload sshd.", |
| 22 | Verify: "sshd -T | grep -Ei 'passwordauthentication|kbdinteractive' should show 'no' for both.", |
| 23 | Refs: []check.Ref{refSSHDManual, refNIST63B}, |
| 24 | Standards: check.Standards{CWE: []string{"CWE-307", "CWE-521"}, NIST: []string{"IA-2(1)", "IA-5"}, CISTopic: "SSH server configuration: authentication methods"}, |
| 25 | Run: func(ctx context.Context, s sys.System) []check.Result { |
| 26 | cfg, ok := collect.SSHD(ctx, s) |
| 27 | if !ok { |
| 28 | return []check.Result{check.SkipR("sshd", "SSH daemon configuration not found")} |
| 29 | } |
| 30 | pw := cfg["passwordauthentication"] |
| 31 | kbd := cfg["kbdinteractiveauthentication"] |
| 32 | if kbd == "" { |
| 33 | kbd = cfg["challengeresponseauthentication"] |
| 34 | } |
| 35 | if pw == "yes" || pw == "" { |
| 36 | return []check.Result{check.FailR("sshd", "Password logins are accepted (PasswordAuthentication defaults to yes)", "passwordauthentication "+orDefault(pw))} |
| 37 | } |
| 38 | if kbd == "yes" { |
| 39 | return []check.Result{check.FailR("sshd", "Keyboard-interactive (password-style) logins are accepted", "kbdinteractiveauthentication yes")} |
| 40 | } |
| 41 | if r, bad := sshMatchFail(cfg, "Password logins are accepted", "passwordauthentication", "kbdinteractiveauthentication", "challengeresponseauthentication"); bad { |
| 42 | return []check.Result{r} |
| 43 | } |
| 44 | return []check.Result{check.PassR("sshd", "Only key-based SSH authentication is accepted")} |
| 45 | }, |
| 46 | }) |
| 47 | } |
| 48 | |
| 49 | func orDefault(v string) string { |
| 50 | if v == "" { |
| 51 | return "(not set, default applies)" |
| 52 | } |
| 53 | return v |
| 54 | } |
| 55 | |
| 56 | // sshMatchFail reports settings that are safe globally but turned back on |
| 57 | // inside a Match block, as a conditional failure whose evidence names the |
| 58 | // Match condition, for example "Match user deploy: passwordauthentication yes". |
| 59 | func sshMatchFail(cfg map[string]string, what string, keys ...string) (check.Result, bool) { |
| 60 | var ev []string |
| 61 | for _, k := range keys { |
| 62 | v, ok := cfg["match:"+k] |
| 63 | if !ok || !collect.SSHDangerous(k, v) { |
| 64 | continue |
| 65 | } |
| 66 | ev = append(ev, "Match "+cfg["matchcond:"+k]+": "+k+" "+v) |
| 67 | } |
| 68 | if len(ev) == 0 { |
| 69 | return check.Result{}, false |
| 70 | } |
| 71 | return check.FailR("sshd", what+" for connections matching a Match block (conditional)", ev...), true |
| 72 | } |
Also available as plain Markdown for notes, scripts and AI tools. Same facts, same source, no styling.
Cite This Page
If this page helped your work, please cite it. The version and web address are filled in; the access date is today's date in your browser. Every other style, and when credit is required, is on How to Cite OpenVAPT.
OpenVAPT. (2026). HOST-SSH-002 SSH Password Authentication Enabled (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/HOST-SSH-002.html
In text: (OpenVAPT, 2026). Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.
@misc{openvapt_host_ssh_002,
author = {{OpenVAPT}},
title = {{HOST-SSH-002 SSH Password Authentication Enabled}},
year = {2026},
howpublished = {\url{https://openvapt.com/checks/HOST-SSH-002.html}},
note = {OpenVAPT Check Catalogue, version 0.1.0-alpha.6. Accessed 16 October 2026}
}
Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.
Citing OpenVAPT does not mean OpenVAPT endorses, approves or has reviewed your work.