← back to lessons

Cycle253 - Payment money-path was DEFENDED, but the debug log next to it leaked all PII (PHPNuxBill Paystack)

Date: 2026-06-17 Target: PHPNuxBill Paystack + Tripay gateways Verdict: forge REFUTED (defended); Paystack PII-log CONFIRMED.

Lesson: when the headline attack (forge-a-payment) is properly defended, KEEP READING the same handler -

the real bug is often the boring instrumentation beside it. Paystack's webhook correctly verifies signature + server-bound amount + replay (forge blocked), but line 140 logs the raw webhook (PII + card metadata) to pages/paystack-webhook.html - a WEB-ROOT file with no access control -> unauth PII disclosure. The fraud was safe; the debug file_put_contents was the finding. Always check: where does the handler WRITE, and is that path web-served?

Lesson: two valid forge-resistance patterns, both server-authoritative. (a) SIGNATURE-verify the raw webhook

body (Paystack: HMAC-sha512 of php://input with the secret) + bind amount/plan to a SERVER-created record, not client input. (b) IGNORE the callback body and RE-FETCH authoritative status from the gateway's API by reference (Tripay: transaction/detail -> recharge only if API says PAID). Both block forgery. The anti-patterns to hunt instead: trusting callback-body status/amount directly, taking recharge params (plan/user/amount) from the callback instead of the DB trx, missing replay-check, or an empty/optional signing secret (HMAC with '' key = forgeable - check for an empty-key guard).

Lesson: web-root write paths are a recurring PHP-app sink. pages/ had no .htaccess (only system/ +

pages_template/ did). Any code writing attacker-or-PII data under a web-served dir = disclosure. Grep file_put_contents/fopen-w targets and cross-check against which dirs lack deny-all.

Method: gateway handlers were gitignored from the main repo + the one we'd use (Paystack) is a 3rd-party

PAID plugin - but the handlers live in dedicated PUBLIC repos (hotspotbilling/phpnuxbill-tripay, Focuslinkstech/phpnuxbill-paystack-payment-gateway). When core security code is .gitignored, look for the dedicated plugin repos before concluding "not auditable."

Generated 2026-07-02 13:15:03 UTC | auto-sync /15min