Bonjour!
Who Let the Cats Out?
Solving the Authorization Mystery with ReBAC
🐱
HalfStack Vienna • September 2025
Bonjour! 👋
**Edouard Maleix** Engineering @ GetLarge Cat enthusiast & authorization detective 🕵️
AGENDA
→ OWASP API Security Risks (with cats!)
― Live Exploitation Demos
― The Authorization Horror Story
― Enter ReBAC: Relationships > Roles
― PurrfectSitter Demo with OpenFGA
Conference & Cat Edition 🐱
OWASP API Security Top 10
API1:2023 - Broken Object Level Authorization
**"Taking Someone Else's Stuff"** 🎒
**Conference example:** ✓ You're a speaker (correct role) ✗ But you can't take MY backpack! Just because you can access `/api/backpacks/` doesn't mean you can access `/api/backpacks/mine`
// Vulnerable: Only checks if user is a sitter
GET /api/cats/romeo/health-updates
Authorization: Bearer
// Anne is a sitter, but is she Romeo's sitter? 🤔
API5:2023 - Broken Function Level Authorization
**"Wrong Time Slot, Wrong Stage"** ⏰
**Conference example:** ✓ You're a speaker (correct role) ✗ But it's NOT your time slot (wrong context) *"Hey, I'm also a speaker!" waves from audience* Audience: "Wait, two speakers?"
// Anne tries to post updates at 3 AM when her sitting ended at 6 PM
POST /api/sittings/123/updates
Authorization: Bearer
// Is Anne an ACTIVE sitter right now?
Breaking PurrfectSitter 🔴
Live Exploitation Demos
🔴 LIVE: The Curious Case of the Wrong Cat Owner
**Setup:** Bob owns Romeo, Anne is a sitter
// Vulnerable endpoint
app.get('/api/cats/:catId', authenticateUser, async (req, res) => {
const { catId } = req.params;
// ❌ Only checks if user is authenticated, not if they own this cat
const cat = await db.cats.findById(catId);
res.json(cat);
});
🎯 **Audience participation:** "What cat IDs should we try?"
Enter ReBAC 🌟
There Has To Be A Better Way!
What If We Could Express This Naturally?
Bob **owns** Romeo Anne **sits** Romeo Jenny **administers** system
↓
user:bob → owner → cat:romeo
user:anne → sitter → cat:romeo
user:jenny → admin → system
Welcome to **ReBAC** *Relation-Based Access Control*
From 127 Lines to This
**The Model:**
type cat
relations
define owner: [user]
define admin: admin from system
define active_sitter: [cat_sitting#sitter with is_active]
define can_update: owner or admin or active_sitter
**The Check:**
const canUpdate = await fga.check({
user: `user:${userId}`,
relation: 'can_update',
object: `cat:${catId}`,
context: { current_time: new Date().toISOString() }
});
*"Raise your hand if you'd rather debug 6 lines than 127 lines at 3 AM 🙋"*
Who Let the Cats Out?: Solving the Authorization Mystery with ReBAC
By edouard_maleix
Who Let the Cats Out?: Solving the Authorization Mystery with ReBAC
HalfStack Vienna - September 2025
- 10