본문으로 바로가기
728x90
반응형
728x170

GO를 사용하여 서버를 구축하고, 운영중에 있는데 문뜩 외부에서 Postman을 사용하여 무분별한 Post가 이뤄질 때, 

우리의 데이터를 파싱할 때 이것을 막는 방법이 없을까? 생각해보게 되었습니다.

 

물론, 내부에 필수값이 존재하기 때문에 여기서 1차적으로 걸러지지만 이런것도 언젠간 뚫리기 마련이기 때문에 좀 더 단단한 무언가가 있었으면 좋겠다 생각이 들었습니다.

 

물론 완벽한 보안이라는 것은 존재하지 않지만 최소한의 방어벽을 둘러보고 싶었습니다.

 

먼저 찾아본 것은 'X-Csrf-Token'였습니다. 거기다 고릴라 안에 있던 패키지였기 때문에 이거다! 싶었죠.

https://github.com/gorilla/csrf

 

GitHub - gorilla/csrf: gorilla/csrf provides Cross Site Request Forgery (CSRF) prevention middleware for Go web applications & s

gorilla/csrf provides Cross Site Request Forgery (CSRF) prevention middleware for Go web applications & services 🔒 - GitHub - gorilla/csrf: gorilla/csrf provides Cross Site Request Forgery (CSR...

github.com

그렇게 제 머릿속엔 온갖 행복회로들이 탁!하고 켜지게 되었고

package main

import (
    "github.com/gorilla/csrf"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key"))

    api := r.PathPrefix("/api").Subrouter()
    api.Use(csrfMiddleware)
    api.HandleFunc("/user/{id}", GetUser).Methods("GET")

    http.ListenAndServe(":8000", r)
}

func GetUser(w http.ResponseWriter, r *http.Request) {
    // Authenticate the request, get the id from the route params,
    // and fetch the user from the DB, etc.

    // Get the token and pass it in the CSRF header. Our JSON-speaking client
    // or JavaScript framework can now read the header and return the token in
    // in its own "X-CSRF-Token" request header on the subsequent POST.
    w.Header().Set("X-CSRF-Token", csrf.Token(r))
    b, err := json.Marshal(user)
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }

    w.Write(b)
}​

깃 내부에 있는 사용법도 저렇게 나와있어서 '아~ 그냥 w.Header()에 추가만 해주면 되겠구나' 싶었죠.

그렇게 코딩을 하였고 

 

컴파일 후 실행을 하는데 아뿔사.. 403 forbidden Error가 뜨고 맙니다.. 역시 갖다 붙여서 다 되면 개발자가 있을 필요가 없죠 허허..

그 순간 제 행복회로가 불타는 것이 느껴지기 시작했고, csrf 패키지의 내부 코드를 살펴보았습니다.

동작하는 원리는 Post로 받은 헤더값이 다르면 튕겨내고, 맞으면 받는 원리였죠.

거기다 체크하는 헤더값은 하나로 고정이였는데 제가 원했던 건 체크하는 헤더의 value값이 변하길 원했습니다. 

그래서 csrf패키지는 아쉽게도 쓰지 못했죠 ㅠㅠ

 

그러다 스택오버플로우에 cors패키지를 사용하여 하는 부분이 나오길래 cors 패키지를 사용하여 시도를 해보았습니다.

corHandler := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
		AllowedMethods: []string{"POST", "GET"},
		AllowedHeaders: []string{"Content-Type", "X-Csrf-Token", "Content-Length", "Accept-Encoding", "Authorization", "Access-Control-Allow-Credentials",
			"Access-Control-Allow-Origin"},
		AllowCredentials: true,
		MaxAge:           0,
		Debug:            true,
})

이러한 형태의 코드인데요. 물론, cors 처리를 하지 않은 것은 아니지만 이 패키지 내에 저런 역할을 해주는게 있나? 싶어 해보았습니다.

 

결과는 '잘 동작한다.' 였어요. 하지만 X-Csrf-Token 헤더가 존재하지 않음에도 정상적으로 동작하는게 문제였죠.

왜 그럴까? 하고 알아보니 AllowedHeaders 부분은 allow-headers의 검증은 허용되지 않는 header의 유무를 체크하는 것이기 때문에 그 헤더값이 있든, 없든 reject를 시켜주는게 아니였던 것이죠.

 

그래서 결론은 따로 handler를 만들어서 처리해주는 방법 밖에 없었습니다.

func Handler(w http.ResponseWriter, r *http.Request) {
	r.Header.Get("exampleHeader")
}

그래서 데이터를 읽어오는 부분에서 먼저 체크하려는 헤더를 셋팅해주었습니다.

그 후에 파일을 하나 만들어서 키값을 넣고, 

ioutil.ReadFile(파일경로)

를 통해 해더 value값을 읽어와 POST로 받은 해더 값을 체크하여 같으면 처리, 틀리면 데이터를 쌓지 않는 식으로 체크하는 핸들러를 만들어 처리하였습니다.

 

이렇게 해서 Header가 존재하지 않을 때, Header value값이 존재하지 않을 때를 처리하여 서버의 보안을 한 층 두껍게 세울 수 있게 되었습니다.

728x90
반응형
그리드형

댓글을 달아 주세요