Security Hardening A Small PHP Portal

Archive note, October 2025: This post is based on BDC.aztexsystems security audit and remediation notes. Operational details have been generalized.

Small internal tools have a way of becoming production tools before anyone admits it.

That is dangerous because the assumptions are different. A development tool can tolerate rough edges. A production portal that handles images, campaign links, customer-facing email HTML, and authenticated workflows cannot.

The BDC security audit was a reminder of that.

The Two-Character Bug

One of the most important findings was also one of the smallest.

An HTML escaping helper had the mappings for < and > inverted. The function existed. The intent was right. The implementation was wrong.

That meant user input that should have rendered as harmless text could still become executable markup in previews or exported templates.

The fix was tiny. The impact was not.

That is the uncomfortable part of security work. A two-character mistake can invalidate the protection around dozens of fields.

Preview Surfaces Are Attack Surfaces

Template builders are especially risky because they often accept arbitrary-looking input and immediately render it.

Vehicle descriptions, campaign titles, button text, footer copy, image URLs, tracking parameters, and promotional text all become part of a preview. If that preview uses unsafe HTML insertion or accepts dangerous URL protocols, the builder becomes an XSS playground.

The audit found the right classes of concern:

  • Direct HTML injection.
  • Unsafe innerHTML behavior.
  • Missing URL protocol validation.
  • Weak campaign identifier handling.
  • Path traversal risk.
  • Authentication gaps around image endpoints.

Those are not exotic bugs. They are the normal bugs that appear when a tool grows quickly.

Authentication Drift

The image tool had its own authentication history. At one point it used a separate PAM-style approach. Later it was moved toward portal session authentication. During development, checks were disabled so the tool could be tested more easily.

That is a common pattern, and it is exactly why pre-deployment checklists matter.

Authentication code that is “ready to enable” is not enabled. A TODO is not a control. A development shortcut is fine only if the deployment process reliably catches it before the tool is exposed.

Security posture is not just code. It is the path from code to production.

The Practical Lesson

The useful result of the audit was not shame. It was clarity.

The project needed:

  • Correct escaping.
  • URL protocol validation.
  • Safer preview rendering.
  • Campaign identifier sanitization.
  • Server-side file validation.
  • Authentication on upload and processing endpoints.
  • CSRF protection where state changes occur.
  • Regression tests for known failures.

That is the difference between a tool that works and a tool that can be trusted.

The lesson is simple: once an internal PHP portal starts producing customer-facing assets, it deserves production-grade hardening even if the team is small and the tool started as a convenience.