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)
| id | username | role |
|---|---|---|
| 1 | alice | client |
| 2 | bob | staff |
| 3 | admin | admin |
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
}Cookies set
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.
| Endpoint | As “client” |
|---|---|
| GET /api/v1/health | allow |
| GET /api/v1/services | allow |
| POST /api/v1/enquiries | allow |
| GET /api/v1/auth/me | allow |
| GET /api/v1/portal/engagements | allow |
| GET /api/v1/admin/enquiries | deny |
| POST /api/v1/admin/invoices | deny |
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" →