Free Tool ยท OWASP Top 10 2021

OWASP Top 10
Code Playground.

Explore real-world vulnerable and fixed code for every OWASP Top 10 2021 category. JavaScript, Python, and Java examples with line-by-line explanations.

AquilaX Platform

Run 32 scanners on your
real codebase.

These tools give you a glimpse of AquilaX's capabilities. The full platform runs everything automatically on every commit, across your entire stack.

\n\n\n// Also bad: deserializing arbitrary objects\nconst obj = eval("(" + userInput + ")");\n// Or with node-serialize:\nconst serialize = require("node-serialize");\nconst obj = serialize.unserialize(req.body.data); // RCE vulnerability\n', fixed: '\n\n\n\n\n// For deserialization: use safe data formats\nconst data = JSON.parse(userInput); // JSON, not eval/unserialize\n// Validate structure with schema (zod, joi)\nconst safeData = schema.parse(data);\n' }, python: { vuln: '# โŒ VULNERABLE: Pickle deserialization of user input\nimport pickle\n\[email protected]("/load-profile", methods=["POST"])\ndef load_profile():\n # pickle.loads on user-controlled data = arbitrary code execution!\n data = pickle.loads(request.data)\n return jsonify(data)\n\n# Attack payload can execute: os.system("rm -rf /") or worse\n', fixed: '# โœ… FIXED: Never deserialize untrusted data with pickle\nimport json\nfrom marshmallow import Schema, fields, validate\n\nclass ProfileSchema(Schema):\n name = fields.Str(required=True, validate=validate.Length(max=100))\n bio = fields.Str(validate=validate.Length(max=500))\n\[email protected]("/load-profile", methods=["POST"])\ndef load_profile():\n # Use JSON + schema validation โ€” never pickle from untrusted sources\n schema = ProfileSchema()\n data = schema.load(request.json) # Validates and sanitises\n return jsonify(data)\n' }, java: { vuln: '// โŒ VULNERABLE: Java deserialization of user-provided data\nimport java.io.*;\n\n@PostMapping("/restore-session")\npublic void restoreSession(@RequestBody byte[] data) throws Exception {\n ObjectInputStream ois = new ObjectInputStream(\n new ByteArrayInputStream(data)\n );\n // Deserializing untrusted bytes can trigger RCE via gadget chains\n SessionData session = (SessionData) ois.readObject();\n sessionStore.restore(session);\n}\n', fixed: '// โœ… FIXED: Use JSON with a safe library\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n@PostMapping("/restore-session")\npublic void restoreSession(@RequestBody String jsonData) throws Exception {\n ObjectMapper mapper = new ObjectMapper();\n // Configure to reject all subtypes (no polymorphic deserialization)\n mapper.deactivateDefaultTyping();\n // Use strict type โ€” no arbitrary class loading\n SessionData session = mapper.readValue(jsonData, SessionData.class);\n sessionStore.restore(session);\n}\n' } } }, { id: 'A09', title: 'Logging Failures', desc: 'Without sufficient logging and monitoring, breaches cannot be detected and responded to. Attackers rely on the lack of monitoring to maintain persistence, pivot to other systems, and exfiltrate data.', vulns: ['Authentication events (login, logout, failures) not logged', 'Logs stored locally with no central aggregation or alerting', 'Sensitive data (passwords, PII) inadvertently written to logs'], fixes: ['Log all authentication events, access control failures, and input validation errors', 'Ship logs to an immutable centralised store (SIEM)', 'Never log sensitive data โ€” sanitise log messages'], code: { js: { vuln: '// โŒ VULNERABLE: No security logging\napp.post("/login", async (req, res) => {\n const user = await authenticate(req.body.email, req.body.password);\n if (user) {\n res.json({ token: generateToken(user) });\n } else {\n res.status(401).json({ error: "Invalid credentials" });\n // No log of failed attempt โ€” attacker can brute-force silently\n }\n});\n\n// Also bad: logging sensitive data\nconsole.log("Login attempt:", req.body); // Logs password in plaintext!\n', fixed: '// โœ… FIXED: Structured security logging\nconst logger = require("pino")({\n level: "info",\n redact: ["req.body.password", "req.body.token"] // Auto-redact\n});\n\napp.post("/login", async (req, res) => {\n const { email } = req.body;\n const user = await authenticate(email, req.body.password);\n if (user) {\n logger.info({ event: "auth.success", userId: user.id, ip: req.ip, email });\n res.json({ token: generateToken(user) });\n } else {\n logger.warn({ event: "auth.failure", email, ip: req.ip }); // Alert-worthy\n res.status(401).json({ error: "Invalid credentials" });\n }\n});\n' }, python: { vuln: '# โŒ VULNERABLE: Inadequate logging\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\n\[email protected]("/api/data")\n@login_required\ndef sensitive_data():\n data = get_sensitive_data()\n # No audit log of who accessed what\n # Debug level logs everything including tokens!\n logging.debug(f"Request: {request.headers}")\n return jsonify(data)\n', fixed: '# โœ… FIXED: Structured audit logging\nimport structlog\n\nlog = structlog.get_logger()\n\[email protected]("/api/data")\n@login_required\ndef sensitive_data():\n # Structured log with security context\n log.info("data.access",\n user_id=current_user.id,\n resource="sensitive_data",\n ip=request.remote_addr,\n method=request.method\n )\n data = get_sensitive_data()\n return jsonify(data)\n\n# Never log: passwords, tokens, PII, credit card numbers\n# structlog auto-redacts configured fields\n' }, java: { vuln: '// โŒ VULNERABLE: Logging sensitive data + insufficient coverage\n@PostMapping("/transfer")\npublic void transfer(@RequestBody TransferRequest req) {\n // Logs the full request including account numbers!\n log.debug("Transfer request: " + req);\n // No audit trail of who initiated the transfer\n bankService.transfer(req.getFrom(), req.getTo(), req.getAmount());\n}\n', fixed: '// โœ… FIXED: Audit logging without sensitive data\nimport org.slf4j.MDC;\n\n@PostMapping("/transfer")\n@PreAuthorize("isAuthenticated()")\npublic void transfer(@RequestBody TransferRequest req,\n @AuthenticationPrincipal UserDetails user) {\n // Set MDC context for all logs in this request\n MDC.put("userId", user.getUsername());\n MDC.put("requestId", req.getRequestId());\n // Log business event โ€” no account numbers or amounts in log message\n log.info("AUDIT: transfer.initiated from={} to={} userId={}",\n mask(req.getFrom()), mask(req.getTo()), user.getUsername());\n bankService.transfer(req.getFrom(), req.getTo(), req.getAmount());\n log.info("AUDIT: transfer.completed");\n}\n' } } }, { id: 'A10', title: 'SSRF', desc: 'Server-Side Request Forgery (SSRF) occurs when a web application fetches a remote resource without validating the user-supplied URL. An attacker can make the server connect to internal services or cloud metadata endpoints.', vulns: ['User-provided URL fetched without validation โ€” can target internal IPs', 'Cloud metadata endpoint (169.254.169.254) reachable via SSRF', 'URL redirects followed without checking the destination'], fixes: ['Validate URLs against an allowlist of permitted schemes and domains', 'Disable HTTP redirects in server-side HTTP clients', 'Use network controls to prevent app servers reaching internal ranges'], code: { js: { vuln: '// โŒ VULNERABLE: Unvalidated URL fetch\napp.post("/preview", async (req, res) => {\n const { url } = req.body;\n // Attack 1: url = "http://169.254.169.254/latest/meta-data/iam/..."\n // Attack 2: url = "http://internal-db:5432/"\n // Attack 3: url = "file:///etc/passwd"\n const response = await fetch(url);\n const html = await response.text();\n res.send(html);\n});\n', fixed: '// โœ… FIXED: URL allowlist validation\nconst ALLOWED_DOMAINS = ["api.trusted.com", "cdn.partner.com"];\n\nfunction isUrlAllowed(urlStr) {\n try {\n const url = new URL(urlStr);\n if (!["http:", "https:"].includes(url.protocol)) return false;\n return ALLOWED_DOMAINS.includes(url.hostname);\n } catch { return false; }\n}\n\napp.post("/preview", async (req, res) => {\n const { url } = req.body;\n if (!isUrlAllowed(url)) {\n return res.status(400).json({ error: "URL not permitted" });\n }\n const response = await fetch(url, { redirect: "manual" }); // No auto-redirect\n res.send(await response.text());\n});\n' }, python: { vuln: '# โŒ VULNERABLE: User-supplied URL directly fetched\nimport requests\n\[email protected]("/fetch")\ndef fetch_url():\n url = request.args.get("url")\n # Attacker: url=http://169.254.169.254/latest/meta-data/ (AWS metadata)\n # Or: url=http://10.0.0.5/admin (internal service)\n resp = requests.get(url, timeout=10)\n return resp.content\n', fixed: '# โœ… FIXED: Allowlist + DNS rebinding protection\nimport ipaddress\nimport socket\nimport requests\nfrom urllib.parse import urlparse\n\nALLOWED_DOMAINS = {"api.trusted.com", "cdn.partner.com"}\n\ndef is_safe_url(url: str) -> bool:\n parsed = urlparse(url)\n if parsed.scheme not in ("http", "https"):\n return False\n if parsed.hostname not in ALLOWED_DOMAINS:\n return False\n # Resolve IP and check it\'s not private/loopback\n try:\n ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname))\n return not (ip.is_private or ip.is_loopback or ip.is_link_local)\n except Exception:\n return False\n\[email protected]("/fetch")\ndef fetch_url():\n url = request.args.get("url", "")\n if not is_safe_url(url):\n return "URL not allowed", 400\n resp = requests.get(url, timeout=5, allow_redirects=False)\n return resp.content\n' }, java: { vuln: '// โŒ VULNERABLE: Unvalidated URL used in HttpClient\n@GetMapping("/proxy")\npublic String proxy(@RequestParam String url) throws Exception {\n // Attacker passes internal service URL\n HttpClient client = HttpClient.newHttpClient();\n HttpRequest req = HttpRequest.newBuilder(URI.create(url)).build();\n HttpResponse resp = client.send(req, BodyHandlers.ofString());\n return resp.body();\n}\n', fixed: '// โœ… FIXED: Allowlist + no redirects\n@GetMapping("/proxy")\npublic ResponseEntity proxy(@RequestParam String url) throws Exception {\n URI uri = URI.create(url);\n if (!ALLOWED_HOSTS.contains(uri.getHost())) {\n return ResponseEntity.badRequest().body("Host not allowed");\n }\n if (!List.of("http", "https").contains(uri.getScheme())) {\n return ResponseEntity.badRequest().body("Scheme not allowed");\n }\n HttpClient client = HttpClient.newBuilder()\n .followRedirects(HttpClient.Redirect.NEVER) // No auto-redirect\n .build();\n HttpRequest req = HttpRequest.newBuilder(uri).build();\n HttpResponse resp = client.send(req, BodyHandlers.ofString());\n return ResponseEntity.ok(resp.body());\n}\n' } } } ]; var currentLang = 'js'; var currentCat = 0; function buildNav() { var nav = document.getElementById('owasp-nav'); CATEGORIES.forEach(function(cat, i) { var btn = document.createElement('button'); btn.className = 'owasp-tab' + (i === 0 ? ' active' : ''); btn.textContent = cat.id + ': ' + cat.title; btn.onclick = function() { switchCat(i); }; nav.appendChild(btn); }); } function switchCat(idx) { currentCat = idx; document.querySelectorAll('.owasp-tab').forEach(function(t,i){ t.classList.toggle('active', i === idx); }); document.querySelectorAll('.category-panel').forEach(function(p,i){ p.classList.toggle('active', i === idx); }); } function switchLang(lang) { currentLang = lang; document.querySelectorAll('.lang-tab').forEach(function(t){ t.classList.toggle('active', t.dataset.lang === lang); }); document.querySelectorAll('.lang-panel').forEach(function(p){ p.classList.toggle('active', p.dataset.lang === lang); }); } function buildPanels() { var container = document.getElementById('panels-container'); CATEGORIES.forEach(function(cat, ci) { var div = document.createElement('div'); div.className = 'category-panel' + (ci === 0 ? ' active' : ''); var html = '
'; html += '' + cat.id + ''; html += '

