Access Control: Multi User Access Control¶
Identifier:
multi_user_access_control
Scanner(s) Support¶
| GraphQL Scanner | REST Scanner | WebApp Scanner | ASM Scanner |
|---|---|---|---|
Description¶
Multi-user access control vulnerabilities occur when systems fail to properly separate access between users, roles, or tenants, allowing unauthorized access or modification due to flawed authorization logic.
How we test: We replay the same requests with different authenticated users and compare responses to detect broken access control across user scopes (cross-tenant and RBAC/privilege boundaries).
Execution conditions (BLST):
- Runs when this test is enabled and preconditions pass: coverage is OK or EMPTY_RESPONSE, at least one other user exists, operation is READ/CREATE/ UPDATE, and arguments are present.
- Additional filters: denylisted paths are skipped, and READ checks are skipped when the response is empty.
By default, when no configuration is provided, the check will cover all paths and keys matching. Response similarity will be used to detect isolation violations.
References:
Configuration¶
Example¶
Example configuration:
---
security_tests:
multi_user_access_control:
keys_matching:
- card_number
main_user: ''
natural_language_rule: Ensure that a user's notes cannot be accessed by other
users.
other_users:
detect:
- if_: request.is_authenticated
is_: true
is_not: null
- if_: helpers.fingerprints.same
is_: true
is_not: null
paths:
- /users/{id}
skip: false
specific_users: {}
Reference¶
keys_matching¶
Type : List[string]*
List of keys in a response body that will be compared between different users, to detect an isolation violation.
If the key values are the exact same between these users, an alert will be raised.
For example if you want to control the key card_number, you can use the following:
main_user¶
Type : string
The main user to use for the check. It will be used as source of truth to detect isolation violations with all other users.
natural_language_rule¶
Type : string
A natural language prompt to describe what should be checked for multi-user access control. This will be used to generate the rules to detect multi-user access control issues when analyzing the responses. You can review the generated rules in the alert details, or the scan logs with the prefix [Agentic - Multi User Access Control]
other_users¶
Type : MultiUserAccessControlRule*
The conditions to trigger the alert when comparing the original and switched responses. The list of conditions are combined with AND logic by default. By default, the conditions check if the request is authenticated and if the responses of both users have the same fingerprint.
paths¶
Type : List[string]*
List of paths that this check will cover. Add * to cover all paths.
For example if you want to control the path /users/{id}, you can use the following:
To cover all paths, you can use the following:
skip¶
Type : boolean
Skip the test if true.
specific_users¶
Type : Dict[string, MultiUserAccessControlRule]
The conditions to trigger the alert when analyzing the responses between a given user and specific users. The list of conditions are combined with AND logic by default.
APILogicalAndDetector¶
and¶
Type : List[APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector|VariableDefinedDetector]*
Logical and on a list of detectors
if¶
Type : Const[and]*
Use this to apply a logical and on a list of detectors.
Example¶
detect:
- if: and
and:
- if: helpers.request.crud
in:
- CREATE
- UPDATE
- if: response.status_code
is: 200
APILogicalNotDetector¶
if¶
Type : Const[not]*
Use this to apply a logical not on a detector.
Example¶
not¶
Type : APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector|VariableDefinedDetector
Logical not of a detector
APILogicalOrDetector¶
if¶
Type : Const[or]*
Use this to apply a logical or on a list of detectors.
Example¶
detect:
- if: or
or:
- if: helpers.request.crud
in:
- CREATE
- UPDATE
- if: response.status_code
is: 200
or¶
Type : List[APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector|VariableDefinedDetector]*
Logical or on a list of detectors
FingerprintCountDetector¶
gt¶
Type : integer
Condition is greater than this integer
if¶
Type : Const[helpers.fingerprints.count]*
Use this to select and compare the count of unique fingerprints of the current and original response.
in¶
Type : List[integer]
Condition is in this list of integers (exact match)
is¶
Type : integer
Condition is this exact integer
is_not¶
Type : integer
Condition is not this exact integer
lt¶
Type : integer
Condition is less than this integer
FingerprintsSameDetector¶
if¶
Type : Const[helpers.fingerprints.same]*
Use this to determine whether the current and original responses have the same fingerprint.
is¶
Type : boolean
Condition is true
is_not¶
Type : boolean
Condition is false
HelpersRequestCrudDetector¶
if¶
Type : Const[helpers.request.crud]*
Use this to select against the detected CRUD operation of the request.
Example¶
in¶
Type : List[CRUD]
Condition is the request is in this list of CRUD operations (exact match)
is¶
Type : CRUD
Condition is the request is this CRUD operation
is_not¶
Type : CRUD
Condition is the request is not this CRUD operation
HelpersResponseIsSuccessfulDetector¶
if¶
Type : Const[helpers.response.is_successful]*
True when the response status code is in the 2xx range.
Matcher type: BooleanMatcher · Operators: is
In context — combine with helpers.request.crud and request.is_authenticated to flag anonymous mutations:
{{ example: api/access_control/unauthenticated-mutation.yaml }}
is¶
Type : boolean
Condition is true
is_not¶
Type : boolean
Condition is false
JSONMatchesAllDetector¶
if¶
Type : Const[helpers.json_matches.all]*
Use this to determine whether every the current and original responses contain the same JSON fragment.
is¶
Type : boolean
Condition is true
is_not¶
Type : boolean
Condition is false
jq¶
Type : string
Use this to select the exact JSON you want to compare between the current and original response.
JSONMatchesCountDetector¶
gt¶
Type : integer
Condition is greater than this integer
if¶
Type : Const[helpers.json_matches.count]*
Use this to count the number of times a JSON match is in the current and original response.
in¶
Type : List[integer]
Condition is in this list of integers (exact match)
is¶
Type : integer
Condition is this exact integer
is_not¶
Type : integer
Condition is not this exact integer
jq¶
Type : string
Use this to select the exact JSON you want to compare between the current and original response.
lt¶
Type : integer
Condition is less than this integer
JsonValue¶
MultiUserAccessControlRule¶
detect¶
Type : List[APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector|VariableDefinedDetector]*
The detectors to trigger the alert when analyzing the responses between the main user and other users.
ObjectTypeMatcher¶
in¶
Type : List[OBJECT_TYPE]
Object type is in the following list
is¶
Type : OBJECT_TYPE
Object type is exactly this type
is_not¶
Type : OBJECT_TYPE
Object type is any this type except this one
RegexMatchesAllDetector¶
if¶
Type : Const[helpers.regex_matches.all]*
Use this to determine whether every the current and original responses match the same regular expression.
is¶
Type : boolean
Condition is true
is_not¶
Type : boolean
Condition is false
regex¶
Type : string
Condition is matched on this regex with fullmatch
RegexMatchesCountDetector¶
gt¶
Type : integer
Condition is greater than this integer
if¶
Type : Const[helpers.regex_matches.count]*
Use this to count the number of times a regex match is in the current and original response.
in¶
Type : List[integer]
Condition is in this list of integers (exact match)
is¶
Type : integer
Condition is this exact integer
is_not¶
Type : integer
Condition is not this exact integer
lt¶
Type : integer
Condition is less than this integer
regex¶
Type : string
Condition is matched on this regex with fullmatch
RequestBodyJSONDetector¶
if¶
Type : Const[request.body.json]*
Use this to select and compare the request body when detected as JSON, using jq-like syntax.
Example 1¶
Example 2¶
in¶
Type : List[JsonValue]
Condition is in this list of JSON
is¶
Type : JsonValue
Condition is this exact JSON
is_not¶
Type : JsonValue
Condition is not this exact JSON
jq¶
Type : string
JQ query to match and use as boolean. If use_extraction is True, only this attribute will be parsed (if set).
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
RequestBodyTextDetector¶
contains¶
Type : string
Contains this substring (case-insensitive)
if¶
Type : Const[request.body.text]*
Use this to select and compare the request body as text, using string compare.
Example¶
in¶
Type : List[string]
Condition is in this list (case-insensitive)
is¶
Type : string
Condition is this string (case-insensitive)
is_not¶
Type : string
Condition is not this string (case-insensitive)
regex¶
Type : string
Condition is matched on this regex with fullmatch (case-insensitive)
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
RequestHeadersDetector¶
if¶
Type : Const[request.headers]*
Use that to select and compare the request headers in a key value dictionary.
Example¶
key¶
Type : StringMatcher
Key to match
value¶
Type : StringMatcher
Value to match
RequestIsAuthenticatedDetector¶
if¶
Type : Const[request.is_authenticated]*
Use this to select whether the request is authenticated.
Example¶
is¶
Type : boolean
Condition is true
is_not¶
Type : boolean
Condition is false
RequestMethodDetector¶
if¶
Type : Const[request.method]*
Use this to select against the request HTTP Method.
Example¶
in¶
Type : List[HTTPMethod]
Condition is the request is in this list of CRUD operations (exact match)
is¶
Type : HTTPMethod
Condition is the request is this CRUD operation
is_not¶
Type : HTTPMethod
Condition is the request is not this CRUD operation
RequestObjectDetector¶
if¶
Type : Const[request.object]*
Use this to select and compare the detected object scalars (including custom scalars) in the request, with their kind, name and value.
Example¶
name¶
Type : StringMatcher
Object scalar name to match
type¶
Type : ObjectTypeMatcher
Object scalar type to match
value¶
Type : StringMatcher
Object scalar value to match
RequestUserDetector¶
contains¶
Type : string
Contains this substring (case-insensitive)
if¶
Type : Const[request.user]*
Match the user name attached to the request. The value must match a user defined in your scan authentication settings (the special user public represents an unauthenticated caller).
Matcher type: StringMatcher · Operators: is, is_not, contains, regex, in
In context — BOLA detection by replaying as a different user:
{{ example: api/access_control/bola-user-swap-fingerprint.yaml }}
in¶
Type : List[string]
Condition is in this list (case-insensitive)
is¶
Type : string
Condition is this string (case-insensitive)
is_not¶
Type : string
Condition is not this string (case-insensitive)
regex¶
Type : string
Condition is matched on this regex with fullmatch (case-insensitive)
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
ResponseBodyJSONDetector¶
if¶
Type : Const[response.body.json]*
Use this to select and compare the response body when detected as JSON, using jq-like syntax.
Example 1¶
Example 2¶
in¶
Type : List[JsonValue]
Condition is in this list of JSON
is¶
Type : JsonValue
Condition is this exact JSON
is_not¶
Type : JsonValue
Condition is not this exact JSON
jq¶
Type : string
JQ query to match and use as boolean. If use_extraction is True, only this attribute will be parsed (if set).
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
ResponseBodyTextDetector¶
contains¶
Type : string
Contains this substring (case-insensitive)
if¶
Type : Const[response.body.text]*
Use this to select and compare the response body as text, using string compare.
Example¶
in¶
Type : List[string]
Condition is in this list (case-insensitive)
is¶
Type : string
Condition is this string (case-insensitive)
is_not¶
Type : string
Condition is not this string (case-insensitive)
regex¶
Type : string
Condition is matched on this regex with fullmatch (case-insensitive)
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
ResponseDurationDetector¶
gt¶
Type : integer
Condition is greater than this integer
if¶
Type : Const[response.duration_ms]*
Use this to compare the duration of the request in milliseconds.
Example¶
in¶
Type : List[integer]
Condition is in this list of integers (exact match)
is¶
Type : integer
Condition is this exact integer
is_not¶
Type : integer
Condition is not this exact integer
lt¶
Type : integer
Condition is less than this integer
ResponseHeadersDetector¶
if¶
Type : Const[response.headers]*
Use that to select and compare the response headers in a key value dictionary.
Example¶
key¶
Type : StringMatcher
Key to match
value¶
Type : StringMatcher
Value to match
ResponseObjectDetector¶
if¶
Type : Const[response.object]*
Use this to select and compare the detected object scalars (including custom scalars) in the response, with their kind, name and value.
Example¶
name¶
Type : StringMatcher
Object scalar name to match
type¶
Type : ObjectTypeMatcher
Object scalar type to match
value¶
Type : StringMatcher
Object scalar value to match
ResponseStatusCodeDetector¶
gt¶
Type : integer
Condition is greater than this integer
if¶
Type : Const[response.status_code]*
Match against the HTTP response status code.
Matcher type: IntegerMatcher · Operators: is, is_not, gt, lt, in
For simple success/failure checks prefer helpers.response.is_successful (it covers the full 2xx range and is more readable).
In context — partial-content trick for SQL dump probing:
{{ example: api/information_disclosure/exposed-sql-dumps.yaml }}
in¶
Type : List[integer]
Condition is in this list of integers (exact match)
is¶
Type : integer
Condition is this exact integer
is_not¶
Type : integer
Condition is not this exact integer
lt¶
Type : integer
Condition is less than this integer
ScanTypeDetector¶
if¶
Type : Const[scan.type]*
Match against the type of scan being performed.
Matcher Type: ScanTypeMatcher · Operators: is, is_not, in · Valid values: REST, GRAPHQL
in¶
Type : List[CustomRuleScanType]
The scan type is in this list
is¶
Type : CustomRuleScanType
The scan type is exactly this
is_not¶
Type : CustomRuleScanType
The scan type is not this type
SchemaNeedAuthenticationDetector¶
if¶
Type : Const[schema.need_authentication]*
Use this to select whether or not the schema requires authentication.
Example¶
is¶
Type : boolean
Condition is true
is_not¶
Type : boolean
Condition is false
SchemaPathRefDetector¶
contains¶
Type : string
Contains this substring (case-insensitive)
if¶
Type : Const[schema.path_ref]*
Match the operation name (GraphQL) or the path (REST) of the request.
Matcher type: StringMatcher · Operators: is, is_not, contains, regex, in
detect:
- if: schema.path_ref
contains: /admin/
# Or regex over a versioned path:
detect:
- if: schema.path_ref
regex: /api/v[0-9]+/admin/.*
In context — anonymous admin route detection:
{{ example: api/access_control/public-admin-route.yaml }}
in¶
Type : List[string]
Condition is in this list (case-insensitive)
is¶
Type : string
Condition is this string (case-insensitive)
is_not¶
Type : string
Condition is not this string (case-insensitive)
regex¶
Type : string
Condition is matched on this regex with fullmatch (case-insensitive)
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
SchemaUrlDetector¶
contains¶
Type : string
Contains this substring (case-insensitive)
if¶
Type : Const[schema.url]*
Use this to string compare the URL of the request.
Example¶
in¶
Type : List[string]
Condition is in this list (case-insensitive)
is¶
Type : string
Condition is this string (case-insensitive)
is_not¶
Type : string
Condition is not this string (case-insensitive)
regex¶
Type : string
Condition is matched on this regex with fullmatch (case-insensitive)
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
StringMatcher¶
contains¶
Type : string
Contains this substring (case-insensitive)
in¶
Type : List[string]
Condition is in this list (case-insensitive)
is¶
Type : string
Condition is this string (case-insensitive)
is_not¶
Type : string
Condition is not this string (case-insensitive)
regex¶
Type : string
Condition is matched on this regex with fullmatch (case-insensitive)
use_extraction¶
Type : boolean
If True, variable references between {{ }} will be replace with the extracted value (If exists). The string representation of the variable will be used without any extra-processing.
VariableDefinedDetector¶
if¶
Type : Const[variable.defined]*
Use this to detect if a given variable has been extracted or not yet.
Basic Example¶
Common Use Cases¶
- Making sure a variable has been already extracted before executing a mutation, a detector or another extraction.
variable_name¶
Type : string*
Use this to specify the variable name that has to be defined before proceeding further
Enums¶
CRUD¶
| Value |
|---|
CREATE |
READ |
UPDATE |
DELETE |
CustomRuleScanType¶
| Value |
|---|
GRAPHQL |
REST |
HTTPMethod¶
| Value |
|---|
CONNECT |
DELETE |
GET |
HEAD |
OPTIONS |
PATCH |
POST |
PUT |
TRACE |
OBJECT_TYPE¶
| Value |
|---|
adobe_client_secret |
age_secret_key |
alibaba_access_key_id |
alibaba_secret_key |
amount |
application |
area_code |
asana_client_secret |
atlassian_api_token |
authentication |
author |
authorization_code |
aws_access_token |
aws_mws_id |
bank |
bank_account |
bank_card |
base64 |
bcrypt |
beamer_api_token |
bearer |
bearer_uuid |
bitbucket_client_secret |
bitcoin |
body_type |
boolean |
boolean_wannabe |
building |
card_type |
category |
city |
clojars_api_token |
command |
commit_hash |
confirmation_code |
content_type |
country |
country_code |
county |
coupon_code |
cuid |
currency_code |
cvv |
dash |
databricks_api_token |
datadog_access_token |
date |
datetime |
delivery_method |
device_name |
device_type |
did |
digitalocean_pat |
directory |
discount |
document_type |
doppler_api_token |
driving_license |
dropbox_api_token |
dropbox_long_lived_api_token |
dropbox_short_lived_api_token |
duffel_api_token |
duration |
dynatrace_api_token |
e_commerce_indicator |
email |
environment |
ethereum |
event_type |
facebook |
fee |
file |
float |
form |
french_phone |
func |
gender |
github_app_token |
github_fine_grained_pat |
github_oauth |
github_pat |
github_refresh_token |
gitlab_pat |
gitlab_rrt |
google_api_key |
grafana_service_account_token |
hash |
hashicorp_tf_api_token |
health_insurance_number |
heroku_api_key |
hex_color_code |
hexadecimal |
host |
house_number |
hsl |
hsla |
html_body |
http_method |
hubspot_api_key |
huggingface_token |
id |
identity_number |
injection |
integer |
intercom_api_key |
ipc_patent |
ipv4 |
ipv6 |
isbn |
item |
jfrog_api_key |
join |
json |
jwt |
language_iso_639_1 |
language_iso_639_2 |
latitude |
launchdarkly_access_token |
legal_name |
limit |
linear_api_key |
linear_client_secret |
linkedin_client_secret |
llm_input |
lob_api_key |
locale |
location |
longitude |
mac |
mailchimp_api_key |
mailgun_private_api_token |
mailgun_signing_key |
mask |
md5 |
medical_record_number |
merchant |
messagebird_api_token |
microsoft_teams_webhook |
monero |
mongo_db_object_id |
month |
navigation |
netlify_access_token |
new_relic_user_api_id |
npm_access_token |
offset |
okta_access_token |
openai_api_key |
pagination |
pagination_limit |
pagination_wannabe |
passport |
password |
permission |
phone |
pin_code |
plan |
planetscale_api_token |
planetscale_password |
policy |
port |
postman_api_token |
prescription_number |
price |
private_key |
protocol |
pubnubpublishkey |
pulumi_api_token |
pypi_upload_token |
reason_code |
reference |
region |
return_type |
rgb |
rgba |
role |
rubygems_api_token |
search |
secret |
sendgrid_api_token |
sendinblue_api_token |
sentry_access_token |
serial_number |
sha1 |
sha256 |
shipping_method |
shippo_api_token |
shopify_access_token |
shopify_custom_access_token |
shopify_private_app_access_token |
shopify_shared_secret |
sidekiq_secret |
slack_bot_token |
slack_legacy_workspace_token |
slack_user_token |
slack_webhook_url |
slug |
social_security_number |
software_component |
ssh_url |
status |
status_code |
status_message |
street_address |
string |
stripe_access_token |
stripe_public_access_token |
sumologic_access_token |
time |
timestamp |
title |
twitch_api_token |
twitter_api_key |
typeform_api_token |
unsanitized_payload |
upload |
uri |
url |
us_bank_account_number |
us_bank_routing_number |
us_zip_code |
user_agent |
username |
uuid |
vault_service_token |
vehicle_type |
version |
view |
year |
zendesk_secret_key |
zip_code |