DEV Community

Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

API Security Best Practices with Go

Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool that helps you automatically index API endpoints across all your repositories. LiveAPI makes it easier to discover, understand, and interact with APIs in large infrastructures.


Securing your API is non-negotiable. Whether you're building public or internal services, unprotected endpoints can expose your system to data theft, misuse, or even total compromise.

This guide outlines essential API security principles, with practical Go code snippets using the Echo web framework where applicable.

1. Use Proper HTTP Methods

Only allow HTTP methods appropriate for each endpoint. Respond with 405 Method Not Allowed for disallowed methods.

e := echo.New()

// Proper usage
e.GET("/users/:id", getUser)
e.POST("/users", createUser)
e.PUT("/users/:id", updateUser)
e.DELETE("/users/:id", deleteUser)

// Middleware to handle unsupported methods
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        allowedMethods := map[string][]string{
            "/users":     {"POST"},
            "/users/:id": {"GET", "PUT", "DELETE"},
        }
        route := c.Path()
        method := c.Request().Method

        for path, methods := range allowedMethods {
            if echo.NewRouter().Find(method, path, c) != nil {
                return next(c)
            }
            if path == route {
                return echo.NewHTTPError(http.StatusMethodNotAllowed, "Method Not Allowed")
            }
        }
        return next(c)
    }
})
Enter fullscreen mode Exit fullscreen mode

2. Content-Type Validation

Validate the Content-Type header. Expect application/json for JSON APIs.

e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        if c.Request().Method == http.MethodPost || c.Request().Method == http.MethodPut {
            if c.Request().Header.Get(echo.HeaderContentType) != echo.MIMEApplicationJSON {
                return echo.NewHTTPError(http.StatusUnsupportedMediaType, "Only application/json allowed")
            }
        }
        return next(c)
    }
})
Enter fullscreen mode Exit fullscreen mode

3. Validate User Input

Use input validation to prevent injection attacks and ensure data integrity.

type CreateUserInput struct {
    Name  string `json:"name" validate:"required,min=3"`
    Email string `json:"email" validate:"required,email"`
}

func createUser(c echo.Context) error {
    var input CreateUserInput
    if err := c.Bind(&input); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, "Invalid request")
    }

    if err := c.Validate(&input); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    // Proceed safely
    return c.JSON(http.StatusOK, map[string]string{"status": "user created"})
}
Enter fullscreen mode Exit fullscreen mode

To enable validation:

import "github.com/go-playground/validator/v10"

type CustomValidator struct {
    validator *validator.Validate
}

func (cv *CustomValidator) Validate(i interface{}) error {
    return cv.validator.Struct(i)
}

e.Validator = &CustomValidator{validator: validator.New()}
Enter fullscreen mode Exit fullscreen mode

4. Use Authorization Header for Tokens

Tokens should be sent in the standard Authorization: Bearer <token> header—not in query parameters.

func tokenAuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        auth := c.Request().Header.Get("Authorization")
        if auth == "" || !strings.HasPrefix(auth, "Bearer ") {
            return echo.NewHTTPError(http.StatusUnauthorized, "Missing or invalid Authorization header")
        }

        token := strings.TrimPrefix(auth, "Bearer ")
        if !validateToken(token) {
            return echo.NewHTTPError(http.StatusUnauthorized, "Invalid token")
        }

        return next(c)
    }
}
Enter fullscreen mode Exit fullscreen mode

Avoid this:

GET /api/data?token=abc123  ❌
Enter fullscreen mode Exit fullscreen mode

5. Avoid Client-Side Encryption

Don’t rely on client-side encryption for sensitive data. Encrypt on the server side using robust libraries.

// Example: AES encryption
func encryptAES(plaintext, key string) (string, error) {
    block, err := aes.NewCipher([]byte(key))
    if err != nil {
        return "", err
    }
    plaintextBytes := []byte(plaintext)
    ciphertext := make([]byte, aes.BlockSize+len(plaintextBytes))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return "", err
    }
    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintextBytes)
    return base64.URLEncoding.EncodeToString(ciphertext), nil
}
Enter fullscreen mode Exit fullscreen mode

Keep keys and secrets on the server, never in the client-side code.

6. Use an API Gateway

Use tools like Kong, Traefik, or AWS API Gateway for enhanced security features:

  • JWT validation
  • IP whitelisting
  • Rate limiting
  • Request logging
  • TLS termination

Example: Kong Rate Limiting (declarative config)

services:
  - name: my-api
    url: http://myapi:8000
    routes:
      - name: my-api-route
        paths:
          - /v1/
        plugins:
          - name: rate-limiting
            config:
              minute: 100
              policy: local
Enter fullscreen mode Exit fullscreen mode

Final Tips

  • Always use HTTPS (TLS) to encrypt data in transit.
  • Enable logging and alerting for abnormal traffic.
  • Keep dependencies updated to patch known vulnerabilities.
  • Run security headers like X-Content-Type-Options: nosniff.

TL;DR Checklist

Security Feature Status
Proper HTTP methods
Content-Type validation
Input validation
Token via Authorization
No client-side encryption
API gateway used

API security is not a luxury — it’s a baseline. Build secure by default, and save yourself from firefighting later.


LiveAPI helps you get all your backend APIs documented in a few minutes.

With LiveAPI, you can generate interactive API docs that allow users to search and execute endpoints directly from the browser.

LiveAPI Demo

If you're tired of updating Swagger manually or syncing Postman collections, give it a shot.

Top comments (0)