ASVS 5.0 V16.3.2 requires that failed authorization attempts are logged. For Level 3, every authorization decision must be logged, including when sensitive data is accessed, without logging the sensitive data itself. The requirement is Level 2 for failed attempts. Put the logging in your central access check so no endpoint can skip it.
Why it matters
A burst of 403 responses against object ids is the signature of someone probing for broken access control, one of the most common web flaws. If denials leave no trace, you only learn about the attack after data has gone. Allowed decisions on sensitive records matter too, because they answer the question regulators ask after an incident: who looked at this record.
How to implement it
Log inside the single function that makes the decision.
import logging
log = logging.getLogger("authz")
SENSITIVE = {"medical_record", "payment_method", "salary"}
def authorize(user, action, resource_type, resource_id, allowed: bool):
if not allowed:
log.warning("authz.denied", extra={"fields": {
"user_id": user.id, "action": action,
"resource_type": resource_type, "resource_id": resource_id,
"tenant": user.tenant_id, "roles": sorted(user.roles)}})
elif resource_type in SENSITIVE: # extra rule for Level 3
log.info("authz.sensitive_access", extra={"fields": {
"user_id": user.id, "action": action,
"resource_type": resource_type, "resource_id": resource_id}})
return allowed
Record who, what action, which resource and why it was denied. Log the resource id, never its contents. Alert on many denials from one user or address in a short window.
Teams that use a policy engine can emit the decision log there, so every service gets the same record format.
How to test it
- As a low privilege user, request another user's object and an admin only endpoint.
- Verify each denial appears in the logs with user, action and resource.
- For Level 3, read a sensitive record as an allowed user and confirm an access entry exists without the record's data.
- Search the logs for field values from the record. There should be none.
- Check that denials from every route, including APIs and background jobs, are covered.
Common mistakes
- Logging denials only for the web UI and not the API.
- Returning 404 to hide existence and logging nothing.
- Writing denial logs per endpoint by hand, so some are missed.
- Putting the sensitive value into the log to help debugging.
Quick check
Is logging successful sensitive access required at Level 2?
No. Level 2 asks for failed attempts. Logging all decisions and sensitive access is the Level 3 addition.
Should a 404 used to hide a resource be logged as a denial?
Yes. Log the internal decision even if the client sees a 404.