[go] Go 언어를 사용한 RESTful API의 인증과 보안

이 글은 Go 언어로 개발된 RESTful API의 인증과 보안에 대해 다룹니다.

목차

  1. 소개
  2. 인증
    1. 토큰 기반 인증
    2. 세션 기반 인증
  3. 보안
    1. HTTPS 사용
    2. 입력 데이터 검증
    3. SQL 인젝션 방지

소개

RESTful API는 많은 웹 서비스에서 사용되는 중요한 개발 방법론입니다. 그러나 API의 인증과 보안은 개발자에게 매우 중요한 주제입니다. Go 언어는 강력하고 안전한 웹 서비스 개발을 위한 도구를 제공합니다.

인증

인증은 API 사용자의 신원을 확인하는 과정입니다. Go 언어에서 RESTful API의 인증을 구현하는 데에는 여러 가지 방법이 있습니다.

토큰 기반 인증

토큰 기반 인증은 매 요청마다 발급된 토큰을 사용하여 사용자를 인증하는 방법입니다. 클라이언트는 토큰을 요청하고, 서버는 해당 토큰을 검증하여 요청을 처리합니다. 이는 세션 관리를 필요로 하지 않으며, 토큰의 유효성을 확인하기 위해 서버에 저장된 토큰을 비교하는 것이 가능합니다.

Go 언어에서는 jwt-go와 같은 라이브러리를 사용하여 토큰 기반 인증을 구현할 수 있습니다.

import (
    "github.com/dgrijalva/jwt-go"
)

func GenerateToken(user User) (string, error) {
    token := jwt.New(jwt.SigningMethodHS256)
    claims := token.Claims.(jwt.MapClaims)
    claims["id"] = user.ID
    claims["username"] = user.Username
    claims["exp"] = time.Now().Add(time.Hour * 24).Unix()

    tokenString, err := token.SignedString([]byte("secret-key"))

    if err != nil {
        return "", err
    }

    return tokenString, nil
}

func ValidateToken(tokenString string) (jwt.MapClaims, error) {
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return []byte("secret-key"), nil
    })

    if err != nil {
        return nil, err
    }

    claims, ok := token.Claims.(jwt.MapClaims)

    if !ok || !token.Valid {
        return nil, errors.New("invalid token")
    }

    return claims, nil
}

세션 기반 인증

세션 기반 인증은 세션 식별자를 사용하여 사용자를 인증하는 방법입니다. 클라이언트가 로그인하면 서버에서는 사용자의 세션 식별자를 생성하고, 이를 클라이언트에게 전달합니다. 클라이언트는 이 세션 식별자를 매 요청마다 전송하며, 서버는 이를 검증하여 요청을 처리합니다.

Go 언어에서는 gorilla/sessions와 같은 라이브러리를 사용하여 세션 기반 인증을 구현할 수 있습니다.

import (
    "github.com/gorilla/sessions"
)

var store = sessions.NewCookieStore([]byte("session-secret"))

func LoginHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session-name")
    session.Values["authenticated"] = true
    session.Save(r, w)
}

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        session, _ := store.Get(r, "session-name")
        if session.Values["authenticated"] == nil {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        next.ServeHTTP(w, r)
    })
}

보안

RESTful API의 보안은 인증 외에도 중요한 요소입니다. 아래에 Go 언어를 사용하여 RESTful API의 보안을 강화하는 몇 가지 방법을 소개합니다.

HTTPS 사용

HTTPS는 통신을 암호화하기 위해 SSL 인증서를 사용하는 프로토콜로, 데이터의 안전한 전송을 보장합니다. Go 언어에서 HTTPS를 사용하기 위해서는 SSL 인증서를 생성하고, 서버에 적용하는 작업이 필요합니다. 여러 인증 기관들이 무료로 인증서를 발급해주고 있으니 이를 활용할 수 있습니다.

func main() {
    http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
}

입력 데이터 검증

사용자로부터 받은 입력 데이터는 반드시 검증되어야 합니다. Go 언어에서는 validator와 같은 패키지를 사용하여 입력 데이터의 유효성 검사를 수행할 수 있습니다. 이를 통해 악성 코드나 잘못된 형식의 데이터를 걸러낼 수 있습니다.

import (
    "github.com/go-playground/validator"
)

type User struct {
    Username string `json:"username" validate:"required"`
    Password string `json:"password" validate:"required"`
}

func CreateUser(w http.ResponseWriter, r *http.Request) {
    var user User
    decoder := json.NewDecoder(r.Body)
    err := decoder.Decode(&user)
    if err != nil {
        http.Error(w, "Bad Request", http.StatusBadRequest)
        return
    }

    validate := validator.New()
    if err := validate.Struct(user); err != nil {
        http.Error(w, "Bad Request", http.StatusBadRequest)
        return
    }

    // 유효한 데이터 처리
}

SQL 인젝션 방지

SQL 인젝션은 사용자 입력을 악용하여 데이터베이스의 쿼리를 변조하는 공격 방법입니다. Go 언어에서는 go-sql-driver/mysql와 같은 라이브러리의 Prepared Statement를 사용하여 SQL 인젝션을 방지할 수 있습니다.

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

func GetUserByID(userID int) (*User, error) {
    db, err := sql.Open("mysql", "user:password@/dbname")
    if err != nil {
        return nil, err
    }
    defer db.Close()

    stmt, err := db.Prepare("SELECT username FROM users WHERE id = ?")
    if err != nil {
        return nil, err
    }
    defer stmt.Close()

    var username string
    err = stmt.QueryRow(userID).Scan(&username)
    if err != nil {
        return nil, err
    }

    user := &User{
        ID:       userID,
        Username: username,
    }

    return user, nil
}

결론

Go 언어를 사용하여 RESTful API의 인증과 보안을 구현할 수 있습니다. 토큰 기반 인증과 세션 기반 인증을 적절하게 선택하고, HTTPS 사용, 입력 데이터 검증, SQL 인젝션 방지 과정을 거쳐 더욱 안전한 웹 서비스를 개발할 수 있습니다.

참고 자료: