{} The Go Reference

Defense · Security · Beginner

The OWASP Top 10

The industry's reference list of the most critical web app security risks — what each category means, how it shows up in Go, and the defensive habit that neutralizes it.

Defense Beginner ⏱ 4 min read Complete

🩺 Analogy

The OWASP Top 10 is the security equivalent of “wash your hands, don’t smoke, wear a seatbelt.” It isn’t the whole of medicine — but the handful of habits on the list prevent the overwhelming majority of harm. If a web app falls to one of these ten, it’s almost never because the attack was clever; it’s because a basic, well-known precaution was skipped. Learn the ten, build the reflex, and you’ve closed the doors attackers try first.

What it is

The OWASP Top 10 is a community-driven awareness document from the Open Worldwide Application Security Project, refreshed every few years from real-world breach and testing data. It names the most critical categories of web application risk — a baseline checklist every developer should be able to defend against. It is the floor, not the ceiling: cover it first, then go deeper with the ASVS.

The ten (2021 edition), with the Go defense

graph TD
A1["A01 Broken Access Control"] --> D1["enforce authz server-side, deny by default"]
A2["A02 Cryptographic Failures"] --> D2["TLS everywhere, hash passwords, no weak crypto"]
A3["A03 Injection (+XSS)"] --> D3["parameterize queries, html/template auto-escape"]
A4["A04 Insecure Design"] --> D4["threat-model; secure defaults"]
A5["A05 Security Misconfiguration"] --> D5["hardened config, no defaults, least privilege"]
#CategoryGo-flavored defense
A01Broken Access ControlCheck authorization on every request; deny by default; never trust client-supplied IDs/roles → authn & authz
A02Cryptographic FailuresTLS in transit; hash passwords with bcrypt/argon2; avoid weak/legacy crypto → hashing, TLS & PKI
A03Injection (incl. XSS)Parameterized queries (database/sql placeholders); html/template auto-escaping → injection
A04Insecure DesignThreat-model early; choose secure defaults; rate-limit sensitive flows
A05Security MisconfigurationNo default creds, minimal surface, security headers → hardening
A06Vulnerable & Outdated Componentsgovulncheck, pin & scan dependencies → supply chain
A07Identification & Auth FailuresStrong session mgmt, MFA, lockout → authn & authz
A08Software & Data Integrity FailuresVerify signatures/digests; secure CI/CD; guard deserialization
A09Security Logging & Monitoring FailuresLog security events, alert, don’t log secrets
A10Server-Side Request Forgery (SSRF)Allowlist outbound destinations → SSRF

See it: context-aware escaping kills XSS (A03)

The single highest-leverage XSS defense in Go is using html/template (not text/template). It’s context-aware: the same untrusted value is escaped differently depending on where it lands. This runs here:

xss_defense.go — editable & runnable
package main

import (
"html/template"
"os"
)

// One piece of attacker-controlled input...
const userInput = "<script>steal()</script>"

func main() {
// html/template auto-escapes by CONTEXT. The same value is encoded
// differently for HTML body, attribute, and URL positions.
t := template.Must(template.New("p").Parse(
	"<p>{{.}}</p>\n" +
		"<a title=\"{{.}}\">link</a>\n" +
		"<a href=\"/search?q={{.}}\">go</a>\n"))

_ = t.Execute(os.Stdout, userInput)
// The <script> tag is neutralized in every context — it can never
// execute. With text/template it would render raw and run. Never
// use text/template to build HTML, and never wrap untrusted data
// in template.HTML to "fix" escaping.
}

The <script> is rendered as inert text in the body, attribute, and URL — it can never execute. This is why “use html/template” is the canonical Go answer to A03’s XSS half; the SQL half is “use parameterized queries” (covered in injection).

🐹 Go's stdlib already ships most of the Top-10 defenses

A lot of the Top 10 is “use the safe stdlib path instead of the dangerous one.” database/sql placeholders give you parameterized queries (A03); html/template gives context-aware escaping (A03); crypto/tls gives transport encryption (A02); golang.org/x/crypto/bcrypt gives password hashing (A02); crypto/subtle.ConstantTimeCompare avoids timing leaks (A02/A07); and govulncheck flags vulnerable dependencies (A06). The Go-idiomatic security posture is: reach for the standard, safe primitive — the footguns (string-concatenated SQL, text/template for HTML, rolling your own crypto) are the deviations.

⚠️ The Top 10 is a floor, not a finish line

Two traps. First, treating the list as complete — it’s an awareness baseline, deliberately only ten categories; real coverage needs threat modeling, the ASVS, dependency scanning, and testing on top. Second, defending against the label instead of the root cause — e.g. “fixing” injection by blacklisting quotes and angle brackets instead of parameterizing and context-encoding. Blacklists are bypassed routinely; structural fixes (separate data from code) are what actually hold. Cover the ten, but understand why each defense works.

See also

Next: the request-forgery family at #10 — SSRF & request forgery.

Check your understanding

Score: 0 / 5

1. What is the OWASP Top 10?

The OWASP Top 10 is a community-driven awareness document from the Open Worldwide Application Security Project, refreshed every few years from real-world data. It names the most critical categories of web app risk (injection, broken access control, etc.). It's not a law or a tool list — it's a baseline checklist: if you can't defend against the Top 10, deeper hardening is premature. Treat it as the floor, not the ceiling.

2. In the current Top 10, which category sits at #1 — the most prevalent serious risk?

Broken Access Control rose to #1 in the 2021 list: the most common serious flaw is letting a user do something they shouldn't — read another user's data by editing an `/orders/123` ID (IDOR), reach an admin endpoint without the admin role, or escalate privileges. The defense is to enforce authorization on every request server-side, deny by default, and never trust a client-supplied identity or role.

3. The Top 10 renamed 'Injection' to fold in XSS. What's the unifying root cause of injection flaws?

Injection (SQL, OS command, LDAP, and XSS) all share one root cause: untrusted data gets mixed into an interpreter's instructions and is executed as code. The reliable fix is structural separation — parameterized queries so data can never become SQL, and context-aware output encoding (e.g. Go's html/template auto-escaping) so data can never become script. Trying to 'sanitize' by blacklisting bad characters is fragile and routinely bypassed.

4. How does Go's html/template package defend against XSS by default?

html/template (distinct from text/template!) tracks the context where each `{{.}}` action sits and applies the correct escaping automatically — HTML-escaping in body text, attribute-escaping inside attributes, JS-escaping inside <script>, URL-escaping in href, etc. This context-aware auto-escaping neutralizes the vast majority of reflected/stored XSS for free. The classic Go footgun is reaching for text/template (no escaping) to render HTML, or using template.HTML to mark untrusted data as safe.

5. What's the correct way to USE the OWASP Top 10 in a project?

The Top 10 is a starting point: it raises awareness of the highest-impact categories and anchors threat modeling, code review, and training. But it's explicitly a minimum, not a full program — OWASP itself points teams to the Application Security Verification Standard (ASVS) for thorough requirements, plus testing (SAST/DAST), dependency scanning, and secure design. Cover the Top 10 first; then go deeper.

Comments

Sign in with GitHub to join the discussion.