Client security engagement·CONFIDENTIAL - ANONYMIZED

Multi-Phase Product Security Assessment

Confidential European B2B Software Platform · Independent Product Security Consultant

Client
Confidential European B2B software company operating a multi-tenant CMS and website-rendering platform
Role
Independent product security consultant: reconnaissance, penetration testing, threat modelling, security architecture, and remediation design
Structure
Three completed assessment phases + a planned incident-response and recovery-validation exercise
Deliverables
Reconnaissance and penetration-test reports, threat model, risk register, target-state architecture, and phased remediation roadmap

Engagement overview

The client operates a multi-tenant platform: an agency builds, manages, and publishes websites for its customers through a central application, with a separate rendering service serving each published site under its own domain.

The platform was approaching the point where it would begin holding real customer data, and the owner commissioned an independent assessment to answer the question every platform owner eventually has to ask honestly instead of hopefully:

What has to be true before this platform can responsibly hold customer data?

Not "do we think we’re secure," which is a feeling, but a specific, falsifiable list of conditions, each with an owner and a way to verify it is actually met. Most engagements quietly answer a smaller question than the one that was asked: a scan tells you about the app, a pentest tells you about the app plus whatever is reachable from outside it, and neither tells you whether the operation around the app, deployment, credentials, backups, incident response, is something you would want to bet customer data on.

Answering the real question took three completed phases and a fourth planned one. External reconnaissance established what an unauthenticated attacker could discover and reach. Live penetration testing examined authentication, authorization, tenant isolation, upload handling, business logic, and infrastructure exposure. A source-informed threat model then evaluated the architecture behind the observed behaviour: credentials, trust boundaries, deployment provenance, destructive attack paths, backup and recovery, monitoring, and incident readiness.

One discipline ran through all of it: evidence strength was kept separate from severity. A finding that is verified live and a finding that is inferred from source but unconfirmed in production can carry identical severity numbers and completely different levels of trust, and a report that blurs the two is quietly asking the reader to trust its confidence along with its content. So every material claim was tagged as one of:

Recorded evidence strength:

  • Verified from source or configuration
  • Verified through live authorized testing
  • Client-confirmed
  • Inferred from available evidence
  • Unknown and requiring additional evidence

Controls that could not be proven were not presented as facts, and hypotheses that did not survive testing were written up as ruled out rather than quietly dropped, because the client deserves to know a scary-sounding theory was actually checked and closed, not just that it went unmentioned.

The result was not only a vulnerability assessment. It was a production-readiness decision backed by evidence, a target-state security architecture, and a prioritized engineering plan for changing that decision.

Phase timeline

01

External reconnaissance

Complete

Mapped the observable attack surface across DNS, TLS, exposed services, application hosts, object storage, monitoring endpoints, and proxy-bypass paths. This phase established what an unauthenticated attacker could discover and reach before application credentials or source access were introduced.

The work covered:

  • DNS and hostname discovery
  • TLS posture
  • Public and out-of-band services
  • Reverse-proxy behaviour
  • Direct-port exposure
  • Storage endpoints
  • Monitoring and infrastructure services
  • Initial attack-path hypotheses

The resulting attack-surface map shaped the authenticated and source-informed phases that followed.

02

Authorized penetration testing

Complete

Performed live, authorized, non-destructive testing of the deployed platform.

Coverage included:

  • Authentication
  • Session handling
  • Horizontal and vertical authorization
  • Cross-tenant access
  • IDOR
  • Privilege escalation
  • Mass assignment
  • CSRF
  • Rate limiting
  • File upload
  • Input injection
  • Renderer behaviour
  • Business logic
  • Infrastructure exposure

The testing also verified remediation of earlier critical findings and confirmed which application-layer controls remained effective under adversarial use rather than source inspection alone.

Test data was restricted to isolated tester-owned projects. No pre-existing tenant data was modified or deleted.

03

Threat modelling and security architecture

Complete

Performed an evidence-disciplined static assessment against a pinned source revision and the available infrastructure configuration.

The work covered:

  • Architecture reconstruction
  • Asset identification
  • Security invariants
  • Trust boundaries
  • Data-flow modelling
  • STRIDE analysis
  • Attack trees
  • Authentication and session design
  • Tenant-isolation architecture
  • Credential blast radius
  • Object-storage and database privileges
  • Renderer and browser attack paths
  • Deployment provenance
  • Supply-chain risk
  • Backup and ransomware exposure
  • Failure modes
  • Detection and incident readiness
  • Assume-breach scenarios

