跳到正文
cccskills
免费在 GitHub 公开

access-control-and-idor

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md7.6 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Access Control and IDOR

Overview

Broken access control is the most common serious finding in real diffs, and it rarely looks dangerous: there is no exec, no string-built query — just a lookup by id with nothing after it. You find it by asking one question of every entry point the diff touches:

"If user A sends user B's id, what stops it?"

If the answer is not a line of code you can point at, it is a finding.

1. Object-Level Authorization (BOLA / IDOR — CWE-639, CWE-862)

Enumerate every id the new code accepts: path, query, body, header, nested in a JSON object, inside a batch array, in a GraphQL argument, in a WebSocket message. Follow each to the data layer.

// ❌ any authenticated user reads any invoice
inv, err := h.repo.Get(ctx, r.PathValue("id"))

// ✅ scoped to the caller's organisation in the query itself
inv, err := h.repo.GetForOrg(ctx, r.PathValue("id"), auth.OrgID(ctx))
// ❌ Spring: findById alone, the principal is never consulted
@GetMapping("/orders/{id}") fun get(@PathVariable id: UUID) = repo.findById(id)

// ✅ ownership is part of the lookup
@GetMapping("/orders/{id}") fun get(@PathVariable id: UUID, @AuthenticationPrincipal u: User) =
    repo.findByIdAndCustomerId(id, u.customerId) ?: throw NotFound()

Check the second id too: an endpoint that verifies the project in the path, then updates the task whose id is in the body, without checking the task belongs to that project.

// ❌ project ownership checked, task id from the body never tied to it
await assertProjectOwner(req.params.projectId, user.id);
await db.task.update({ where: { id: req.body.taskId }, data: { title } });

// ✅ the task is looked up inside the checked project
await db.task.update({ where: { id: req.body.taskId, projectId: req.params.projectId }, data: { title } });

2. Function-Level Authorization (BFLA — CWE-285, CWE-863)

A privileged action needs a role check, not just "logged in".

  • Spring Security: a new requestMatchers(...).permitAll() or .authenticated() on an admin path; a @PreAuthorize removed from a method; a check on the URL that a second mapping of the same handler bypasses.
  • Quarkus: a new resource method with no @RolesAllowed where its class's siblings have one; @PermitAll added.
  • Express/Nest/Fastify: a route registered before the auth middleware, or on a router that never mounts it.
  • Django/DRF: permission_classes = [AllowAny], a view missing @login_required beside decorated siblings.
# ❌ DRF: a staff-only action reachable by any authenticated user
@action(detail=True, methods=["post"])
def refund(self, request, pk=None): ...

# ✅ the action carries the same permission as the admin viewset
@action(detail=True, methods=["post"], permission_classes=[IsAdminUser])
def refund(self, request, pk=None): ...

3. Property-Level Authorization (mass assignment — CWE-915; excessive exposure — CWE-213)

Binding a request straight onto an entity lets the client set fields it should not: role, isAdmin, ownerId, orgId, price, emailVerified.

// ❌ Mongoose: whatever the client sends becomes the document
const user = await User.create(req.body);

// ✅ an explicit allowlist of client-settable fields
const { name, email } = req.body;
const user = await User.create({ name, email });

Equivalents: Django ModelForm / DRF serializer with fields = "__all__"; Spring @ModelAttribute or @RequestBody on a JPA entity; Rails params.permit!; Pydantic/request DTO that includes role; Go json.Decode into the domain struct. The response side is the mirror: returning the entity serialises passwordHash, internal flags or other users' rows in a list.

4. Tenant Scoping

In multi-tenant code, every read and write needs the tenant predicate — including the ones people forget:

  • list/search/export/count endpoints and their pagination cursors;
  • background jobs and queue consumers that receive an id from a message;
  • caches keyed by object id without tenant (one tenant warms, another reads);
  • object storage keys built from a user-supplied filename;
  • GraphQL nested resolvers (the parent was checked, the child field loads by raw id);
  • batch endpoints taking ids: [...] — each id must be checked, not the first.

5. Failing Open (CWE-280, OWASP A10:2025)

// ❌ a lookup error is treated as "no restriction"
perms, err := h.authz.For(ctx, userID)
if err == nil && !perms.CanDelete { return ErrForbidden }
return h.repo.Delete(ctx, id)

// ✅ any error denies
perms, err := h.authz.For(ctx, userID)
if err != nil || !perms.CanDelete { return ErrForbidden }

Also: a switch on role with a permissive default; a feature flag that disables the check when unset; an inverted comparison.

6. Parallel Paths to the Same Effect

The check on the route the diff adds may be right while the same state change stays reachable another way with a weaker one. For each protected effect the diff touches, list every door: the REST route and its GraphQL mutation, the v1 and v2 handlers, the single-item and the bulk endpoint, the import file, the admin tool, the background job, the WebSocket message, the agent tool. Each needs the same permission on the same resource — not authentication alone, and not a permission on a different object. Also ask whether a field in the body can override what the permission system restricts ("orgId", "ownerId", "role" in an update body) and whether bulk, export and import enforce the check per item. Tenant scoping across copies and lifecycle — caches, search, exports, soft-delete, revocation — is data-isolation-and-lifecycle.

Severity Guide

  • Reading or changing another tenant's or user's data → HIGH; CRITICAL when unauthenticated, when it reaches credentials or payment data, or when it is admin takeover.
  • Exposure of non-sensitive fields of another user → MEDIUM.

Common Mistakes

  • Reporting "ids are guessable": UUIDs are not, and guessability is not the bug — the missing check is. A sequential id with a correct check is fine.
  • Missing a check that exists one layer down: read the repository method; GetForOwner may already scope the query.
  • Reporting a missing check in client code — find the server endpoint and judge that.
  • Treating 403 vs 404 as a vulnerability. Follow the repo's convention; it is not your finding.

Red Flags

  • WHERE id = $1 with no second predicate on a caller-scoped resource.
  • findById, get_object_or_404(Model, pk=pk), Model.findByPk(id) returning straight to the response.
  • A new route that its neighbours' middleware chain does not cover.
  • A request DTO that is also the entity.
  • An authorization helper whose error path returns nil, true or falls through.

References (names and links only)

OWASP API Security Top 10 2023 — API1 BOLA, API3 BOPLA, API5 BFLA · OWASP Top 10:2025 A01 Broken Access Control · ASVS 5.0 V8 Authorization

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when deciding whether a request needs an analiz task before implementation - the conditions that require the architect's analysis versus going straight to implementation

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

查看 makifbaysal 的全部技能

报告此技能的问题