Access Control Case Studies
GitLab (2021) — GraphQL IDOR leaked private projects. Facebook (2018) — View As broke access boundaries. Capital One (2019) — SSRF + path traversal. The root cause in every case: a missing server-side check.
GitLab (2021) — GraphQL IDOR leaked private projects. Facebook (2018) — View As broke access boundaries. Capital One (2019) — SSRF + path traversal. The root cause in every case: a missing server-side check.
Three major access-control breaches across three different attack classes. GitLab (2021) — GraphQL IDOR leaked private project data. Facebook (2018) — the “View As” feature bypassed access boundaries on 50 million accounts. Capital One (2019) — SSRF chained with path traversal exfiltrated data on 100 million credit card applicants. Every case shares the same root cause: a missing server-side check.
The common thread across the GitLab, Facebook, and Capital One breaches is not the specific technique but the absence of a server-side authorisation check. In each case, the application trusted the client to specify which resource to access — a project ID, a profile view, a metadata URL — without verifying that the requesting user had permission.
GraphQL IDOR — any authenticated user could read private project data by supplying a numeric project ID without a membership check.
# GraphQL resolver (vulnerable) def resolve_project(id, current_user) Project.find(id) # no membership check end
POST /api/graphql
{
"query": "query {
a: project(id: 1) { name, visibility }
b: project(id: 2) { name, visibility }
...
z: project(id: 26) { name, visibility }
}"
}- def resolve_project(id, current_user) - Project.find(id) - end + def resolve_project(id, current_user) + project = Project.find(id) + unless project.members.include?(current_user) || + project.visibility == 'public' + raise ForbiddenError + end + project + end
GitLab disclosed a critical vulnerability in their GraphQL API that allowed any authenticated user to access private project data, including the contents of repositories in public projects with restricted issue boards. The endpoint accepted a numeric project ID and returned full project data — including the namespace, visibility level, and issue content — without checking if the user was a member of that project.
The attacker used GraphQL's batching feature to query thousands of project IDs in a single HTTP request, extracting enough data to map the entire GitLab SaaS instance. The fix was straightforward: add an ownership check that verified the requesting user was a member of the project before returning data. The case is the canonical example of how GraphQL amplifies the blast radius of a missing access check.
Facebook's “View As” feature was designed to let users see how their profile looks to a specific friend. The feature worked by temporarily elevating the friend's access level on the current user's data — but a rarely-used code path involving video uploads and birthday wishes skipped the boundary check entirely. The result: an attacker could view any account's private profile data, including phone numbers and email addresses, without permission.
Facebook's post-mortem revealed that the bug was not in the core access control system but in a single handler that deviated from the standard pattern. The fix was two-fold: patch the specific handler and audit every other endpoint for the same pattern. The takeaway: even the most mature access control systems fail when a single code path bypasses the centralised gate.
// VULNERABLE - trusts client-supplied project ID
graphql resolver:
project(root, { id }) {
return db.projects.findUnique({ where: { id } });
}
// SAFE - verifies membership
graphql resolver:
project(root, { id }, { userId }) {
const project = db.projects.findUnique({ where: { id } });
const membership = db.members.findUnique({
where: { projectId_userId: { projectId: id, userId } }
});
if (!membership && project.visibility !== 'public') {
throw new ForbiddenError();
}
return project;
}The Capital One case adds a second lesson: path traversal and SSRF often appear together. The fix in that case was to block access to the AWS metadata endpoint (169.254.169.254) at the WAF level and to validate that user-supplied URLs resolved to approved external domains, not internal IP ranges.
1.Which attack class does the GitLab 2021 breach represent?
2.What made the Facebook 2018 breach particularly concerning?
3.What two vulnerabilities were chained in the Capital One 2019 breach?