The assessment concluded with:

  • A target-state security architecture
  • Ten proposed architecture decisions
  • Implementation-ready remediation specifications
  • Migration and rollback considerations
  • Security regression-test requirements
  • Detection and incident-response specifications
  • Objective closure criteria for each material finding
04

Incident-response and recovery validation

Planned

Once the backup and recovery architecture has been implemented, conduct a controlled exercise covering:

  • Clean-environment restoration
  • Destructive data-loss scenarios
  • Credential rotation
  • Compromised-host response
  • Evidence preservation
  • Incident ownership and escalation
  • Recovery sequencing
  • Measured RPO and RTO
  • Post-exercise remediation

The objective is to verify that the new controls function under realistic failure conditions rather than existing only as documentation.

Methodology

The engagement combined black-box, authenticated, white-box, and architectural analysis, because each answers a question the others cannot. A source review that finds a beautiful authorization check tells you nothing about whether that check is what is actually running in production; a pentest that finds nothing tells you about the paths it tried, not the architecture underneath them.

M-01External attack-surface mapping

What can an unauthenticated attacker discover and reach?

M-02Live application and API testing

Which reachable paths can be exploited in the deployed application?

M-03Authentication and session analysis

How are identities established, maintained, revoked, and protected?

M-04Tenant-isolation and authorization testing

Can a user cross project, tenant, role, or object boundaries?

M-05Source and configuration review

What security behaviour is implemented in the reviewed revision?

M-06STRIDE and attack-tree analysis

How can individual weaknesses combine across trust boundaries?

M-07Credential and assume-breach analysis

What authority does an attacker inherit after compromising an application process, service account, or operator identity?

M-08Backup, ransomware, and recovery analysis

Can the platform detect, contain, and recover from a destructive event?

Running all of these together is what let the engagement say something a single-method assessment structurally cannot: not just "here are some bugs," but "here is what would have to be true for this platform to be a defensible place to put customer data." It also kept source-level assumptions from being mistaken for production facts, and kept live testing from being read as complete coverage of architectural and operational risk.

Selected findings

anonymized · exploit details withheld · click to expand

The interesting result was not the bug count, it was where the risk actually lived. Authentication and tenant isolation, the parts everyone assumes are the scary part of a multi-tenant CMS, held up under live testing. What actually blocked the platform from holding customer data was almost entirely operational: no independent recovery path, a production build that could not be traced to a reviewed commit, and credentials with far more authority than the application ever needed. The findings below show the range of the assessment; they are not the complete risk register.

BACKUP-001High 15No production backup or disaster-recovery capability

The platform’s internal "backup" feature was page-version snapshots living inside the same database they were supposedly backing up. That is version history, genuinely useful for undoing a bad edit, and it is not disaster recovery. Treating one as the other is the single most common category error in this kind of review: mistaking an in-band convenience feature for an out-of-band safety net.

At the time of assessment, no independent off-box backup existed for the primary database or object storage, so database loss, storage loss, ransomware, destructive compromise, or host failure had no recovery path at all.

The risk was amplified by:

  • Hard-delete code paths
  • Fully privileged database credentials
  • Root-level object-storage credentials
  • No independent recovery account
  • No tested restore procedure
Business impact

A single ransomware event, destructive intrusion, infrastructure failure, or operator error could result in permanent and unrecoverable loss of all tenant data.

Existing controls

No control with disaster-recovery value existed at the time of assessment.

Remediation direction
  • Off-box database backups
  • Independent object-storage replication
  • Separate backup credentials
  • Immutable or versioned storage
  • Encryption
  • Defined retention
  • Backup-failure alerting
  • Scheduled clean-environment restore drills
Closure requirement

The finding cannot be closed merely because backup files exist. Closure requires a successful restore into a clean environment, verification of platform functionality, and evidence that the documented recovery objective can be met under realistic conditions. A backup nobody has restored from is a hypothesis, not a control.

DEPLOY-001High 12Running production build not attributable to a reviewed commit

The running production build could not be reliably traced back to a specific reviewed commit. That sounds like a process gap; it is actually a foundation crack under every other finding in the report, because a source review’s authority rests entirely on the assumption that the reviewed source is what is running.

The deployment used manually built images and mutable references, the health endpoint exposed no build identity, and the live environment behaved differently from the reviewed source in both directions. Break that attribution and every "this control is present in source" claim quietly downgrades to "present in a version of the source, unclear which one, unclear if it is this one." Incident response suffers for the same reason: you cannot roll back to a known-good state you cannot identify.

