Skip to Content
OpenVAPTAlpha
Pre-release. Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. Free for your own use, 100% at your own risk. This website collects no data. Read MoreRead Less
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
CriticalNET-DB-002Data ServicesNetwork

Redis Authentication and Network Binding

Redis lacks protected mode, authentication, or a loopback-only bind.

Why It Matters
An unauthenticated network-reachable Redis instance can disclose or alter data and may permit dangerous administrative operations.
How It Detects
Parses redis.conf for protected-mode, requirepass or ACL users, and bind. It does not connect or authenticate.
How to Fix
Set protected-mode yes, configure ACL users with strong secrets, bind to 127.0.0.1 and ::1, and restrict remote access with a firewall.
How to Verify
Review redis-server --test-memory only for health, then inspect the effective configuration and listening socket after reload.
CVSS v3.1 Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Control Category
Data Services: CIS Controls v8 3, 4, 12; NIST CSF 2.0 PR.DS, PR.PS
Fix Effort
Low
CIS Controls v8 Safeguard
3.3
NIST CSF 2.0 Subcategory
PR.AA-05, PR.DS-01
CWE
CWE-306, CWE-1188
OWASP Top 10
A05:2021
NIST SP 800-53 Rev. 5
AC-3, CM-6, SC-7
Benchmark Topic
Secure data service configuration (topic only, no benchmark text)
References
Redis Security
Learn the theory behind this check: P3 Network Exposure and Firewalls, P8 Data Services: Databases, File Shares, DNS and Mail.

Source Code

The complete file that defines this check. Run it with openvapt scan --only NET-DB-002, or read it offline with openvapt explain NET-DB-002.

