Common Security Pitfalls in Software Development and How to Avoid Them
Security is often overlooked in the rush to deliver software quickly. Yet, a single vulnerability can compromise user data, cause financial losses, or damage a company’s reputation. In this blog, we’ll explore the most common security pitfalls in software development and provide practical strategies to avoid them.
1. Hard-Coded Secrets and Credentials
,
The Pitfall
Storing API keys, database passwords, or encryption secrets directly in the source code.
Example (Vulnerable):
db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/mydb")
,
How to Avoid
- Store secrets in environment variables or secret managers (e.g., AWS Secrets Manager, Vault).
- Use
.env
files for local development and never commit them to version control.
Example (Safe):
import "os"
dsn := os.Getenv("DB_DSN")
db, err := sql.Open("mysql", dsn)
2. SQL Injection
,
The Pitfall
Constructing SQL queries using unvalidated user input.
Example (Vulnerable):
query := "SELECT * FROM users WHERE username = '" + username + "'"
db.Query(query)
,
How to Avoid
Use prepared statements or parameterized queries:
stmt, _ := db.Prepare("SELECT * FROM users WHERE username=?")
stmt.Query(username)
3. Cross-Site Scripting (XSS)
,
The Pitfall
Displaying unescaped user input on web pages.
Example (Vulnerable in HTML template):
<p>Welcome, {{username}}</p>
,
How to Avoid
- Escape HTML content before rendering.
- Use frameworks like React or Angular that handle output encoding automatically.
Example (Safe with Go html/template):
import "html/template"
tmpl := template.Must(template.New("user").Parse(`<p>Welcome, {{.Username}}</p>`))
tmpl.Execute(w, data)
4. Cross-Site Request Forgery (CSRF)
,
The Pitfall
Attackers trick logged-in users into performing actions without consent.
,
How to Avoid
- Use anti-CSRF tokens in forms and state-changing requests.
- Validate Origin and Referrer headers.
- Prefer stateless authentication (JWT) for APIs.
Example (Gin middleware for CSRF):
import "github.com/utrack/gin-csrf"
r := gin.Default()
r.Use(csrf.Middleware(csrf.Options{
Secret: "a-32-byte-long-secret",
}))
5. Outdated Dependencies
,
The Pitfall
Using outdated packages exposes applications to known vulnerabilities.
,
How to Avoid
- Audit dependencies regularly using tools like Snyk or
go list -m -u all
. - Apply patches promptly.
Example (Check for outdated Go modules):
go list -u -m all
6. Poor Authentication and Authorization
,
The Pitfall
Weak password policies or missing role checks.
,
How to Avoid
- Enforce strong passwords.
- Implement multi-factor authentication (MFA).
- Validate authorization on every sensitive operation.
Example (bcrypt password hash in Go):
import "golang.org/x/crypto/bcrypt"
password := []byte("user-password")
hashedPassword, _ := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
7. Insufficient Logging and Monitoring
,
The Pitfall
Without logs or monitoring, attacks may go unnoticed.
,
How to Avoid
- Implement centralized logging.
- Set up alerts for suspicious behavior.
Example (Simple request logging in Go using Gorilla Mux):
import "log"
import "net/http"
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("Request: %s %s", r.Method, r.RequestURI)
next.ServeHTTP(w, r)
})
}
r := mux.NewRouter()
r.Use(loggingMiddleware)
8. Unencrypted Data in Transit or at Rest
,
The Pitfall
Transmitting sensitive information without encryption.
,
How to Avoid
- Use HTTPS/TLS for all communications.
- Encrypt sensitive data at rest (AES-256).
- Never store plaintext passwords; always hash them.
Example (TLS server in Go):
http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)