' + cat.title + '

'; html += '

' + cat.desc + '

'; html += '
'; // Vuln + fix bullets html += '
'; html += '

โŒ Vulnerability Patterns

'; cat.vulns.forEach(function(v){ html += '
' + v + '
'; }); html += '
'; html += '

โœ… How to Fix

'; cat.fixes.forEach(function(f){ html += '
' + f + '
'; }); html += '
'; html += '
'; // Lang tabs html += '
'; html += '
'; ['js','python','java'].forEach(function(lang, li) { var labels = ['JavaScript', 'Python', 'Java']; html += ''; }); html += '
'; // Code panels for each lang ['js','python','java'].forEach(function(lang) { html += '
'; html += '
'; html += '
โŒ Vulnerable Code
'; html += '
โœ… Fixed Code
'; html += '
'; }); div.innerHTML = html; container.appendChild(div); // Inject code after DOM is set ['js','python','java'].forEach(function(lang) { var vc = document.getElementById('vc-' + ci + '-' + lang); var fc = document.getElementById('fc-' + ci + '-' + lang); if (vc && cat.code[lang]) { vc.textContent = cat.code[lang].vuln; } if (fc && cat.code[lang]) { fc.textContent = cat.code[lang].fixed; } }); }); } function switchLangPanel(btn, lang, catIdx) { var panel = document.querySelectorAll('.category-panel')[catIdx]; panel.querySelectorAll('.lang-tab').forEach(function(t){ t.classList.toggle('active', t.dataset.lang === lang); }); panel.querySelectorAll('.lang-panel').forEach(function(p){ p.classList.toggle('active', p.dataset.lang === lang); }); } buildNav(); buildPanels();