internal/checks/network/data_services.go · Lines 1-285View on GitHub at This Commit
1package network
2
3import (
4 "context"
5 "fmt"
6 "strings"
7
8 "github.com/openvapt/openvapt/internal/check"
9 "github.com/openvapt/openvapt/internal/checks/collect"
10 "github.com/openvapt/openvapt/internal/sys"
11)
12
13type configRule struct {
14 id, title, summary, rationale, detection, fix, verify, cvss string
15 severity check.Severity
16 patterns []string
17 ref check.Ref
18 evaluate func([]collect.ConfigFile) (bool, []string)
19}
20
21func values(fs []collect.ConfigFile, keys ...string) []string {
22 var out []string
23 for _, f := range fs {
24 for _, k := range keys {
25 out = append(out, f.Values[strings.ToLower(k)]...)
26 }
27 }
28 return out
29}
30func hasValue(fs []collect.ConfigFile, want func(string) bool, keys ...string) bool {
31 for _, v := range values(fs, keys...) {
32 if want(strings.ToLower(strings.TrimSpace(v))) {
33 return true
34 }
35 }
36 return false
37}
38func anyIncomplete(fs []collect.ConfigFile) bool {
39 for _, f := range fs {
40 if !f.Complete {
41 return true
42 }
43 }
44 return false
45}
46func evidence(fs []collect.ConfigFile, keys ...string) []string {
47 var out []string
48 for _, f := range fs {
49 for _, k := range keys {
50 for _, v := range f.Values[strings.ToLower(k)] {
51 out = append(out, fmt.Sprintf("%s: %s = %s", f.Path, k, v))
52 }
53 }
54 }
55 return out
56}
57
58func registerConfig(r configRule) {
59 check.Register(&check.Check{ID: r.id, Title: r.title, Domain: check.DomainNetwork, Severity: r.severity, Category: check.CatData, Effort: check.EffortLow, CVSS: r.cvss,
60 Summary: r.summary, Rationale: r.rationale, Detection: r.detection, Fix: r.fix, Verify: r.verify, Refs: []check.Ref{r.ref},
61 Standards: dataStandards(r.id),
62 Run: func(ctx context.Context, s sys.System) []check.Result {
63 fs := collect.ReadConfigs(ctx, s, r.patterns...)
64 if len(fs) == 0 {
65 return []check.Result{check.SkipR(strings.Join(r.patterns, ", "), "Service configuration was not found")}
66 }
67 bad, ev := r.evaluate(fs)
68 if bad {
69 return []check.Result{check.FailR(fs[0].Path, r.summary, ev...)}
70 }
71 if anyIncomplete(fs) {
72 return []check.Result{check.IncompleteR(fs[0].Path, "Configuration input was truncated; a secure result cannot be established")}
73 }
74 return []check.Result{check.PassR(fs[0].Path, "Required security settings are present", ev...)}
75 },
76 })
77}
78
79func init() {
80 yes := func(v string) bool { return v == "yes" || v == "true" || v == "on" || v == "1" }
81 no := func(v string) bool { return v == "no" || v == "false" || v == "off" || v == "0" }
82 registerConfig(configRule{
83 "NET-DB-002",
84 "Redis Authentication and Network Binding",
85 "Redis lacks protected mode, authentication, or a loopback-only bind.",
86 "An unauthenticated network-reachable Redis instance can disclose or alter data and may permit dangerous administrative operations.",
87 "Parses redis.conf for protected-mode, requirepass or ACL users, and bind. It does not connect or authenticate.",
88 "Set protected-mode yes, configure ACL users with strong secrets, bind to 127.0.0.1 and ::1, and restrict remote access with a firewall.",
89 "Review redis-server --test-memory only for health, then inspect the effective configuration and listening socket after reload.",
90 "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
91 check.Critical,
92 []string{"/etc/redis/redis.conf", "/etc/redis.conf"},
93 check.Ref{Title: "Redis Security", URL: "https://redis.io/docs/latest/operate/oss_and_stack/management/security/"},
94 func(fs []collect.ConfigFile) (bool, []string) {
95 bad := !hasValue(fs, yes, "protected-mode") || len(values(fs, "requirepass", "user")) == 0 || !hasValue(fs, func(v string) bool { return strings.Contains(v, "127.0.0.1") && !strings.Contains(v, "0.0.0.0") }, "bind")
96 return bad, evidence(fs, "protected-mode", "bind", "user")
97 }})
98 registerConfig(configRule{
99 "NET-DB-003",
100 "MongoDB Authorization and Network Binding",
101 "MongoDB authorization is disabled or it binds beyond loopback.",
102 "MongoDB without authorization or with broad exposure can permit unauthenticated database access.",
103 "Parses mongod configuration for security.authorization and net.bindIp.",
104 "Set security.authorization to enabled and net.bindIp to 127.0.0.1, then allow only approved clients through a controlled network path.",
105 "Run mongod --config <file> --configExpand none in a maintenance workflow and confirm port 27017 is loopback-only.",
106 "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
107 check.Critical,
108 []string{"/etc/mongod.conf", "/etc/mongodb.conf"},
109 check.Ref{Title: "MongoDB Security Checklist", URL: "https://www.mongodb.com/docs/manual/administration/security-checklist/"},
110 func(fs []collect.ConfigFile) (bool, []string) {
111 bad := !hasValue(fs, func(v string) bool { return v == "enabled" }, "authorization", "security.authorization") || !hasValue(fs, func(v string) bool { return v == "127.0.0.1" || v == "localhost" }, "bindip", "net.bindip")
112 return bad, evidence(fs, "authorization", "bindip")
113 }})
114 registerConfig(configRule{
115 "NET-DB-004",
116 "PostgreSQL Trust and Broad Client Access",
117 "PostgreSQL accepts trust authentication or unrestricted client networks.",
118 "Trust authentication and all-address client rules can allow database access without a password from untrusted networks.",
119 "Reads postgresql.conf and pg_hba.conf, flags listen_addresses '*' or 0.0.0.0, trust methods, and 0.0.0.0/0 or ::/0 client ranges.",
120 "Bind PostgreSQL to required interfaces only and replace trust or unrestricted pg_hba.conf rules with narrow CIDRs and scram-sha-256. Caution: users whose passwords are stored as md5 cannot log in with scram-sha-256 until their password is set again with password_encryption = scram-sha-256, so reset them first and reload, not restart, to test.",
121 "Run pg_ctl reload through normal operations, then use pg_hba_file_rules and ss -tlnp to verify the effective rules.",
122 "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
123 check.Critical,
124 []string{"/etc/postgresql/*/main/postgresql.conf", "/etc/postgresql/*/main/pg_hba.conf", "/var/lib/pgsql/data/pg_hba.conf"},
125 check.Ref{Title: "PostgreSQL Client Authentication", URL: "https://www.postgresql.org/docs/current/client-authentication.html"},
126 func(fs []collect.ConfigFile) (bool, []string) {
127 var ev []string
128 bad := false
129 for _, f := range fs {
130 raw := strings.ToLower(strings.Join(flatten(f.Values), " "))
131 if strings.Contains(raw, "0.0.0.0/0") || strings.Contains(raw, "::/0") || strings.Contains(raw, " trust") || hasValue([]collect.ConfigFile{f}, func(v string) bool { return strings.Contains(v, "*") || strings.Contains(v, "0.0.0.0") }, "listen_addresses") {
132 bad = true
133 ev = append(ev, f.Path+": broad or trust rule detected")
134 }
135 }
136 return bad, ev
137 }})
138 registerConfig(configRule{
139 "NET-DB-005",
140 "MySQL or MariaDB Unsafe Network Options",
141 "MySQL or MariaDB enables local file loading, skips grants, or listens broadly with networking enabled.",
142 "These options can bypass authorization, expand file access, or expose the database to untrusted networks.",
143 "Parses MySQL and MariaDB option files for local_infile, skip-grant-tables, skip-networking, and bind-address.",
144 "Disable local_infile and skip-grant-tables. Bind to 127.0.0.1 or enable skip-networking when TCP access is unnecessary.",
145 "Inspect SHOW VARIABLES through an approved administrator session and confirm the listening address locally.",
146 "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
147 check.High,
148 []string{"/etc/mysql/my.cnf", "/etc/mysql/mysql.conf.d/mysqld.cnf", "/etc/my.cnf"},
149 check.Ref{Title: "MySQL Server Security Guidelines", URL: "https://dev.mysql.com/doc/refman/8.4/en/security-guidelines.html"},
150 func(fs []collect.ConfigFile) (bool, []string) {
151 bad := hasValue(fs, yes, "local_infile", "mysqld.local_infile") || len(values(fs, "skip-grant-tables", "mysqld.skip-grant-tables")) > 0 || (!hasValue(fs, yes, "skip-networking", "mysqld.skip-networking") && hasValue(fs, func(v string) bool { return v == "0.0.0.0" || v == "*" }, "bind-address", "mysqld.bind-address"))
152 return bad, evidence(fs, "local_infile", "skip-grant-tables", "skip-networking", "bind-address")
153 }})
154 registerConfig(configRule{
155 id: "NET-DB-006",
156 title: "Memcached UDP or Broad Listen Address",
157 summary: "Memcached enables UDP or listens on a non-loopback address.",
158 rationale: "Unauthenticated Memcached exposure can disclose cached data and UDP mode has been abused for amplification attacks.",
159 detection: "Parses memcached.conf (one option per line) and /etc/sysconfig/memcached (OPTIONS and UDP_PORT) for -U or UDP_PORT and -l. A UDP port other than 0 fails; a listen address fails unless every address is loopback (127.0.0.1, ::1 or localhost); no -l at all means every interface and fails. The actual values are the evidence.",
160 fix: "Set UDP_PORT=0 or -U 0 and bind with -l 127.0.0.1. Use a firewall for any explicitly required clients.",
161 verify: "Confirm ss -ltnup shows Memcached only on loopback and no UDP port 11211.",
162 cvss: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
163 severity: check.High,
164 patterns: []string{"/etc/memcached.conf", "/etc/sysconfig/memcached"},
165 ref: check.Ref{Title: "memcached Configuration", URL: "https://docs.memcached.org/serverguide/configuring/"},
166 evaluate: memcachedEvaluate})
167 registerConfig(configRule{
168 id: "NET-DB-007",
169 title: "Elasticsearch Security and Network Binding",
170 summary: "Elasticsearch security is disabled or network.host is broadly reachable.",
171 rationale: "An exposed Elasticsearch cluster without authentication can disclose, alter, or delete indexed data.",
172 detection: "Parses elasticsearch.yml for xpack.security.enabled and network.host.",
173 fix: "Set xpack.security.enabled true, bind network.host to loopback or an approved private interface, require TLS, and filter access.",
174 verify: "Use the local node configuration API with approved credentials and confirm port 9200 is not broadly exposed.",
175 cvss: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
176 severity: check.Critical,
177 patterns: []string{"/etc/elasticsearch/elasticsearch.yml"},
178 ref: check.Ref{Title: "Elastic Security Minimal Setup", URL: "https://www.elastic.co/docs/deploy-manage/security/set-up-minimal-security"},
179 evaluate: func(fs []collect.ConfigFile) (bool, []string) {
180 bad := !hasValue(fs, yes, "xpack.security.enabled") || hasValue(fs, func(v string) bool { return v == "0.0.0.0" || v == "_site_" || v == "_global_" }, "network.host")
181 return bad, evidence(fs, "xpack.security.enabled", "network.host")
182 }})
183 _ = no
184}
185
186// memcachedEvaluate judges memcached's listen addresses and UDP port, quoting
187// the values it saw.
188func memcachedEvaluate(fs []collect.ConfigFile) (bool, []string) {
189 var listen, udp, ev []string
190 for _, f := range fs {
191 for k, vals := range f.Values {
192 for _, v := range vals {
193 v = strings.Trim(strings.TrimSpace(v), `"'`)
194 switch k {
195 case "-l":
196 listen = append(listen, v)
197 case "-u", "udp_port":
198 if k == "-u" {
199 k = "-U"
200 }
201 udp = append(udp, v)
202 case "options":
203 flds := strings.Fields(v)
204 for i := 0; i+1 < len(flds); i++ {
205 switch flds[i] {
206 case "-l":
207 listen = append(listen, flds[i+1])
208 case "-U":
209 udp = append(udp, flds[i+1])
210 }
211 }
212 default:
213 continue
214 }
215 ev = append(ev, f.Path+": "+k+" "+v)
216 }
217 }
218 }
219 bad := false
220 if len(listen) == 0 {
221 bad = true
222 ev = append(ev, "no -l listen address: memcached listens on every interface")
223 }
224 for _, l := range listen {
225 for _, a := range strings.Split(l, ",") {
226 a = strings.TrimSpace(a)
227 if i := strings.LastIndex(a, ":"); i > 0 && strings.Count(a, ":") == 1 {
228 a = a[:i] // 127.0.0.1:11211
229 }
230 if a != "127.0.0.1" && a != "::1" && a != "localhost" {
231 bad = true
232 }
233 }
234 }
235 for _, u := range udp {
236 if strings.TrimSpace(u) != "0" {
237 bad = true
238 }
239 }
240 return bad, ev
241}
242
243func flatten(m map[string][]string) []string {
244 var out []string
245 for k, v := range m {
246 for _, x := range v {
247 out = append(out, k+" "+x)
248 }
249 }
250 return out
251}
252
253// dataCWE gives each data service check the weakness it actually detects:
254// missing authentication on an insecure default, unsafe sharing permissions,
255// amplification, information exposure or an unintended relay.
256var dataCWE = map[string][]string{
257 "NET-DB-002": {"CWE-306", "CWE-1188"}, // Redis
258 "NET-DB-003": {"CWE-306", "CWE-1188"}, // MongoDB
259 "NET-DB-004": {"CWE-306", "CWE-1188"}, // PostgreSQL trust
260 "NET-DB-005": {"CWE-306", "CWE-1188"}, // MySQL skip-grant-tables, local_infile
261 "NET-DB-006": {"CWE-406", "CWE-1188"}, // memcached UDP amplification
262 "NET-DB-007": {"CWE-306", "CWE-1188"}, // Elasticsearch
263 "NET-DB-008": {"CWE-732", "CWE-306"}, // NFS exports
264 "NET-DB-009": {"CWE-732", "CWE-306"}, // Samba guest
265 "NET-DB-010": {"CWE-406"}, // open recursion
266 "NET-DB-011": {"CWE-497"}, // zone transfer
267 "NET-DB-012": {"CWE-441"}, // open relay
268 "NET-DB-013": {"CWE-319", "CWE-523"}, // cleartext mail authentication
269}
270
271func dataStandards(id string) check.Standards {
272 st := check.Standards{CWE: dataCWE[id], OWASP: []string{"A05:2021"}, NIST: []string{"AC-3", "CM-6", "SC-7"}, CISTopic: "Secure data service configuration"}
273 switch id {
274 case "NET-DB-006", "NET-DB-010":
275 st.NIST = []string{"SC-5", "CM-6", "SC-7"}
276 case "NET-DB-013":
277 st.OWASP = []string{"A02:2021"}
278 st.NIST = []string{"SC-8", "IA-5(1)"}
279 st.ASVS = []string{"9.1.1"}
280 }
281 if len(st.CWE) == 0 {
282 st.CWE = []string{"CWE-1188"}
283 }
284 return st
285}

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.

APA 7
OpenVAPT. (2026). NET-DB-002 Redis Authentication and Network Binding (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/NET-DB-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.

BibTeX
@misc{openvapt_net_db_002,
  author       = {{OpenVAPT}},
  title        = {{NET-DB-002 Redis Authentication and Network Binding}},
  year         = {2026},
  howpublished = {\url{https://openvapt.com/checks/NET-DB-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.