iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

A01 Broken Access Control

A01:2021 — Broken Access Control — is the top OWASP risk. The bug class is “the server let a user do something they shouldn’t”: IDOR, missing function-level checks, vertical/horizontal privilege escalation.

Authz patterns + IDOR defences

EXAMPLE
// 1) ANTI-PATTERN — trusting the client
app.get('/orders/:id', async (req, res) => {
    const order = await db.orders.findOne({ id: req.params.id });
    res.json(order);     // ANYONE can read ANY order by guessing IDs
});

// 2) FIX — always check ownership on read
app.get('/orders/:id', requireAuth, async (req, res) => {
    const order = await db.orders.findOne({ id: req.params.id });
    if (!order || order.userId !== req.user.id) {
        return res.status(404).end();      // 404, not 403 — don't reveal existence
    }
    res.json(order);
});

// 3) Or include the user in the WHERE clause (better — atomic)
app.get('/orders/:id', requireAuth, async (req, res) => {
    const order = await db.orders.findOne({
        id:     req.params.id,
        userId: req.user.id,                // scoped query
    });
    if (!order) return res.status(404).end();
    res.json(order);
});

// 4) Vertical privilege check — role-based
function requireRole(role) {
    return (req, res, next) => {
        if (req.user?.role !== role) return res.status(403).end();
        next();
    };
}
app.delete('/admin/users/:id', requireAuth, requireRole('admin'), deleteUser);

// 5) Resource-based policies (Casbin / ABAC)
const can = (user, action, resource) =>
    enforcer.enforce(user.role, resource.type, action);

app.put('/posts/:id', requireAuth, async (req, res) => {
    const post = await db.posts.findOne({ id: req.params.id });
    if (!post || !can(req.user, 'edit', post)) return res.status(403).end();
    // ...
});

// 6) Object-IDs that don't leak
// BAD:   /orders/1, /orders/2 → IDORs become a numeric increment
// GOOD:  /orders/ord_8f3a91... → UUIDv4 / KSUID / NanoID
// BEST:  Still verify ownership server-side, regardless of how unguessable the ID looks

// 7) Mass-assignment — restrict editable fields
// BAD:
await db.users.update({ id }, req.body);            // role: 'admin' lands here

// GOOD:
const { name, email } = req.body;                    // pluck allowed fields
await db.users.update({ id }, { name, email });
// Better: Zod / Pydantic / DTO schema

// 8) GraphQL — every resolver needs auth
const resolvers = {
    Query: {
        order: async (_, { id }, ctx) => {
            const order = await db.orders.findOne({ id });
            if (!order || order.userId !== ctx.user.id) return null;
            return order;
        },
    },
};

// 9) Deny by default
// Every route is private unless explicitly marked public.
const PUBLIC = new Set(['/health', '/login', '/signup', '/csrf']);
app.use((req, res, next) =>
    PUBLIC.has(req.path) ? next() : requireAuth(req, res, next));

// 10) Tests — write authz tests AS REGRESSIONS
describe('orders authz', () => {
    it('returns 404 for orders the user doesn\'t own', async () => {
        const me   = await signIn('me@example.com');
        const them = await createOrder('them@example.com');
        const r = await me.get(`/orders/${them.id}`);
        expect(r.status).toBe(404);
    });
});

// 11) Audit log every privileged action
await audit.log({
    actor:    req.user.id,
    action:   'delete_user',
    target:   targetUserId,
    request:  { ip: req.ip, ua: req.headers['user-agent'] },
    at:       new Date(),
});

Why it matters

Authz is the most-tested area in mature codebases — one test per route per role. Deny-by-default + scoped queries + audit logs catch the bugs that ship anyway.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// A01 Broken Access Control — the #1 risk.
// Examples: IDOR (/orders/42 → other users' orders), missing role checks,
// trusting client-provided role.
// Fix: deny by default; enforce on the server; tests for every protected route.
Try it Yourself »

Exercise

OWASP Top 10 (2021) category #1 short name.

Broken Control

Test yourself

Q1. A01 is…
Q2. A typical example is…
Q3. The fix is to…

Discussion

Loading…