Skip to content

Security

Security that protects your business, not just your data

For commercial buyers, security is risk, liability and reputation. Here’s how that’s handled — and below, you can poke at the real mechanisms.

GDPR readiness

Data-protection-by-design: lawful-basis mapping, minimisation, subject-rights handling and a documented position you can show a regulator.

Penetration testing

Applications built to withstand testing — and structured so an external pen test finds hardening, not open doors.

OWASP audit

Delivery aligned to the OWASP Top 10: injection-safe data access, secure auth and session handling, and hardened headers.

How this discipline is applied to client work

The Secure Delivery Playbook sets out the five principles and the delivery lifecycle behind every build — and the evidence pack you hold at the end.

Security Lab

Three interactive demos showing how this platform defends itself — the same techniques used across every build. Behind them: access + refresh JWTs in httpOnly cookies with rotation, TOTP two-factor authentication, and RBAC that re-checks per-record ownership in the service layer.

These demonstrations run against this site's own instrumented endpoints. Never apply these techniques to systems you are not authorised to test — in Ireland that is a criminal offence. See our legal notice.

SQL injection — concatenation vs parameterized

Pick an attacker-supplied “username”, then flip how the query is built. The result runs against an in-memory mock users table.

SELECT id, username, role FROM users WHERE username = '' OR '1'='1';

BREACH — injection succeeded

The injected condition is always true, so every row is returned — including admin. Authentication is bypassed.

Rows returned (3)

idusernamerole
1aliceclient
2bobstaff
3adminadmin

JWT lifecycle — access + refresh in httpOnly cookies

Credentials verified (bcrypt cost 12). The server signs a 15-minute access token and a 7-day refresh token and sets both as httpOnly cookies.

Access token payload

{
  "sub": "user-uuid",
  "role": "client",
  "email": "alice@acme.ie",
  "iat": 1700000000,
  "exp": 1700000900
}
Access token TTL100%

Cookies set

access_token
HttpOnly: trueSameSite: LaxSecure: prod onlyMax-Age: 15m
refresh_token
HttpOnly: trueSameSite: LaxSecure: prod onlyMax-Age: 7d
Step 1 of 5

RBAC explorer — who can call what

Switch the caller’s role and watch the allow/deny matrix update — exactly how this app’s authenticateToken + requireRole guards decide.

As client: 5 of 7 endpoints allowed.

EndpointAs “client”
GET /api/v1/healthallow
GET /api/v1/servicesallow
POST /api/v1/enquiriesallow
GET /api/v1/auth/meallow
GET /api/v1/portal/engagementsallow
GET /api/v1/admin/enquiriesdeny
POST /api/v1/admin/invoicesdeny

Guards applied

// Public route — no guard
router.get('/services', list)

// Authenticated route
router.get('/auth/me',
  authenticateToken,            // ✓ token valid
  me)

// Role-gated route (double-gated)
router.post('/admin/invoices',
  authenticateToken,
  requireRole('client', 'staff', 'admin'),
  create)
// caller role = "client" →