cyberaudit-skill 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +77 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +241 -0
  4. package/dist/mcp-server.d.ts +2 -0
  5. package/dist/mcp-server.js +126 -0
  6. package/package.json +58 -0
  7. package/skills/cyberaudit/AGENT-BOOT.md +122 -0
  8. package/skills/cyberaudit/COMMANDS.md +1125 -0
  9. package/skills/cyberaudit/INSTALL.md +311 -0
  10. package/skills/cyberaudit/MASTER.md +194 -0
  11. package/skills/cyberaudit/README.md +154 -0
  12. package/skills/cyberaudit/USAGE-GUIDE.md +107 -0
  13. package/skills/cyberaudit/mobile/MOBILE-CHECKLIST.md +245 -0
  14. package/skills/cyberaudit/mobile/MOBILE-PHILOSOPHY.md +352 -0
  15. package/skills/cyberaudit/mobile/MOBILE-REMEDIATION-LIBRARY.md +468 -0
  16. package/skills/cyberaudit/mobile/MOBILE-THREAT-MODELS.md +279 -0
  17. package/skills/cyberaudit/mobile/frameworks/EXPO.md +247 -0
  18. package/skills/cyberaudit/mobile/frameworks/FLUTTER.md +338 -0
  19. package/skills/cyberaudit/mobile/frameworks/IONIC.md +248 -0
  20. package/skills/cyberaudit/mobile/frameworks/REACT-NATIVE.md +479 -0
  21. package/skills/cyberaudit/mobile/vulnerabilities/AUTH-MOBILE.md +160 -0
  22. package/skills/cyberaudit/mobile/vulnerabilities/BINARY-ANALYSIS.md +193 -0
  23. package/skills/cyberaudit/mobile/vulnerabilities/CRYPTO-MOBILE.md +192 -0
  24. package/skills/cyberaudit/mobile/vulnerabilities/IPC-DEEPLINKS.md +231 -0
  25. package/skills/cyberaudit/mobile/vulnerabilities/NETWORK-MOBILE.md +165 -0
  26. package/skills/cyberaudit/mobile/vulnerabilities/PERMISSIONS.md +180 -0
  27. package/skills/cyberaudit/mobile/vulnerabilities/RUNTIME-MOBILE.md +214 -0
  28. package/skills/cyberaudit/mobile/vulnerabilities/STORAGE.md +197 -0
  29. package/skills/cyberaudit/reports/EXECUTIVE-SUMMARY-TEMPLATE.md +188 -0
  30. package/skills/cyberaudit/reports/REPORT-TEMPLATE-MOBILE.md +410 -0
  31. package/skills/cyberaudit/reports/REPORT-TEMPLATE-WEB.md +222 -0
  32. package/skills/cyberaudit/shared/COMPLIANCE.md +307 -0
  33. package/skills/cyberaudit/shared/CVSS-GUIDE.md +175 -0
  34. package/skills/cyberaudit/shared/OWASP-MAPPER.md +130 -0
  35. package/skills/cyberaudit/shared/SEVERITY-SCORING.md +102 -0
  36. package/skills/cyberaudit/shared/THREAT-MODELING.md +112 -0
  37. package/skills/cyberaudit/web/WEB-CHECKLIST.md +222 -0
  38. package/skills/cyberaudit/web/WEB-PHILOSOPHY.md +308 -0
  39. package/skills/cyberaudit/web/WEB-REMEDIATION-LIBRARY.md +665 -0
  40. package/skills/cyberaudit/web/WEB-THREAT-MODELS.md +460 -0
  41. package/skills/cyberaudit/web/frameworks/ANGULAR.md +169 -0
  42. package/skills/cyberaudit/web/frameworks/EXPRESS.md +185 -0
  43. package/skills/cyberaudit/web/frameworks/LARAVEL.md +371 -0
  44. package/skills/cyberaudit/web/frameworks/NESTJS.md +337 -0
  45. package/skills/cyberaudit/web/frameworks/NEXTJS.md +352 -0
  46. package/skills/cyberaudit/web/frameworks/REACT.md +346 -0
  47. package/skills/cyberaudit/web/frameworks/VUE.md +205 -0
  48. package/skills/cyberaudit/web/vulnerabilities/AUTH-AUTHZ.md +117 -0
  49. package/skills/cyberaudit/web/vulnerabilities/BUSINESS-LOGIC.md +603 -0
  50. package/skills/cyberaudit/web/vulnerabilities/CORS.md +117 -0
  51. package/skills/cyberaudit/web/vulnerabilities/CSRF.md +131 -0
  52. package/skills/cyberaudit/web/vulnerabilities/DESERIALIZATION.md +96 -0
  53. package/skills/cyberaudit/web/vulnerabilities/HEADERS.md +105 -0
  54. package/skills/cyberaudit/web/vulnerabilities/IDOR-BOLA.md +153 -0
  55. package/skills/cyberaudit/web/vulnerabilities/INJECTION.md +165 -0
  56. package/skills/cyberaudit/web/vulnerabilities/SECRETS.md +119 -0
  57. package/skills/cyberaudit/web/vulnerabilities/SSRF.md +124 -0
  58. package/skills/cyberaudit/web/vulnerabilities/SUPPLY-CHAIN.md +142 -0
  59. package/skills/cyberaudit/web/vulnerabilities/XSS.md +133 -0
  60. package/skills/cyberaudit/web/vulnerabilities/XXE.md +94 -0
