APIs are a core part of modern software, but they are also vulnerable to attacks. Here’s how to secure your APIs using Go (Golang) with practical examples.
1. Enforce HTTPS
Always use HTTPS to encrypt data in transit.
- Example:
Usehttp.ListenAndServeTLS
to enforce HTTPS in Go:
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, HTTPS!"))
})
log.Fatal(http.ListenAndServeTLS(":443", "server.crt", "server.key", nil))
}
2. Use Strong Authentication and Authorization
Ensure only authorized users and systems can access your APIs.
- Example:
Validate JWT tokens in Go using thegithub.com/golang-jwt/jwt
library:
package main
import (
"fmt"
"net/http"
"github.com/golang-jwt/jwt/v5"
)
var secretKey = []byte("your-secret-key")
func validateToken(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tokenString := r.Header.Get("Authorization")
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return secretKey, nil
})
if err != nil || !token.Valid {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
http.Handle("/", validateToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Authenticated"))
})))
http.ListenAndServe(":8080", nil)
}
3. Validate Input Data
Sanitize and validate inputs to prevent injection attacks.
- Example:
Use Go’svalidator
package for validation:
package main
import (
"encoding/json"
"net/http"
"github.com/go-playground/validator/v10"
)
type User struct {
Email string `json:"email" validate:"required,email"`
}
var validate = validator.New()
func main() {
http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
var user User
json.NewDecoder(r.Body).Decode(&user)
err := validate.Struct(user)
if err != nil {
http.Error(w, "Invalid input", http.StatusBadRequest)
return
}
w.Write([]byte("Valid input"))
})
http.ListenAndServe(":8080", nil)
}
4. Rate Limit and Throttle Requests
Prevent abuse by limiting API calls.
- Example:
Usegolang.org/x/time/rate
for rate limiting:
package main
import (
"net/http"
"golang.org/x/time/rate"
)
var limiter = rate.NewLimiter(1, 3) // 1 request per second with a burst of 3
func rateLimitMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
http.Handle("/", rateLimitMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome"))
})))
http.ListenAndServe(":8080", nil)
}
5. Monitor and Log API Activity
Log requests and responses for debugging and suspicious activity.
- Example:
Use Go’slog
package to log activity:
package main
import (
"log"
"net/http"
)
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s %s", r.Method, r.URL.Path, r.RemoteAddr)
next.ServeHTTP(w, r)
})
}
func main() {
http.Handle("/", loggingMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Logged"))
})))
http.ListenAndServe(":8080", nil)
}
6. Use Secure API Gateways
Integrate your API with secure gateways for centralized security and monitoring.
- Example:
Use NGINX or AWS API Gateway for security, applying rate limits and authentication layers.
7. Avoid Exposing Sensitive Data
Ensure sensitive data is not exposed in logs or API responses.
- Example:
Mask sensitive data in JSON responses:
package main
import (
"encoding/json"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Password string `json:"-"` // Exclude from JSON
}
func main() {
http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
user := User{ID: 1, Name: "John Doe", Password: "secret123"}
json.NewEncoder(w).Encode(user)
})
http.ListenAndServe(":8080", nil)
}
8. Implement CORS Policies
Control cross-origin requests to your API.
- Example:
Use Go’sgithub.com/rs/cors
package:
package main
import (
"net/http"
"github.com/rs/cors"
)
func main() {
c := cors.New(cors.Options{
AllowedOrigins: []string{"https://trusted.com"},
AllowCredentials: true,
})
handler := c.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("CORS Enabled"))
}))
http.ListenAndServe(":8080", handler)
}
9. Secure API Endpoints
Restrict sensitive routes to authorized users.
- Example:
Add middleware to check user roles:
package main
import (
"net/http"
)
func adminMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Role") != "admin" {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
http.Handle("/admin", adminMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Admin Access"))
})))
http.ListenAndServe(":8080", nil)
}
10. Scan for Vulnerabilities
Run security scans regularly to find and fix vulnerabilities.
- Example:
Use tools like OWASP ZAP or Gosec:
go install github.com/securego/gosec/v2/cmd/gosec@latest
gosec ./...
11. Protect Against DDoS Attacks
Mitigate DDoS attacks using rate limiting and IP whitelisting.
- Example:
Use Cloudflare to set up DDoS protection for your API endpoint.
12. Keep APIs Updated
Regularly update your dependencies and Go runtime.
- Example:
Usego get -u
to update dependencies:
go get -u all
Conclusion
Securing APIs in Go involves a mix of best practices and practical implementation. Use these examples to start protecting your APIs effectively.