프로그래밍 언어/Go

Go: path vs path/filepath

path vs path/filepath

Go 에서 경로를 핸들링 할 수 있는 패키지에는 path, path/filepath 패키지가 있다. 이 둘은 쓰임새가 달라서 조심씨 사용하여야 하는데, 둘이 경로라는 대상을 다루지만, 실은 핸들링하는 대상이 다르다. 결론부터 말하자면 pathHTTP, FTP 경로 등 논리적인 경로를 핸들링한다, 즉 슬래쉬로 구분되는 경로에 대해서 다루며, 반면 path/filepath 패키지는 이름 그대로 실질적인 파일 경로를 취급하게 된다. Windows 같은 경우에는 C:\ 등으로 파일 경로가 시작되기도 하기 때문에 각별히 주의해야 할 필요가 있다.

 

data/hello.txt 파일에는 Hello, Go! 라는 텍스트가 입력되어 있다고 가정하고, 아래의 코드를 살펴보자. 파일을 열고 읽어들여 콘솔에 출력을 하는 것이 전부인데, 우리가 주목해서 보아야 하는 것은 바로 input, filepath.Base() 부분이다.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"path/filepath"
)

func main() {
	input := "hello.txt"
	p := filepath.Join("./data", filepath.Base(input))

	b, err := ioutil.ReadFile(p)
	if err != nil {
		log.Fatal(err)
	}

	// -> Hello, Go!
	fmt.Println(string(b))
}

input 변수에는 읽어들일 파일의 이름이 담겨있다. 하지만 만약 input 의 값이 ..\\main.go 처럼 된다면 어떨까? filepath.Base() 함수는 경로의 마지막 부분을 반환하기 때문에 우리가 기대하는 값은 ./data/main.go 이며 실제로 해당 경로의 파일을 읽으려고 시도한다. 하지만 아래의 이를 path.Base() 로 바꾸게 되면 놀랍게도 ..\\main.go 의 내용을 읽을 수 있게 된다.

// ...

func main() {
	input := "..\\main.go"
	p := filepath.Join("./data", path.Base(input))

	// ...
}

왜 이 둘을 구분하여야 하는가?

path 패키지의 설명을 보면 아래와 같이 나와있다. 즉, path 패키지는 위에서 언급한 것처럼 오직 슬래시로 구분되는 경로에 대해 취급해야 하며 Windows 에서 취급되는 역슬래시를 다루지 않는다고 하니까 그런 용도로는 하지말고, path/filepath 를 사용하라고 하고 있다.

Package path implements utility routines for manipulating slash-separated paths.

The path package should only be used for paths separated by forward slashes, such as the paths in URLs. This package does not deal with Windows paths with drive letters or backslashes; to manipulate operating system paths, use the path/filepath package.

따라서 우리는 파일 시스템에 대한 경로를 다루려면 반드시 path/filepath 패키지를 사용하자. path 패키지를 사용함으로써 발생하는 문제점은 파일 업로드 공격과 파일 다운로드 공격에 대해 보안 취약점을 드러내기 때문에 이를 웹서버에서 사용하게 되면 큰 문제가 될 수 있다.