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
MediumWEB-PHP-001Secure ConfigurationWeb Apps

PHP Displays Errors to Visitors

Checks whether PHP prints error messages into web pages.

Why It Matters
Error messages reveal file paths, SQL queries and code structure, and can leak data. They help attackers turn a small bug into a working exploit.
How It Detects
Asks the PHP command-line binary for ini_get('display_errors') (the only PHP code OpenVAPT may run). Web PHP-FPM pools can use a different php.ini; verify those too.
How to Fix
In php.ini set display_errors = Off and log_errors = On , then restart PHP-FPM or the web server.
How to Verify
php -r "echo ini_get('display_errors');" and check the FPM pool's php.ini or php_admin_value settings.
CVSS v3.1 Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Control Category
Secure Configuration: CIS Controls v8 4; NIST CSF 2.0 PR.PS
Fix Effort
Low
CIS Controls v8 Safeguard
16.7
NIST CSF 2.0 Subcategory
PR.PS-01
OWASP WSTG v4.2
WSTG-ERRH-01
CWE
CWE-209
OWASP Top 10
A05:2021
OWASP ASVS 4.0.3
14.3.2
NIST SP 800-53 Rev. 5
SI-11, CM-6
References
PHP manual, php.ini directives
OWASP PHP Configuration Cheat Sheet
Learn the theory behind this check: P6 Information Exposure and Misconfiguration.

Source Code

The complete file that defines this check. Run it with openvapt scan --only WEB-PHP-001, or read it offline with openvapt explain WEB-PHP-001.

