Securing Web Applications Against Common Vulnerabilities
Building a beautiful user interface and a fast backend is fantastic, but it means nothing if your system is vulnerable to security exploits. Web security is a core engineering requirement, not an afterthought.
Let's explore the three most common web vulnerabilities—**Cross-Site Scripting (XSS)**, **Cross-Site Request Forgery (CSRF)**, and **SQL Injection**—and look at how to implement secure defenses!
### 1. Mitigating Cross-Site Scripting (XSS)
XSS occurs when an application receives untrusted data and displays it on a page without sanitizing or escaping it, allowing malicious scripts to execute in the user's browser.
#### The Defense:
* **Always escape output:** In Vue 3, using `{{ mustache }}` syntax automatically escapes text content. Only use `v-html` with extreme caution and never with user-supplied comments.
* **Implement a strict Content Security Policy (CSP):** Prevent inline scripts and unauthorized API requests by serving a secure header:
```http
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;
```
### 2. Preventing Cross-Site Request Forgery (CSRF)
CSRF occurs when a malicious website tricks a logged-in user’s browser into sending an unauthorized HTTP request (like making a purchase or changing an email address) to your application.
#### The Defense:
* **Use SameSite Cookies:** Configure all authentication session cookies with `SameSite=Lax` or `SameSite=Strict` and the `Secure` flag.
* **Double-Submit Cookie Pattern:** For state-changing POST requests (e.g. adding comments), generate a unique token, store it in a cookie, and verify that it matches a custom request header or hidden input field on submission:
```go
func CSRFMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
cookieToken, err := r.Cookie("csrf_token")
formToken := r.FormValue("csrf_token")
if err != nil || cookieToken.Value != formToken {
http.Error(w, "Invalid CSRF Token", http.StatusForbidden)
return
}
}
next.ServeHTTP(w, r)
})
}
```
### 3. Preventing SQL Injection
SQL Injection occurs when user input is concatenated directly into SQL query strings, allowing attackers to manipulate queries and steal database records.
#### The Defense:
* **Parameterized Queries:** Never use string formatting (`fmt.Sprintf`) to embed parameters. Always use placeholder tokens (`?`) provided by your database driver:
```go
// ❌ HIGHLY VULNERABLE!
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", input)
// ✓ parameter-safe and secure!
query := "SELECT * FROM users WHERE username = ?"
row := db.QueryRowContext(ctx, query, input)
```
Enforcing these parameterized guidelines completely removes the risk of SQL injection!
// Read next
Related articles
Building Resilient Microservices with Go and gRPC
Discover the architecture secrets behind high-throughput microservices using Go and gRPC. Learn about serialization effi...
Mastering SQLite for Production Web Applications
Think SQLite is just a toy database? Think again. Learn how to configure WAL mode, handle locking, and scale SQLite to m...
Architecting High-Performance API Gateways in Go
Learn how to design a blazing fast API gateway in Go. Implement the token bucket rate-limiting algorithm, reverse proxie...
// Reader response
Comments
This article has no comments yet.
// Author
Hoàng Ngô Anh Đức
Senior Full-Stack Engineer & Software Architect
Tôi là một kỹ sư phần mềm giàu kinh nghiệm chuyên thiết kế và xây dựng các hệ thống web hiện đại, scalable backend sử dụng Go, Vue.js, TypeScript và kiến trúc đám mây Cloud. Đam mê chia sẻ kiến thức kỹ thuật và tối ưu hiệu năng phần mềm.