Business impact

The client could not prove:

  • Which code was running
  • Whether a source-reviewed control existed in production
  • Whether a deployed fix was included
  • Which artifact was known-good
  • Which version should be used during rollback
  • Whether a registry or build compromise had changed the runtime
Remediation direction
  • Build from a clean and approved commit
  • Record the full commit SHA
  • Generate an immutable image digest
  • Sign and verify the digest
  • Deploy by digest rather than mutable tag
  • Expose build metadata through an operational endpoint
  • Record the deployment operator and timestamp
  • Retain the previous known-good digest
Required invariant

Every running production artifact must map to an approved repository commit, a recorded build, an immutable registry digest, and a named deployment event.

EDGE-001High 12ConditionalProduction served an internal self-signed TLS certificate

The deployed platform used an internally issued certificate rather than a publicly trusted production certificate.

The rating depends on the intended operating model.

If the service is deliberately private and every managed client securely trusts the organizational CA, the condition may be acceptable. If the platform is intended to serve public client websites, it is a release-blocking control failure.

Business impact

Users cannot establish a normally trusted browser connection. Certificate warnings train users to bypass trust failures, and security controls such as HSTS cannot be safely relied upon until the certificate path is correct.

Remediation direction
  • Replace the internal certificate with a valid production certificate
  • Confirm normal browser and system trust
  • Verify renewal
  • Enable HSTS only after trusted HTTPS is stable
  • Prevent direct application ports from bypassing the edge
CRED-001 / CRED-002Medium 10 eachApplication used administrative database and object-storage identities

The application ran with administrative database and object-storage identities rather than role-scoped runtime credentials: a root-level object-storage credential and a database-owner identity on PostgreSQL.

This is the exact failure mode where infrastructure consolidation quietly becomes identity consolidation. An application process that can also drop schema, administer storage policy, and reach backup data is not an application process anymore, it is an administrative control plane wearing an application’s clothes. A compromise would inherit that authority, not just the data operations normal runtime needs.

Business impact

A compromised application process could potentially:

  • Read or modify data across all tenants
  • Delete production content
  • Change storage policy
  • Create additional service credentials
  • Alter or destroy database objects
  • Expand persistence
  • Amplify the consequences of the missing backup system
Remediation direction

Introduce separate identities for:

  • Database ownership
  • Database migration
  • Application runtime
  • Background workers
  • Logical backup
  • Object-storage runtime
  • Object-storage administration
  • Backup replication
  • Restore operations
TENANT-001Medium 8Cross-tenant write path in the shared preset library

An authenticated user could supply an arbitrary project identifier when creating a reusable block preset, permitting unauthorized writes into another project’s preset library or the global shared library. Reads stayed scoped, so this was not direct access to another tenant’s content.

Individually it is a smaller finding, but it is a good illustration of why shared code needs to be scoped at the helper level rather than trusted at each call site: a single unscoped lookup reused across contexts turns a local mistake into a platform-wide one.

Business impact

The issue created a cross-tenant integrity violation:

  • Unauthorized content pollution
  • Shared-library manipulation
  • Potential workflow disruption
  • Loss of confidence in tenant boundaries
Existing controls

The wider authorization architecture remained materially sound. The issue was isolated to one write path that performed authentication without enforcing project-scoped authorization before persistence.

Remediation direction
  • Require an explicit project capability on the exact target project
  • Allow global preset creation only for platform administrators
  • Complete authorization before any database insert
  • Return a deterministic denial
  • Add negative tests proving that unauthorized requests create no record

Controls independently validated

Controls that held up deserve the same rigor as controls that did not. A report that only lists what is broken gives you half the picture, and quietly pressures a team into believing nothing they built was any good. The application-layer controls below were verified through source review and, where applicable, challenged through live authorized testing, not just read off the source.

Tenant isolation resisted live adversarial testing

Project authorization was derived server-side from database-backed membership rather than from client-supplied user, tenant, or role values.

Cross-tenant object access, privilege escalation, and mass-assignment attempts failed during live testing.

No general tenant-isolation collapse was identified.

Database access resisted injection testing

Application queries were constructed through parameterized ORM operations rather than attacker-controlled SQL concatenation.

Source tracing found no exploitable raw-query path, and live injection probes did not produce database-level execution.

Upload validation blocked malicious files

The upload pipeline used:

  • File-type allowlisting
  • Magic-byte signature verification
  • Size validation
  • Type revalidation
  • Object metadata checks