@@ -0,0 +1,117 @@
1
+ # 🌐 CORS — CROSS-ORIGIN RESOURCE SHARING GUIDE
2
+
3
+ ═══════════════════════════════════════════════════════════════
4
+ UNDERSTANDING CORS
5
+ ═══════════════════════════════════════════════════════════════
6
+
7
+ SOP (Same-Origin Policy) = Browser protection
8
+ By default, the browser blocks cross-origin requests.
9
+ CORS is a mechanism to selectively RELAX SOP.
10
+
11
+ Misconfigured CORS = Useless SOP
12
+
13
+ CORS CONCERNS :
14
+ → JS fetch/axios requests from the browser
15
+ → Not curl, Postman, or server-to-server requests
16
+ → Only what the browser enforces
17
+
18
+ SO : CORS is protection against browser attacks
19
+ NOT against direct server-side calls
20
+
21
+ ═══════════════════════════════════════════════════════════════
22
+ DANGEROUS PATTERNS
23
+ ═══════════════════════════════════════════════════════════════
24
+
25
+ CRITICAL PATTERN — Wildcard with credentials
26
+ DETECT :
27
+ Access-Control-Allow-Origin: *
28
+ Access-Control-Allow-Credentials: true
29
+
30
+ IMPACT :
31
+ Any site can make authenticated requests
32
+ Session data theft, actions on behalf of the user
33
+
34
+ NOTE : Modern browsers refuse * + credentials
35
+ But some custom configs allow it
36
+
37
+ CRITICAL PATTERN — Origin reflection without validation
38
+ DETECT :
39
+ // Bad "dynamic" implementation
40
+ const origin = req.headers.origin
41
+ res.setHeader('Access-Control-Allow-Origin', origin) // Reflect everything!
42
+ res.setHeader('Access-Control-Allow-Credentials', 'true')
43
+
44
+ IMPACT :
45
+ Any origin is accepted → equivalent to *
46
+ But with credentials → even worse than *
47
+
48
+ HIGH PATTERN — null origin accepted
49
+ DETECT :
50
+ if (origin === 'null' || allowedOrigins.includes(origin)) {
51
+ res.setHeader('Access-Control-Allow-Origin', origin)
52
+ }
53
+
54
+ IMPACT :
55
+ Local files (file://) and sandboxed iframes
56
+ send Origin: null → access granted
57
+
58
+ HIGH PATTERN — Overly broad subdomains
59
+ DETECT :
60
+ // Poorly written regex
61
+ if (origin.endsWith('.yourapp.com')) {
62
+ // evil.yourapp.com.attacker.com → passes!
63
+ }
64
+
65
+ // Better but still risky if subdomains compromised :
66
+ if (origin.match(/\.yourapp\.com$/)) {
67
+ // An XSS'd subdomain compromises everything
68
+ }
69
+
70
+ ═══════════════════════════════════════════════════════════════
71
+ CORS REMEDIATION
72
+ ═══════════════════════════════════════════════════════════════
73
+
74
+ COMPLETE SECURE CONFIGURATION
75
+ const ALLOWED_ORIGINS = new Set([
76
+ 'https://yourapp.com',
77
+ 'https://www.yourapp.com',
78
+ 'https://admin.yourapp.com',
79
+ ...(process.env.NODE_ENV === 'development'
80
+ ? ['http://localhost:3000', 'http://localhost:3001']
81
+ : [])
82
+ ])
83
+
84
+ function corsMiddleware(req, res, next) {
85
+ const origin = req.headers.origin
86
+
87
+ if (!origin) {
88
+ // Request without Origin (curl, Postman, server-to-server)
89
+ // Allow but without CORS header
90
+ return next()
91
+ }
92
+
93
+ if (ALLOWED_ORIGINS.has(origin)) {
94
+ res.setHeader('Access-Control-Allow-Origin', origin)
95
+ res.setHeader('Access-Control-Allow-Credentials', 'true')
96
+ res.setHeader('Access-Control-Allow-Methods',
97
+ 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
98
+ res.setHeader('Access-Control-Allow-Headers',
99
+ 'Content-Type, Authorization, X-Requested-With')
100
+ res.setHeader('Access-Control-Max-Age', '86400') // Cache preflight
101
+
102
+ if (req.method === 'OPTIONS') {
103
+ return res.status(204).end()
104
+ }
105
+ } else {
106
+ // Silently reject (no CORS header)
107
+ // The browser will block the request
108
+ }
109
+
110
+ next()
111
+ }
112
+
113
+ // Vary header important for caches
114
+ app.use((req, res, next) => {
115
+ res.setHeader('Vary', 'Origin')
116
+ next()
117
+ })
@@ -0,0 +1,131 @@
1
+ # 🎭 CSRF — CROSS-SITE REQUEST FORGERY GUIDE
2
+
3
+ ═══════════════════════════════════════════════════════════════
4
+ DEFINITION
5
+ ═══════════════════════════════════════════════════════════════
6
+
7
+ CSRF = The attacker forces the victim's browser
8
+ to send an authenticated request without their knowledge.
9
+
10
+ Prerequisites :
11
+ → The victim is logged into the target application
12
+ → The application uses session cookies
13
+ → No CSRF protection in place
14
+
15
+ OWASP : A01:2021 (in Broken Access Control)
16
+ CWE : CWE-352
17
+
18
+ ATTACK EXAMPLE :
19
+ 1. Victim is logged into bank.com
20
+ 2. Victim visits evil.com
21
+ 3. evil.com contains :
22
+ <img src="https://bank.com/transfer?to=attacker&amount=1000">
23
+ 4. The browser sends the request with bank.com cookies
24
+ 5. Transfer made without the victim's knowledge
25
+
26
+ ═══════════════════════════════════════════════════════════════
27
+ PATTERNS TO DETECT
28
+ ═══════════════════════════════════════════════════════════════
29
+
30
+ CSRF DISABLED (Laravel)
31
+ Route::withoutMiddleware([VerifyCsrfToken::class])
32
+ ->group(...)
33
+
34
+ // In VerifyCsrfToken.php
35
+ protected $except = [
36
+ '*', // CRITICAL : disables CSRF everywhere
37
+ 'api/*',
38
+ ]
39
+
40
+ CSRF MISSING (API without protection)
41
+ // API accepting mutations without CSRF token
42
+ // AND without Origin verification
43
+ app.post('/api/delete-account', deleteAccountHandler)
44
+
45
+ SameSite NOT CONFIGURED
46
+ res.cookie('session', token, {
47
+ httpOnly: true,
48
+ secure: true,
49
+ // SameSite missing → default depends on browser
50
+ })
51
+
52
+ ═══════════════════════════════════════════════════════════════
53
+ REMEDIATION
54
+ ═══════════════════════════════════════════════════════════════
55
+
56
+ APPROACH 1 — SAMESITE COOKIES (modern, recommended)
57
+ res.cookie('session', token, {
58
+ httpOnly: true,
59
+ secure: true,
60
+ sameSite: 'Strict', // Or 'Lax' for incoming links
61
+ // Strict : Cookie sent only on first-party navigation
62
+ // Lax : Cookie sent for links, not for sub-resources
63
+ // None : Sent everywhere (requires Secure=true)
64
+ })
65
+
66
+ // SameSite=Lax vs Strict :
67
+ // Strict : login lost if user comes from external link
68
+ // Lax : Balance security/UX
69
+ // → Recommendation : Lax for public apps, Strict for sensitive apps
70
+
71
+ APPROACH 2 — CSRF TOKEN (classic)
72
+ LARAVEL :
73
+ // Automatic with VerifyCsrfToken middleware
74
+ // In Blade forms :
75
+ <form method="POST">
76
+ @csrf
77
+ ...
78
+ </form>
79
+
80
+ // For JS API calls :
81
+ fetch('/api/data', {
82
+ method: 'POST',
83
+ headers: { 'X-CSRF-TOKEN': document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1] }
84
+ })
85
+
86
+ NODE.JS WITH CSURF (deprecated) or csrf-csrf :
87
+ import { doubleCsrf } from 'csrf-csrf'
88
+
89
+ const { generateToken, doubleCsrfProtection } = doubleCsrf({
90
+ getSecret: () => process.env.CSRF_SECRET,
91
+ cookieName: '__Host-psifi.x-csrf-token',
92
+ cookieOptions: { sameSite: 'Strict', secure: true }
93
+ })
94
+
95
+ app.use(doubleCsrfProtection)
96
+
97
+ APPROACH 3 — ORIGIN VERIFICATION
98
+ // For APIs without cookies (JWT in headers)
99
+ // CSRF is not applicable if token is in Authorization header
100
+ // CORS is the protection in this case
101
+
102
+ // Verify Origin for mutations :
103
+ app.use((req, res, next) => {
104
+ if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
105
+ const origin = req.headers.origin
106
+ const referer = req.headers.referer
107
+
108
+ const allowedOrigins = ['https://yourapp.com']
109
+
110
+ if (origin && !allowedOrigins.includes(origin)) {
111
+ return res.status(403).json({ error: 'Origin not allowed' })
112
+ }
113
+ }
114
+ next()
115
+ })
116
+
117
+ WEBHOOKS — SPECIAL CASE
118
+ // Webhooks receive POST from third-party services
119
+ // Exclude from CSRF BUT validate HMAC signature
120
+
121
+ // Stripe
122
+ app.post('/webhooks/stripe',
123
+ express.raw({ type: 'application/json' }), // Raw body for signature
124
+ (req, res) => {
125
+ const sig = req.headers['stripe-signature']
126
+ const event = stripe.webhooks.constructEvent(
127
+ req.body, sig, process.env.STRIPE_WEBHOOK_SECRET
128
+ )
129
+ // ... processing
130
+ }
131
+ )
@@ -0,0 +1,96 @@
1
+ # 💣 DESERIALIZATION — VULNERABILITY GUIDE
2
+
3
+ ═══════════════════════════════════════════════════════════════
4
+ DEFINITION
5
+ ═══════════════════════════════════════════════════════════════
6
+
7
+ INSECURE DESERIALIZATION =
8
+ Deserializing untrusted data can lead to :
9
+ → Remote Code Execution (RCE) via gadget chains
10
+ → Object injection with malicious behavior
11
+ → DoS via malformed objects
12
+
13
+ OWASP : A08:2021 — Software and Data Integrity Failures
14
+ CWE : CWE-502
15
+
16
+ MOST AT-RISK LANGUAGES :
17
+ → PHP (unserialize)
18
+ → Java (ObjectInputStream)
19
+ → Python (pickle)
20
+ → Ruby (Marshal)
21
+ → Node.js (rare but possible)
22
+
23
+ ═══════════════════════════════════════════════════════════════
24
+ PATTERNS TO DETECT
25
+ ═══════════════════════════════════════════════════════════════
26
+
27
+ PHP — CRITICAL :
28
+ unserialize($userInput)
29
+ unserialize(base64_decode($cookie))
30
+ unserialize(base64_decode($_COOKIE['cart']))
31
+ unserialize(file_get_contents($userProvidedPath))
32
+
33
+ // Second-order : data deserialized from DB
34
+ // that was inserted via user input
35
+
36
+ PYTHON — CRITICAL :
37
+ pickle.loads(userInput)
38
+ pickle.load(file_from_user)
39
+ yaml.load(userInput) // PyYAML without Loader=yaml.SafeLoader
40
+
41
+ NODE.JS — LESS COMMON BUT PRESENT :
42
+ // node-serialize
43
+ serialize.unserialize(userInput)
44
+ // eval() in custom parsers
45
+
46
+ ═══════════════════════════════════════════════════════════════
47
+ REMEDIATION
48
+ ═══════════════════════════════════════════════════════════════
49
+
50
+ PHP :
51
+ // ❌ Never deserialize user data
52
+ unserialize($userInput) // Never
53
+
54
+ // ✅ Use JSON
55
+ $data = json_decode($userInput, true)
56
+
57
+ // ✅ If serialize() needed for internal cache :
58
+ // Ensure data never comes from the user
59
+ // Sign serialized data with HMAC
60
+ $serialized = serialize($data)
61
+ $hmac = hash_hmac('sha256', $serialized, $secret)
62
+ $stored = $hmac . ':' . $serialized
63
+
64
+ // On read :
65
+ [$storedHmac, $storedData] = explode(':', $stored, 2)
66
+ if (!hash_equals($storedHmac, hash_hmac('sha256', $storedData, $secret))) {
67
+ throw new Exception('Corrupted data')
68
+ }
69
+ $data = unserialize($storedData)
70
+
71
+ PYTHON :
72
+ # ❌ Never pickle with user input
73
+ data = pickle.loads(userInput) # RCE possible
74
+
75
+ # ✅ JSON
76
+ data = json.loads(userInput)
77
+
78
+ # ✅ Secure PyYAML
79
+ data = yaml.load(userInput, Loader=yaml.SafeLoader)
80
+ # yaml.SafeLoader does not execute Python code
81
+
82
+ # If pickle needed for internal cache :
83
+ # Sign with hmac before storage
84
+ import hmac, hashlib, pickle
85
+
86
+ def safe_serialize(data, secret):
87
+ pickled = pickle.dumps(data)
88
+ sig = hmac.new(secret, pickled, hashlib.sha256).digest()
89
+ return sig + pickled
90
+
91
+ def safe_deserialize(data, secret):
92
+ sig, pickled = data[:32], data[32:]
93
+ expected = hmac.new(secret, pickled, hashlib.sha256).digest()
94
+ if not hmac.compare_digest(sig, expected):
95
+ raise ValueError('Invalid signature')
96
+ return pickle.loads(pickled)
@@ -0,0 +1,105 @@
1
+ # 🛡️ SECURITY HEADERS — VULNERABILITY GUIDE
2
+
3
+ ═══════════════════════════════════════════════════════════════
4
+ ESSENTIAL SECURITY HEADERS
5
+ ═══════════════════════════════════════════════════════════════
6
+
7
+ EACH MISSING HEADER = MISSING PROTECTION
8
+
9
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
10
+ CONTENT-SECURITY-POLICY (CSP)
11
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ ROLE : Controls authorized sources for each resource type
13
+ PROTECTS : XSS, script injection, clickjacking, data exfiltration
14
+ MISSING : XSS can execute arbitrary code
15
+
16
+ CHECK :
17
+ □ Present on all HTML pages
18
+ □ Restrictive default-src
19
+ □ unsafe-inline absent from script-src
20
+ □ unsafe-eval absent from script-src
21
+ □ frame-ancestors configured
22
+ □ base-uri configured (prevents base tag injection)
23
+ □ form-action configured
24
+
25
+ TEST : https://csp-evaluator.withgoogle.com/
26
+
27
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
28
+ STRICT-TRANSPORT-SECURITY (HSTS)
29
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
30
+ ROLE : Forces the browser to use HTTPS only
31
+ PROTECTS : SSL stripping, downgrade attacks
32
+ MISSING : HTTP connection possible → MitM
33
+
34
+ RECOMMENDED VALUE :
35
+ Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
36
+
37
+ CHECK :
38
+ □ max-age ≥ 31536000 (1 year)
39
+ □ includeSubDomains if all subdomains are HTTPS
40
+ □ preload to be in browser HSTS list
41
+ □ Server on HTTPS (otherwise this header is ignored)
42
+
43
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
44
+ X-FRAME-OPTIONS
45
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
46
+ ROLE : Prevents loading in an iframe
47
+ PROTECTS : Clickjacking
48
+ MISSING : The app can be loaded in a malicious iframe
49
+
50
+ RECOMMENDED VALUE :
51
+ X-Frame-Options: DENY
52
+ // OR if embedding needed :
53
+ X-Frame-Options: SAMEORIGIN
54
+
55
+ NOTE : Replaced by CSP frame-ancestors but X-Frame-Options
56
+ is still supported by older browsers
57
+
58
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
59
+ X-CONTENT-TYPE-OPTIONS
60
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
61
+ ROLE : Prevents MIME sniffing
62
+ PROTECTS : Malicious file upload served as HTML/JS
63
+ MISSING : Uploaded file interpreted with wrong MIME type
64
+
65
+ SINGLE VALUE :
66
+ X-Content-Type-Options: nosniff
67
+
68
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
69
+ REFERRER-POLICY
70
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
71
+ ROLE : Controls info sent in the Referer header
72
+ PROTECTS : Sensitive URL leakage (tokens in URL, internal paths)
73
+
74
+ RECOMMENDED VALUE :
75
+ Referrer-Policy: strict-origin-when-cross-origin
76
+ // Sends : origin only for cross-origin, full URL for same-origin
77
+
78
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
79
+ PERMISSIONS-POLICY
80
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
81
+ ROLE : Controls available browser APIs
82
+ PROTECTS : Unauthorized access to camera, mic, geolocation
83
+
84
+ RECOMMENDED VALUE :
85
+ Permissions-Policy: camera=(), microphone=(), geolocation=(),
86
+ payment=(), usb=(), bluetooth=()
87
+ // Disable everything the app does not use
88
+
89
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
90
+ HEADERS TO REMOVE
91
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
92
+ X-Powered-By → Reveals Express, PHP version, etc.
93
+ Server → Reveals nginx/apache version
94
+ X-AspNet-Version → Reveals .NET version
95
+ X-AspNetMvc-Version
96
+
97
+ This info helps an attacker target specific CVEs.
98
+
99
+ ═══════════════════════════════════════════════════════════════
100
+ TEST TOOL
101
+ ═══════════════════════════════════════════════════════════════
102
+
103
+ → https://securityheaders.com/
104
+ → https://observatory.mozilla.org/
105
+ → curl -I https://yourapp.com
@@ -0,0 +1,153 @@
1
+ # 🎯 IDOR / BOLA — VULNERABILITY GUIDE
2
+ # Insecure Direct Object Reference / Broken Object Level Authorization
3
+
4
+ ═══════════════════════════════════════════════════════════════
5
+ DEFINITION
6
+ ═══════════════════════════════════════════════════════════════
7
+
8
+ IDOR = Access to a resource by manipulating its identifier
9
+ without verifying the user has permission.
10
+
11
+ BOLA = OWASP API Security term for the same concept on the API side.
12
+
13
+ It is the most common and most impactful vulnerability
14
+ in modern web applications.
15
+
16
+ OWASP Top 10 : A01:2021
17
+ OWASP API : API1:2023
18
+
19
+ ═══════════════════════════════════════════════════════════════
20
+ DETECTING IDOR
21
+ ═══════════════════════════════════════════════════════════════
22
+
23
+ SEARCH EVERYWHERE AN ID IS USED :
24
+ URLs : /api/users/123, /documents/456
25
+ Body : { "userId": 123, "orderId": 456 }
26
+ Query params : ?accountId=789&fileId=101
27
+ Headers : X-User-Id: 123 (custom headers)
28
+
29
+ TYPES OF IDOR :
30
+ Sequential numeric IDs : /order/1042 → /order/1041
31
+ Base64 IDs : /file/dXNlcjEyMw==
32
+ UUIDs exposed in API : If listing possible → enumeration
33
+ Predictable file names : /uploads/invoice_user123_2024.pdf
34
+ File paths : /download?path=users/123/file.pdf
35
+
36
+ VULNERABLE CODE TO DETECT :
37
+ // Node.js
38
+ app.get('/api/orders/:id', async (req, res) => {
39
+ const order = await Order.findById(req.params.id)
40
+ // NO check that order.userId === req.user.id !
41
+ res.json(order)
42
+ })
43
+
44
+ // PHP/Laravel
45
+ public function show($id) {
46
+ $invoice = Invoice::find($id)
47
+ return $invoice // NO ownership check!
48
+ }
49
+
50
+ // Prisma/TypeORM
51
+ async getDocument(id: string) {
52
+ return this.db.document.findUnique({ where: { id } })
53
+ // No where userId!
54
+ }
55
+
56
+ ═══════════════════════════════════════════════════════════════
57
+ IDOR VARIANTS
58
+ ═══════════════════════════════════════════════════════════════
59
+
60
+ INDIRECT IDOR (most often missed)
61
+ // A can access B that belongs to C without being C
62
+ GET /api/projects/123/members
63
+ // The user is a member of project 123
64
+ // But do they have the right to see the members?
65
+
66
+ IDOR VIA RELATIONS
67
+ GET /api/comments/456
68
+ // Comment 456 belongs to a private post
69
+ // But comment access does not check post visibility
70
+
71
+ IDOR IN BATCH OPERATIONS
72
+ DELETE /api/messages?ids=1,2,3,4,5
73
+ // Is each ID verified individually?
74
+
75
+ IDOR IN WRITE OPERATIONS (often more severe)
76
+ PUT /api/profiles/123
77
+ // User 456 modifies profile 123
78
+
79
+ IDOR VIA EXPORT
80
+ GET /api/export?userId=123
81
+ // Exports user 123's data
82
+
83
+ ═══════════════════════════════════════════════════════════════
84
+ REMEDIATION
85
+ ═══════════════════════════════════════════════════════════════
86
+
87
+ FUNDAMENTAL PRINCIPLE :
88
+ The user ID ALWAYS comes from the token/session.
89
+ NEVER from the body, params, or query strings.
90
+
91
+ UNIVERSAL FIX PATTERN :
92
+ // Instead of :
93
+ resource = findById(requestedId)
94
+
95
+ // Always :
96
+ resource = findById(requestedId, WHERE ownerId = currentUserId)
97
+
98
+ NODE.JS — COMPLETE FIX
99
+ // Middleware that extracts user from token
100
+ const getCurrentUser = (req) => {
101
+ // Already done by JWT middleware
102
+ return req.user // Comes from the token, not the body
103
+ }
104
+
105
+ // Secured route
106
+ app.get('/api/orders/:orderId', auth, async (req, res) => {
107
+ const currentUser = getCurrentUser(req)
108
+
109
+ const order = await Order.findOne({
110
+ where: {
111
+ id: req.params.orderId,
112
+ userId: currentUser.id, // Ownership check integrated in query
113
+ }
114
+ })
115
+
116
+ if (!order) {
117
+ // 404 rather than 403 : don't confirm the resource exists
118
+ return res.status(404).json({ error: 'Resource not found' })
119
+ }
120
+
121
+ res.json(order)
122
+ })
123
+
124
+ USE UUIDS
125
+ // Instead of sequential IDs :
126
+ id: 1042, 1043, 1044... → trivial to enumerate
127
+
128
+ // Use UUIDs v4 :
129
+ id: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
130
+ // Impossible to guess, but ownership check remains mandatory!
131
+
132
+ IDOR MONITORING
133
+ // Alert on abnormal access patterns
134
+ const accessLog = async (userId, resourceId, success) => {
135
+ await AuditLog.create({
136
+ userId, resourceId, success,
137
+ timestamp: new Date(),
138
+ ip: req.ip,
139
+ })
140
+
141
+ // If >10 denied accesses in 1 minute → alert
142
+ const recentFailures = await AuditLog.count({
143
+ where: {
144
+ userId,
145
+ success: false,
146
+ timestamp: { $gt: new Date(Date.now() - 60000) }
147
+ }
148
+ })
149
+
150
+ if (recentFailures > 10) {
151
+ await alertSecurityTeam({ userId, type: 'possible_idor_scan' })
152
+ }
153
+ }