Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Full stack security for PHP apps

Full stack security for PHP apps

In this 1-day workshop, we will set up a server and work through a set of examples in vanilla PHP to discuss and demonstrate security problems and solutions at every layer of the deployment stack, from cloud infrastructure all the way through firewalls, SSH & TLS config, SQLi, validation & escaping, and XSS.

We will also look at numerous testing tools to help check what you're doing is actually working!

This version of the workshop was presented at the 2025 International PHP Conference in Munich on October 31st, 2025.

Avatar for Marcus Bointon

Marcus Bointon

October 31, 2025
Tweet

More Decks by Marcus Bointon

Other Decks in Technology

Transcript

  1. Marcus Bointon Skier Songwriter Author of "The HTTP/3 Book" PHPMailer

    maintainer, Laravel dev Radically Open Security Pentests, code audits
  2. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Workshop

    plan Infrastructure Server con fi g App front-end security App back-end security
  3. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    acid • hiro • robot • crash • case • ash • elliot • lisbeth • mikael • david • solo • trinity • cereal • whiterose • kevin • zero • plague • chloe • abby • alan • fl ynn • quorra • luther • stanley • bishop • anon • axel Host names
  4. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Laptop with WiFi • macOS, Linux, Windows preferably with WSL • Terminal app • Up-to-date SSH client • Optional: IDE, Burp Suite, ZAP Prerequisites
  5. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    General PKI • OAuth 2 • CORS • DNSSEC, DANE, DoH & DoT • Running a CA, cert pinning • Windows servers • PHP app development! What we're not covering
  6. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    "Don't mess with my stu ff " • Especially if it includes data about people • Reliability • Legal liability, due diligence Why are we doing this?
  7. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Security

    & Privacy • Security • Con fi dentiality • Authenticity/ Availability • Integrity • Data protection • Using security to protect data • Privacy • Using security to protect data about people • Personal data (GDPR)
  8. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Who and what are you defending against? • Admin accidents • Drive-by opportunists • Malicious users • Malicious admins • Nation states Threat modelling
  9. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Threat

    modelling – STRIDE Threat Defence Spoo fi ng Authenticity Tampering Integrity Repudiation Non-repudiation Information disclosure Con fi dentiality Denial of service Availability Escalation of privilege Authorization
  10. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Typical

    deployment stack Cloud provider AWS Azure GC Infrastructure Network Servers Storage Services Database Web Cache Applications NodeJS PHP Python Clients Browsers Apps Attackers
  11. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Multiple layers of defence • Avoiding single points of failure • Limiting lateral movement • Limiting damage • Limiting ex fi ltration Defence in depth
  12. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Security before you even have a server • Network isolation / VPCs / containers • Security groups • DDoS mitigation • Identity Access Management (IAM) Hosting provider security
  13. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Amazon AWS • EC2 • Security groups • Route53 • AIM • Too easy to take the chmod 777 route! Cloud admin demo
  14. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Immutable servers? • SSH the default, but may not need to expose • Especially database servers • Use bastion / jump hosts • Rate limit, limit source IPs, port knocking Remote admin?
  15. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    ssh – secure shell • sftp, scp, rsync • mosh – mobile shell • Indestructible terminal sessions, SSH + UDP • tmux • Server-side terminal state: Never lose your place, share terminals • RDP for Windows servers Remote admin tools
  16. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    ssh-keygen -t ed25519 • Public key in ~/.ssh/id_ed25519.pub • Private key in ~/.ssh/id_ed25519 • Use ssh-agent to manage private keys • Can also use a password manager like 1Password • Require admins to use up to date software Creating SSH keys
  17. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    ~/.ssh/config • Local, set connection params, users, keys • ~/.ssh/known_hosts • Local, remembers servers you've used before • ~/.ssh/authorized_keys • On server, stores public keys that are allowed to connect SSH config files
  18. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Edit ~/.ssh/config: • Host * • IdentityFile ~/.ssh/id_ed25519 SSH local config
  19. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    SSH client ready? • Windows: https://www.chiark.greenend.org.uk/ ~sgtatham/putty/latest.html • Optionally, install openssl locally • Windows: https://wiki.openssl.org/index.php/ Binaries Are you sitting comfortably?
  20. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    ssh ubuntu@<hostname>.hakr.site • mosh ubuntu@<hostname>.hakr.site • tmux ( fi rst time) • tmux at (reconnect) • Demo Connect!
  21. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    "Substitute user do" • Run commands as any user, but most usefully root • May require your password • Unlike su, which needs root's password • Con fi gured in /etc/sudoers and /etc/sudoers.d/ • Run a command as root with sudo <command> • Become root with sudo su - Sudo
  22. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    ufw reset • ufw default deny • ufw limit from any to any app OpenSSH • ufw enable • ufw status Firewall
  23. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Hardening

    SSH • Run ssh-audit <your host name> • less /etc/ssh/sshd_config • nano /etc/ssh/sshd_config.d/harden.conf • sshd -t • systemctl restart ssh
  24. Marcus Bointon Full-stack PHP security – IPC Munich 2025 fail2ban

    • Scans authentication log fi les • Creates fi rewall blocks for persistent failures • Can work with any auth system • Con fi gured for SSH by default • less /etc/fail2ban/jail.local • fail2ban-client status sshd • fail2ban-client unban <ip>
  25. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    apt update • apt list -- upgradeable • apt upgrade or apt full-upgrade • apt install unattended-upgrades Updating system packages
  26. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Running

    nginx • nginx is already installed from the o ffi cial nginx-mainline package archive • Shell script for this workshop: vhost • Symlinks fi les from /etc/nginx/sites-available to /etc/nginx/ sites-enabled • Checks nginx con fi g and reloads it • Run vhost 0 • Visit your site in a browser http: // <hostname>.hakr.site/ • ufw allow proto tcp from any to any port 80 • ufw allow proto tcp from any to any port 443
  27. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Transport encryption – TLS • Content encryption – GPG email • Signatures – ACME, apt • Obfuscation – ACME, UUIDs Securing services
  28. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    The Transport Layer Security Protocol • The "new" name for Secure Socket Layer (SSL) • Since 1999! • A set of standards for security & encryption • Can wrap around any higher-level protocol • HTTP, SMTP, FTP, IMAP, etc • Popular implementations: OpenSSL, LibreSSL, BoringSSL What is TLS?
  29. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    SSLv2: 1995 – 2011 • SSLv3: 1996 – 2015 • TLSv1.0: 1999 – 2020 • TLSv1.1: 2006 – 2020 • TLSv1.2: 2008: AEAD ciphers, SHA256 • TLSv1.3: 2018: ECC ciphers, 1/0-RTT, PFS, no more bad stuff! TLS history
  30. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Protocols • Hashes • MACs • Ciphers • Key exchange What's in TLS?
  31. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Provides con fi dentiality, authenticity, and integrity • Better performance with HTTP/2 & HTTP/3 • Increasingly required by integrations, e.g. iOS and Android app back-ends Why use TLS?
  32. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Key

    exchange analogy Alice Bob Random colour Secret colour + + + + = = = = Secret colour Common secret Exchange intermediate colours
  33. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Removal of weak and legacy algorithms & extensions • More encryption • Lower handshake overhead • PFS-only • Elliptic curve ciphers as standard • Downgrade protection Changes in TLS 1.3
  34. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    RSA (Rivest-Shamir-Adleman): wide support, old, big, slow • EC (Elliptic Curve): new, small, fast, strong • Keys are general purpose – not just for HTTPS • Encrypt local fi les • Digital signatures TLS keys
  35. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    umask 077 • RSA key • openssl genrsa -out key.pem 4096 • EC key • openssl ecparam -name secp384r1 -genkey -noout -out eckey.pem Making TLS keys
  36. Marcus Bointon Full-stack PHP security – IPC Munich 2025 RSA

    private key -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAsD4rCXPQrhPmw6StcgRZTD7Q004mia8JPRlIQ6Q0mkZB4gaF /J7kQjiX5MqwDeBPg+6cUMbHB/uwIdndrr8WIZAkWBY1GUWavpohS+Ah/+DqaS7U ... 2v9RAB6pNWoiZ7UAyy+MY2QzjOK2ZNva8HyndLdIdL5qPHjtDYeMG6YOOuw3OUNy B4AhFmccLM/QfDWKsb0mV1OmyhcBxLa/f6cJOtYxxTvQguiq1xo= -----END RSA PRIVATE KEY-----
  37. Marcus Bointon Full-stack PHP security – IPC Munich 2025 EC

    private key -----BEGIN EC PRIVATE KEY----- MHcCAQEEIAiwMhnSMFg4Bsk2QFMpeUHwbaLWDKtRYBFB+tHOknURoAoGCCqGSM49 AwEHoUQDQgAELnUTiNjDHYOQfnJOR33djPYrECy4HIyGREwN0GbexQ/abYknG1hn 8SVq0+6bj+/F1IeP7iiWp2MXhgU+X6Tv8Q== -----END EC PRIVATE KEY-----
  38. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Derived from existing private keys • RSA key • openssl rsa -in key.pem -outform PEM -pubout -out public.pem • ECC key • openssl ec -in eckey.pem -pubout -out ecpublic.pem • PEM header same for both: -----BEGIN PUBLIC KEY----- Public keys
  39. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Certi fi cate signing request — CSR • openssl req -new -key key.pem -out www.example.com.csr • answer questions • ...or use a con fi g fi le • Inspect contents of CSR • openssl req -noout -text -in www.example.com.csr Requesting a certificate
  40. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Self-signed: • openssl x509 -req -days 365 -in www.example.com.csr -signkey key.pem -out cert.pem • Inspect the cert: • openssl x509 -text -noout -in cert.pem Signing a certificate
  41. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    CA public certs (known as root certificates) are installed in browsers and OSs, updated frequently • All root certs are self-signed, but are trusted • Can't sign locally — we don't have the CA's private key • Need to send CSR to CA — web or email • Can verify using public key Signing a certificate
  42. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Self-signed == no chain, no authority, no trust • But will still encrypt exactly the same way • CAs have trusted self-signed roots • Intermediate certificates • May be cross-signed Certificate trust chains
  43. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Certificate

    trust chains Intermediate CA certi fi cate Browser & OS certi fi cate store Root CA certi fi cate Site certi fi cate Server certi fi cate store Root CA certi fi
  44. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    echo | openssl s_client -connect www.apple.com:443 2>/dev/null | openssl x509 Obtaining a public key
  45. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    CAs post issued certi fi cates to a public transparency log • Browsers now require CT • Security risk — anyone can watch the logs and fi nd newly-con fi gured servers, so fi x con fi g fi rst! • But the same mechanism helps defend against phishing Certificate Transparency (CT)
  46. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Revoked certs are added to a global CRL which browsers download frequently • The size of this list was growing exponentially, impossible to sustain • OCSP replacement was too hard on CAs • Shorter lifetimes – expired certs don't need revocation Certificate Revocation Lists
  47. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Certi fi cate Authority Authorisation DNS records — CAA • Simply names your permitted CAs in your DNS • HTTP Public Key Pinning — HPKP • Di ffi cult, prone to error, potential to cut your site o ff for months! • DO NOT USE! Certificate authority protection
  48. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Free TLS certi fi cates since 2015! • ACME protocol: Automated Certificate Management Environment • certbot – EFF's o ffi cial LetsEncrypt client • acme.sh, acme-tiny.py, boulder (go) • Integrated: Apache mod_md, Caddy • Domain Validation ("DV") • DNS, web root, web module — only over http! Let's Encrypt
  49. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Certbot

    setup mkdir -p /var/www/letsencrypt chown www-data:www-data /var/www/letsencrypt vhost 1
  50. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Nginx

    verification config server { listen 80; listen [::]:80; server_name neo.hakr.site; root /var/www/html; index index.html; location ^~ /.well-known/acme-challenge { default_type text/plain; root /var/www/letsencrypt; } }
  51. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Requesting

    a certificate certbot certonly -w /var/www/letsencrypt -d <hostname>.hakr.site -- post-hook 'systemctl reload nginx' Shortcut: ./requestcert.sh
  52. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Using

    the certificate server { listen 443 ssl; listen [::]:443 ssl; http2 on; ... ssl_certificate /etc/letsencrypt/live/<hostname>.hakr.site/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/<hostname>.hakr.site/privkey.pem; location / { try_files $uri $uri/ index2.html; } }
  53. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Redirect

    to secure site server { ...# As before in port 80 section location / { return 301 https://$host$request_uri; } }
  54. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Enable

    the secure site vhost 2 Check the site in your browser
  55. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    We're using a test certificate, so we get an error • But we can ignore that because we know why • Everything else will still work What's the problem?
  56. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    https://www.ssllabs.com/ssltest/ • This takes a while! • We are using an untrusted certi fi cate! • But all other tests are still valid and useful • CLI alternative: https://testssl.sh Testing TLS
  57. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Select protocols & ciphers, e.g. for non-legacy browsers & apps: • ssl_protocols TLSv1.2 TLSv1.3; • ssl_prefer_server_ciphers on; • ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256 : ECDHE-RSA- AES128-GCM-SHA256 : ECDHE-ECDSA-AES256-GCM-SHA384 : ECDHE- RSA-AES256-GCM-SHA384 : ECDHE-ECDSA-CHACHA20- POLY1305 : ECDHE-RSA-CHACHA20-POLY1305 : DHE-RSA-AES128- GCM-SHA256 : DHE-RSA-AES256-GCM-SHA384 : DHE-RSA-CHACHA20- POLY1305; • Mozilla's "intermediate" con fi g Tightening nginx TLS config
  58. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Tightening

    TLS config vhost 3 Check the site in your browser
  59. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Further

    TLS tightening # Generate and use custom strong DH params # First generate the params; Warning: VERY slow! openssl dhparam -out /etc/letsencrypt/dhparams4096.pem 4096 # Then config in nginx ssl_dhparam /etc/letsencrypt/dhparams4096.pem; # Use strong curves for ECDHE key exchange ssl_ecdh_curve X25519:secp384r1:prime256v1:X448;
  60. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Further

    TLS tightening vhost 4 Check the site in your browser
  61. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    A really important header • Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" • Tells browsers to use HTTPS only for your domain until the max-age expires • Even if you include any http URLs — it will convert them (+ CSP) • preload solves the Trust On First Use problem (TOFU) Strict Transport Security
  62. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    X-Content-Type-Options: nosniff • X-XSS-Protection: "0" • X-Frame-Options: SAMEORIGIN • Referrer-Policy: no-referrer • Permissions-Policy interest-cohort=(), midi=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), accelerometer=(), fullscreen=(self), payment=(), geolocation=(), usb=(), encrypted- media=() HTTP security headers
  63. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Content-Security-Policy: "default-src 'none'; script-src 'self' 'unsafe-inline' code.jquery.com; connect-src 'self'; img- src 'self'; style-src 'self'; font-src 'self'; upgrade-insecure-requests; report-to csp-reports" • Lets you control where resources come from • Can block many XSS and malware vectors Content Security Policy
  64. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    https: // report-uri.com • Somewhere to send reports generated by CSP, etc • https: // securityheaders.com • For testing security headers separately from TLS con fi g Header testing tools
  65. Marcus Bointon Full-stack PHP security – IPC Munich 2025 vhost

    5 Check the site in securityheaders.com Testing secure headers
  66. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Issue

    a real certificate edit /etc/letsencrypt/cli.ini # Comment out the "staging" line with a #, or delete it # Request a new certificate certbot certonly -w /var/www/letsencrypt -d <hostname>.hakr.site -- post-hook 'systemctl reload nginx' Shortcut: ./requestrealcert.sh
  67. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Stricter

    TLS for admin sites server { # Limit protocols ssl_protocols TLSv1.3; # Allow the client to choose the cipher suite ssl_prefer_server_ciphers off; # Don't need to specify cipher suites # because all in TLSv1.3 are good # ssl_ciphers ... }
  68. Marcus Bointon Full-stack PHP security – IPC Munich 2025 TLS

    for admin sites vhost 6 Check the site using testssl.sh
  69. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Every time they issue a cert, CAs publish that they have done so • Search for your hostname or domain • https://crt.sh • https://ctlogsearch.com/ • Hackers like this too Certificate Transparency Logs
  70. Marcus Bointon Full-stack PHP security – IPC Munich 2025 QUIC

    & HTTP/3 – nginx server { listen 443 ssl; listen [::]:443 ssl; listen 443 quic; listen [::]:443 quic; http2 on; http3 on; add_header Alt-Svc 'h3=":443"; ma=86400'; ... vhost 7
  71. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    ufw allow proto udp from any to any port 443 • ufw status • Also need to allow in cloud provider security group QUIC & HTTP/3 – firewall
  72. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    https://http3check.net/ • https://github.com/pd4d10/http-indicator QUIC & HTTP/3 – testing
  73. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Qualy SSL Labs: https://www.ssllabs.com/ssltest/ • TestSSL.sh: https://testssl.sh • TLS con fi g generator: https://ssl-con fi g.mozilla.org • Mozilla Observatory: https://observatory.mozilla.org • Privacy checker: https://webbkoll.5july.net/ • SSH audit: https://github.com/jtesta/ssh-audit Testing & config tools
  74. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    PHP has SAPIs – server APIs • cgi – old and slow • mod_php for Apache – easiest, but slow • cli – for command line tools like composer • fpm – fastcgi process manager – fast • FrankenPHP – new and faster, but not today! Configuring PHP
  75. Marcus Bointon Full-stack PHP security – IPC Munich 2025 PHP

    service structure Browser Reverse proxy nginx PHP service php-fpm Server pool1 pool2 app1 app2 app3
  76. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    /etc/php/8.4/fpm/pool.d/www.conf • listen = /run/php/php8.4-fpm.sock • /status – show PHP-FPM connection stats • /ping – healthcheck, responds with pong PHP-FPM pool config
  77. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Connect

    PHP to nginx upstream php84 { server unix:/run/php/php8.4-fpm.sock; } location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; if (!-f $document_root$fastcgi_script_name) { return 404; } fastcgi_pass php84; } location /status { fastcgi_pass php84; } location /ping { fastcgi_pass php84; }
  78. Marcus Bointon Full-stack PHP security – IPC Munich 2025 vhost

    8 Check the site in your browser Testing PHP config
  79. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    We now have a nicely secured server! • Strict cloud and local fi rewalls • Strong SSH con fi g • Strong TLS con fi g • Web service over HTTP/3 • HTTP security headers • PHP working Time to celebrate!
  80. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Outdated dependencies • CSP revisited • Sanitization, Validation, Escaping • SQL injection • Authentication • Cookies • Authorization Back-end application security
  81. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Application packages • composer audit • composer update • Not usually on deployed apps • Run locally, updates composer.lock, commit and redeploy Outdated PHP dependencies
  82. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Pre-emptive updating • composer require -- dev roave/security- advisories:dev-latest • Declares con fl icts with packages containing known vulnerabilities Outdated PHP dependencies
  83. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Content-Security-Policy: "default-src 'none'; script-src 'self' 'unsafe-inline' code.jquery.com; connect-src 'self'; img- src 'self'; style-src 'self'; font-src 'self'; upgrade-insecure-requests; report- to csp-reports" • What does this mean? • Open dev tools, visit /csp.php in your domain Content security policy (CSP)
  84. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    report-uri param lets you know when contraventions happen • https: / / report-uri.com Demo • Content-Security-Policy-Report-Only for testing • Reporting-Endpoints: "csp-reports=https: / / hakr.report-uri.com/r/d/csp/enforce" • Can also handle in your own apps CSP reporting
  85. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Sanitize • Remove anything unexpected • e.g. non-numeric values, HTML markup • Validate • Check that inputs conform to expected formats • Can include deeper checks, like existence in DB Sanitization & validation
  86. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Sanitization

    & validation //Sanitization $phone = substr(preg_replace('/[^+\d]/, '', $_POST['phone']), 0, 20); $description = substr(strip_tags($_POST['description'), 0, 255); $email = substr(preg_replace('/[^a-zA-Z\d!#$%&'@*\.+\/=?^_`{|}~-]/', '', $_POST['email']), 0, 255); //Validation $email_valid = filter_var($email, FILTER_VALIDATE_EMAIL); $phone_valid = preg_match('/^[+0]\d{10,15}$/'); See /validation.php
  87. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Context is everything • URL • HTML • email • shell – which shell? what encoding? • SQL – which fl avour? Escaping
  88. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    URL • rawurlencode($text) • Shell argument • escapeshellarg($text) • HTML • htmlspecialchars($text) • SQLite • SQLite3 :: escapeString($text) • MySQL • mysqli_real_escape_string($mysql, $text) Escaping in PHP
  89. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    idor.php?id=5 • What's at idor.php?id=4 ? • My data, or someone else's? • Insecure Direct Object Reference: IDOR • Internal object IDs, fi le names • Apply authorisation checks, ownership • Obfuscate with unguessable UUID / HashID / ULID Enumeration and IDOR
  90. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Who are you? • Are you allowed to do this? • Passwords • Source IP • OAuth 2.0 • Webauthn & PassKeys Authentication & Authorisation
  91. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    if ($_POST['password'] === 'mypassword') . .. • Timing side channel • Example: timing.php • Use constant-time comparisons • Passwords
  92. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Use a password hash function • Not a generic hash like MD5, SHA256, Blake2b • bcrypt, Argon2id, PBKDF2 • Still subject to timing side-channels • PHP password_hash, password_verify, sodium_crypto_pwhash_str, hash_equals Password hashing
  93. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Numerous fl ows including: • Client credentials ( fi xed ID and password) • Password ( fi xed client with dynamic ID & pass) • Authorisation code (social login) OAuth 2.0
  94. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Really a framework feature • Principle of least responsibility • Only allow what needs to be allowed • Roles and abilities – RBAC • Assign roles, check abilities Authorisation
  95. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    A very speci fi c escaping case... • with very bad consequences • /sqli.php?id=1 • $q = 'SELECT * FROM items WHERE id=' . $_GET['id']; • Loop over results, display in table SQL injection
  96. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /sqli.php • Expecting input to be a number, but what if we give something else with meaning in SQL? • Attack string 1 OR 1=1 results in this query: • SELECT id, name FROM items WHERE id=1 OR 1=1 • Edit the fi le at /var/www/html/sqli.php SQL injection
  97. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Explicit escaping • $result = $db - > query('SELECT * FROM items WHERE id=' . $db - > quote($_GET['id'])); • Parameter binding • $st = $db - > prepare('SELECT * FROM items WHERE id=?'); $st -> bindValue(1, $_GET['id'], SQLITE3_NULL); $result = $st - > execute(); SQL injection – escaping
  98. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Avoid manual SQL operations • Use your Framework's ORM to provide abstraction • Test your app using https://sqlmap.org/ SQL injection
  99. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Allowing attackers to inject malicious scripts into web pages • Di ff erent fl avours • Re fl ected • Stored • Defend by sanitising, validating, escaping, and CSP Cross-site scripting (XSS)
  100. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /xss.php • Attack string: <script>alert(1) </ script> • Type that into the "unsafe" input and submit it • Now try the same on the "safe" input Reflected XSS in PHP
  101. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /xss.html • Attack string <script>alert(1) < / script> • Type that into the "unsafe" input and submit it • Now try using • <img src='x' onerror='alert(1)'> • Now try the same on the "safe" input Reflected XSS in Javascript
  102. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Unsafe: • previewElement.innerHTML = userInput • Safe: • previewElement.textContent = userInput • Can also write your own escaping function • Use your framework Reflected XSS in Javascript
  103. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /sqli.php • Attack string: <script>alert('boom') </ script> • Search for the new ID • The sqli.php script has two problems! Stored cross-site scripting
  104. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    A form on a site can receive submissions from other locations • If the user that sends it is not logged in, they will not be able to submit it... • But they could trick you into submitting it Cross-Site Request Forgery (CSRF)
  105. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /csrf.php • Click log in – login state saved in a cookie • Now we can submit entries that are added to the DB CSRF example
  106. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Copy the URL (contains form data) • Open an incognito window with this URL • Note that it fails as you're not logged in • Edit the URL to contain a new value • Open a new window (non-incognito) • Visit the URL – the amended value is submitted • Try the same thing using your neighbour's site CSRF example – attacker
  107. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Every page load, generate a random token and expiry time and save it in the DB • Insert the token value into a hidden input tag • On submit, check that the token exists and has not expired, otherwise reject the request • After handling submission, delete the token in the DB CSRF defence: tokens
  108. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /csrfsafe.php • Look at source code CSRF defence: tokens
  109. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    CSRF can work because cross-origin requests will include cookies • Even if the link was presented somewhere else • SameSite cookies fi x this • Sec-Fetch-Site: same-origin • Set automatically by browsers if origin matches Other CSRF defences
  110. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Open dev tools to the storage tab • Visit /cookies.php • Cookie fl ags: • HttpOnly – not visible to JS • Secure – only over HTTPS • SameSite – only to current domain Cookies
  111. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    _ _ Secure- • Must have Secure fl ag • Only accepted over HTTPS • Set-Cookie: _ _ Secure-SESSIONID=MYSESSION; Secure • _ _ Host- • Must not set domain; will only match exactly • Path must be / • Set-Cookie: _ _ Host-SESSIONID=MYSESSION; Path=/; Secure Cookie Prefixes
  112. Marcus Bointon Full-stack PHP security – IPC Munich 2025 Set-Cookie:

    __ Host-SESSIONID=MYSESSION; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age=3600; Expires=Fri, 31 Oct 2025 18 : 00 : 00 GMT A Cookie With Everything
  113. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Cause a server to fetch something from a URL provided by an attacker • e.g. fetch logo image, pull RSS feed, play a music track • Open to abuse, need to be careful • Local fi le paths: /etc/passwd • Polyglot images, malicious PDFs • Resource consumption – DoS • Server may have access to other non-public servers Server-Side Request Forgery (SSRF)
  114. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Minimal PHP example: • echo file_get_contents($_GET['url']); • Visit /ssrf.php • Defend by strict validation • Block URLs for internal hosts SSRF example
  115. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /framed.html • Visit /clickjacking.html • Looks the same until you click it • Edit clickjacking.html, point it at your neighbour's site • Defend with X-Frame-Options: SAMEORIGIN or CSP frame-ancestors: 'self' Framing attacks – Clickjacking
  116. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Extends same-origin policy selectively e.g. • App on www.example.net • API on api.example.com • "You are allowed to see this response" • Only applies to scripted requests • May require pre- fl ight OPTIONS requests • Insecure: Access-Control-Allow-Origin * • Access-Control-Allow-Methods: GET Cross-Origin Resource Sharing
  117. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Visit /cors.html • Click the buttons; inspect the responses • Still works because of same origin rule • Now enter your neighbour's hostname and retry CORS example
  118. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    Delegate everything possible to the framework • Reduces code you are responsible for • Improves quality – Framework code gets checked a lot, should have very good test coverage too • Need something slightly di ff erent? • Build it and make a PR so all can bene fi t Trust your framework
  119. Marcus Bointon Full-stack PHP security – IPC Munich 2025 •

    This all sounds like a lot, and it is • You can't anticipate everything • but you might be able to do enough • Security is a moving target • Practice defence in depth Final notes