internal/checks/web/php.go · Lines 1-177View on GitHub at This Commit
1package web
2
3import (
4 "context"
5 "regexp"
6 "strconv"
7 "strings"
8
9 "github.com/openvapt/openvapt/internal/check"
10 "github.com/openvapt/openvapt/internal/sys"
11)
12
13var refPHPIni = check.Ref{Title: "PHP manual, php.ini directives", URL: "https://www.php.net/manual/en/ini.list.php"}
14
15// phpIni asks the PHP CLI for configuration values using only echo ini_get(),
16// which is enforced by the sys allow-list. Note: the CLI may use a different
17// php.ini from PHP-FPM; the evidence says which binary was asked.
18func phpIni(ctx context.Context, s sys.System, keys ...string) (map[string]string, bool) {
19 var b strings.Builder
20 for _, k := range keys {
21 b.WriteString("echo ini_get('" + k + "'),\"\\n\";")
22 }
23 out, err := s.Run(ctx, "php", "-r", b.String())
24 if err != nil {
25 return nil, false
26 }
27 vals := strings.Split(out, "\n")
28 m := map[string]string{}
29 for i, k := range keys {
30 if i < len(vals) {
31 m[k] = strings.TrimSpace(vals[i])
32 }
33 }
34 return m, true
35}
36
37func on(v string) bool {
38 v = strings.ToLower(v)
39 return v == "1" || v == "on" || v == "yes" || v == "true" || v == "stdout" || v == "stderr"
40}
41
42// phpEOL: end of security support per https://www.php.net/supported-versions.php
43var phpEOL = map[string]string{
44 "5.6": "2018-12-31", "7.0": "2019-01-10", "7.1": "2019-12-01", "7.2": "2020-11-30", "7.3": "2021-12-06",
45 "7.4": "2022-11-28", "8.0": "2023-11-26", "8.1": "2025-12-31", "8.2": "2026-12-31", "8.3": "2027-12-31", "8.4": "2028-12-31", "8.5": "2029-12-31",
46}
47
48func init() {
49 iniRule := func(id, title, key, summary, rationale, fix string, sev check.Severity, cvss string, std check.Standards, bad func(string) bool) {
50 check.Register(&check.Check{
51 ID: id, Title: title, Domain: check.DomainWeb, Severity: sev,
52 CVSS: cvss,
53 Summary: summary,
54 Rationale: rationale,
55 Detection: "Asks the PHP command-line binary for ini_get('" + key + "') (the only PHP code OpenVAPT may run). Web PHP-FPM pools can use a different php.ini; verify those too.",
56 Fix: fix,
57 Verify: "php -r \"echo ini_get('" + key + "');\" and check the FPM pool's php.ini or php_admin_value settings.",
58 Refs: []check.Ref{refPHPIni, {Title: "OWASP PHP Configuration Cheat Sheet", URL: "https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html"}},
59 Standards: std,
60 Run: func(ctx context.Context, s sys.System) []check.Result {
61 m, ok := phpIni(ctx, s, key)
62 if !ok {
63 return []check.Result{check.SkipR("php", "PHP command-line binary not found")}
64 }
65 if bad(m[key]) {
66 return []check.Result{check.FailR("php", key+" = "+m[key])}
67 }
68 return []check.Result{check.PassR("php", key+" = "+orOff(m[key]))}
69 },
70 })
71 }
72 iniRule("WEB-PHP-001", "PHP Displays Errors to Visitors", "display_errors",
73 "Checks whether PHP prints error messages into web pages.",
74 "Error messages reveal file paths, SQL queries and code structure, and can leak data. They help attackers turn a small bug into a working exploit.",
75 "In php.ini set display_errors = Off and log_errors = On , then restart PHP-FPM or the web server.", check.Medium, "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N", check.Standards{CWE: []string{"CWE-209"}, OWASP: []string{"A05:2021"}, ASVS: []string{"14.3.2"}, WSTG: []string{"WSTG-ERRH-01"}, NIST: []string{"SI-11", "CM-6"}}, on)
76 iniRule("WEB-PHP-002", "PHP Version Exposed in Headers", "expose_php",
77 "Checks whether PHP adds an X-Powered-By header with its version.",
78 "The header advertises the exact PHP version to every visitor and scanner.",
79 "In php.ini set expose_php = Off and restart PHP.", check.Low, "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N", check.Standards{CWE: []string{"CWE-497"}, OWASP: []string{"A05:2021"}, ASVS: []string{"14.3.3"}, WSTG: []string{"WSTG-INFO-02"}, NIST: []string{"CM-7", "SI-11"}}, on)
80 iniRule("WEB-PHP-003", "PHP Allows Remote File Inclusion", "allow_url_include",
81 "Checks whether include() and require() may load code from URLs.",
82 "With allow_url_include on, a single file-inclusion bug lets an attacker run their own PHP code from their own server (remote file inclusion).",
83 "In php.ini set allow_url_include = Off (the default since PHP 5.2) and restart PHP.", check.High, "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", check.Standards{CWE: []string{"CWE-98"}, OWASP: []string{"A03:2021", "A05:2021"}, ASVS: []string{"12.3.3"}, NIST: []string{"CM-6", "CM-7"}}, on)
84
85 check.Register(&check.Check{
86 ID: "WEB-PHP-004",
87 Title: "PHP Version Past End of Security Support",
88 Domain: check.DomainWeb,
89 Severity: check.High,
90 CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
91 Summary: "Checks whether the installed PHP branch still receives security fixes from the PHP project.",
92 Rationale: "Unsupported PHP versions never get fixes for newly found vulnerabilities, some of which allow remote code execution.",
93 Detection: "Runs php -v, extracts the major.minor version and compares it with the PHP project's published end-of-security-support dates. Distribution vendors may backport fixes; the evidence notes this.",
94 Fix: "Upgrade to a supported PHP branch (see php.net/supported-versions) and test the applications, or confirm your distribution still backports security fixes to this version.",
95 Verify: "php -v",
96 Refs: []check.Ref{{Title: "PHP, Supported Versions", URL: "https://www.php.net/supported-versions.php"}},
97 Standards: check.Standards{CWE: []string{"CWE-1104"}, OWASP: []string{"A06:2021"}, ASVS: []string{"14.2.1"}, NIST: []string{"SI-2"}},
98 Run: func(ctx context.Context, s sys.System) []check.Result {
99 out, err := s.Run(ctx, "php", "-v")
100 if err != nil {
101 return []check.Result{check.SkipR("php", "PHP command-line binary not found")}
102 }
103 m := regexp.MustCompile(`PHP (\d+\.\d+)\.(\d+)`).FindStringSubmatch(out)
104 if m == nil {
105 return []check.Result{check.SkipR("php", "PHP version not recognised")}
106 }
107 end, ok := phpEOL[m[1]]
108 if !ok && phpOlderThanTable(m[1]) {
109 return []check.Result{check.FailR("php", "PHP "+m[1]+" is older than every branch in the support table (oldest 5.6, ended 2018-12-31)", "Check whether your distribution backports security fixes to this version")}
110 }
111 if !ok {
112 return []check.Result{check.SkipR("php", "PHP "+m[1]+" not in the support table")}
113 }
114 if end < today() {
115 return []check.Result{check.FailR("php", "PHP "+m[1]+" security support ended on "+end, "Check whether your distribution backports security fixes to this version")}
116 }
117 return []check.Result{check.PassR("php", "PHP "+m[1]+" supported until "+end)}
118 },
119 })
120}
121
122func orOff(v string) string {
123 if v == "" {
124 return "Off"
125 }
126 return v
127}
128
129func init() {
130 check.Register(&check.Check{
131 ID: "WEB-PHP-005", Title: "PHP Session Cookies Not Hardened", Domain: check.DomainWeb,
132 Severity: check.Medium, Category: check.CatWebApp, Effort: check.EffortLow,
133 CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N",
134 Summary: "Checks PHP's session settings: HttpOnly and SameSite cookies and strict session id mode.",
135 Rationale: "Session cookies are the keys to logged-in accounts. Without HttpOnly any XSS bug can steal them; without strict mode PHP accepts attacker-chosen session ids (session fixation).",
136 Detection: "Asks the PHP command-line binary for ini_get('session.cookie_httponly'), ini_get('session.use_strict_mode') and ini_get('session.cookie_samesite'). PHP-FPM pools may use a different php.ini; verify those too.",
137 Fix: "In php.ini set session.cookie_httponly = 1 , session.use_strict_mode = 1 , session.cookie_samesite = Lax and, for HTTPS sites, session.cookie_secure = 1 , then restart PHP-FPM.",
138 Verify: "php -r \"echo ini_get('session.cookie_httponly'), ini_get('session.use_strict_mode'), ini_get('session.cookie_samesite');\"",
139 Refs: []check.Ref{refPHPIni, {Title: "PHP manual, Session security", URL: "https://www.php.net/manual/en/session.security.php"}},
140 Standards: check.Standards{CWE: []string{"CWE-1004", "CWE-384"}, OWASP: []string{"A07:2021"}, ASVS: []string{"3.4.2", "3.2.1"}, WSTG: []string{"WSTG-SESS-02", "WSTG-SESS-03"}, NIST: []string{"SC-23", "CM-6"}},
141 Run: func(ctx context.Context, s sys.System) []check.Result {
142 m, ok := phpIni(ctx, s, "session.cookie_httponly", "session.use_strict_mode", "session.cookie_samesite")
143 if !ok {
144 return []check.Result{check.SkipR("php", "PHP command-line binary not found")}
145 }
146 var bad []string
147 if !on(m["session.cookie_httponly"]) {
148 bad = append(bad, "session.cookie_httponly = "+orOff(m["session.cookie_httponly"]))
149 }
150 if !on(m["session.use_strict_mode"]) {
151 bad = append(bad, "session.use_strict_mode = "+orOff(m["session.use_strict_mode"]))
152 }
153 if strings.TrimSpace(m["session.cookie_samesite"]) == "" {
154 bad = append(bad, "session.cookie_samesite is empty")
155 }
156 if len(bad) > 0 {
157 return []check.Result{check.FailR("php", "PHP session cookies are not hardened", bad...)}
158 }
159 return []check.Result{check.PassR("php", "PHP session cookies are hardened")}
160 },
161 })
162}
163
164// phpOlderThanTable reports a major.minor version below 5.6, the oldest row
165// of phpEOL; every such branch is long past end of support.
166func phpOlderThanTable(v string) bool {
167 maj, min, ok := strings.Cut(v, ".")
168 if !ok {
169 return false
170 }
171 a, err1 := strconv.Atoi(maj)
172 b, err2 := strconv.Atoi(min)
173 if err1 != nil || err2 != nil {
174 return false
175 }
176 return a < 5 || a == 5 && b < 6
177}

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). WEB-PHP-001 PHP Displays Errors to Visitors (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/WEB-PHP-001.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_web_php_001,
  author       = {{OpenVAPT}},
  title        = {{WEB-PHP-001 PHP Displays Errors to Visitors}},
  year         = {2026},
  howpublished = {\url{https://openvapt.com/checks/WEB-PHP-001.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.