Session controls were materially sound

Verified controls included:

  • Secure cookies
  • HttpOnly cookies
  • SameSite protection
  • Limited session lifetime
  • Session revocation after password reset
  • Stronger password requirements
  • Rate limiting
  • Modern password hashing
Authorization failed closed

Several malformed or unauthorized requests returned imperfect error responses, including internal server errors where a cleaner denial would have been preferable.

However, the tested routes failed closed and did not return unauthorized tenant data.

This distinction was recorded: poor error handling was reported separately from an authorization bypass.

Architecture and trust boundaries

SANITIZED CURRENT-STATE DATA FLOWextexternalSINGLE PRODUCTION HOSTEdge proxyTLS · headersPlatform appauth · CMS · APIRendererclient sitesPostgreSQLObject storeBackground jobsno off-box backupsBACKUP-001
Intended boundary

Public application traffic was supposed to enter through the edge proxy. Live testing found deployment drift and out-of-band services that bypassed that intended boundary entirely; those exceptions were recorded separately as infrastructure and deployment findings.

The platform’s primary operational weakness was recoverability: production data in the database and object storage had no independent off-box recovery copy (BACKUP-001).

The complete assessment included:

  • Full data-flow diagrams
  • Trust-boundary analysis
  • Attack trees
  • Credential-flow modelling
  • Assume-breach paths
  • Target-state architecture
  • Migration requirements

Deliverables and outcome

Delivered
  • External reconnaissance and security-audit report
  • Follow-up verification and penetration-test report
  • 135-page threat model and attack-surface assessment
  • Risk register with scored and evidence-classified findings
  • Data-flow diagrams and attack trees
  • Target-state security architecture
  • Ten proposed architecture decisions
  • Implementation-ready remediation specifications
  • Security regression-test requirements
  • Acceptance criteria and closure procedures
  • Phased remediation roadmap

The engagement produced a connected chain, not four disconnected documents.

  • The reconnaissance report established what was externally reachable.
  • The penetration test verified what was actually exploitable and confirmed which earlier fixes held under adversarial pressure.
  • The threat model tied all of that back to source code, trust boundaries, credentials, deployment, recovery, and failure modes.
  • The target-state architecture turned the resulting risk into implementation-ready engineering work, with acceptance criteria attached to every material finding.
The platform was not yet ready to hold customer data, but the reason was not a collapse of its application security model.

The application layer was genuinely solid: tenant isolation and authorization held up under real adversarial pressure, not just a source read.

What actually stood between it and customer data was recoverability, deployment provenance, and credential scope, the operational half of the system that is easy to underinvest in precisely because it does not fail loudly until the day it does.

So instead of recommending a broad rewrite of a functioning application layer, the engagement identified the smaller set of architectural and operational controls that would actually change the platform’s risk posture, the part of the system that is not the app but that the app’s safety quietly depends on.

The client received:

  • Immediate actions for the first seven days
  • A 30-day hardening programme
  • Longer-term architectural improvements
  • Defined implementation owners
  • Acceptance and negative-test requirements
  • Migration and rollback considerations
  • Concrete evidence required to close each finding

The value of a multi-phase, evidence-disciplined assessment is not the finding count. It is converting "do we think this is secure" into "here is exactly what has to be true, who owns making it true, and how we will know when it is."

The planned incident-response and recovery exercise will close the lifecycle by testing whether the implemented backup, containment, and restoration controls actually work under realistic destructive conditions.

Client perspective

“Haroun gave us the confidence to finally take our software live. I care a lot about doing things properly, and what stood out to me was how structured he was the whole way through. He really got to know our system from the inside, focused on the controls that actually mattered, and was careful to separate the weaknesses he could confirm from the ones that were just assumptions. Honestly, without his work we wouldn't have felt ready to go live.
At the end he handed us a clear, prioritized plan for fixing things, a picture of where our security should be heading, and acceptance criteria that were specific enough for our own engineers to implement and check themselves. We'll be sticking with Haroun for our regular testing from here on. Can't recommend him enough.”
Co-founder and Platform Owner
Confidential European Software Company

Sample material

The complete reports contain client-confidential architecture, infrastructure, source references, and exploit details and are therefore not published.

The samples below are anonymized and abridged. Client names, product names, domains, addresses, credentials, repository identifiers, source paths, and selected attack steps have been removed or generalized while preserving the assessment methodology, reasoning structure, and deliverable quality.

← back to work