diff --git a/strings.go b/strings.go index 3cec8ed0e1..23af2226e4 100644 --- a/strings.go +++ b/strings.go @@ -7,25 +7,25 @@ var ( ) var ( - strSlash = []byte("/") - strSlashSlash = []byte("//") - strSlashDotDot = []byte("/..") - strSlashDotSlash = []byte("/./") - strSlashDotDotSlash = []byte("/../") - strBackSlashDotDot = []byte(`\..`) - strBackSlashDotBackSlash = []byte(`\.\`) - strSlashDotDotBackSlash = []byte(`/..\`) - strBackSlashDotDotBackSlash = []byte(`\..\`) - strCRLF = []byte("\r\n") - strHTTP = []byte("http") - strHTTPS = []byte("https") - strHTTP10 = []byte("HTTP/1.0") - strHTTP11 = []byte("HTTP/1.1") - strColon = []byte(":") - strColonSlashSlash = []byte("://") - strColonSpace = []byte(": ") - strCommaSpace = []byte(", ") - strGMT = []byte("GMT") + strSlash = []byte("/") + strSlashSlash = []byte("//") + strSlashDotDot = []byte("/..") + strSlashDotSlash = []byte("/./") + strSlashDotDotSlash = []byte("/../") + //strBackSlashDotDot = []byte(`\..`) + //strBackSlashDotBackSlash = []byte(`\.\`) + //strSlashDotDotBackSlash = []byte(`/..\`) + //strBackSlashDotDotBackSlash = []byte(`\..\`) + strCRLF = []byte("\r\n") + strHTTP = []byte("http") + strHTTPS = []byte("https") + strHTTP10 = []byte("HTTP/1.0") + strHTTP11 = []byte("HTTP/1.1") + strColon = []byte(":") + strColonSlashSlash = []byte("://") + strColonSpace = []byte(": ") + strCommaSpace = []byte(", ") + strGMT = []byte("GMT") strResponseContinue = []byte("HTTP/1.1 100 Continue\r\n\r\n") diff --git a/uri.go b/uri.go index 19ceb694ce..d260328791 100644 --- a/uri.go +++ b/uri.go @@ -574,6 +574,11 @@ func normalizePath(dst, src []byte) []byte { dst = dst[:0] dst = addLeadingSlash(dst, src) dst = decodeArgAppendNoPlus(dst, src) + // replacing backslash, if you use windows(backslash) + // fix: Path Traversal Attacks on Windows + if filepath.Separator == '\\' { + dst = replaceSlashes(dst) + } // remove duplicate slashes b := dst @@ -626,61 +631,61 @@ func normalizePath(dst, src []byte) []byte { } b = b[:nn+1] } - - if filepath.Separator == '\\' { - // remove \.\ parts - for { - n := bytes.Index(b, strBackSlashDotBackSlash) - if n < 0 { - break + /* + if filepath.Separator == '\\' { + // remove \.\ parts + for { + n := bytes.Index(b, strBackSlashDotBackSlash) + if n < 0 { + break + } + nn := n + len(strSlashDotSlash) - 1 + copy(b[n:], b[nn:]) + b = b[:len(b)-nn+n] } - nn := n + len(strSlashDotSlash) - 1 - copy(b[n:], b[nn:]) - b = b[:len(b)-nn+n] - } - // remove /foo/..\ parts - for { - n := bytes.Index(b, strSlashDotDotBackSlash) - if n < 0 { - break - } - nn := bytes.LastIndexByte(b[:n], '/') - if nn < 0 { - nn = 0 + // remove /foo/..\ parts + for { + n := bytes.Index(b, strSlashDotDotBackSlash) + if n < 0 { + break + } + nn := bytes.LastIndexByte(b[:n], '/') + if nn < 0 { + nn = 0 + } + nn++ + n += len(strSlashDotDotBackSlash) + copy(b[nn:], b[n:]) + b = b[:len(b)-n+nn] } - nn++ - n += len(strSlashDotDotBackSlash) - copy(b[nn:], b[n:]) - b = b[:len(b)-n+nn] - } - // remove /foo\..\ parts - for { - n := bytes.Index(b, strBackSlashDotDotBackSlash) - if n < 0 { - break - } - nn := bytes.LastIndexByte(b[:n], '/') - if nn < 0 { - nn = 0 + // remove /foo\..\ parts + for { + n := bytes.Index(b, strBackSlashDotDotBackSlash) + if n < 0 { + break + } + nn := bytes.LastIndexByte(b[:n], '/') + if nn < 0 { + nn = 0 + } + n += len(strBackSlashDotDotBackSlash) - 1 + copy(b[nn:], b[n:]) + b = b[:len(b)-n+nn] } - n += len(strBackSlashDotDotBackSlash) - 1 - copy(b[nn:], b[n:]) - b = b[:len(b)-n+nn] - } - // remove trailing \foo\.. - n := bytes.LastIndex(b, strBackSlashDotDot) - if n >= 0 && n+len(strSlashDotDot) == len(b) { - nn := bytes.LastIndexByte(b[:n], '/') - if nn < 0 { - return append(dst[:0], strSlash...) + // remove trailing \foo\.. + n := bytes.LastIndex(b, strBackSlashDotDot) + if n >= 0 && n+len(strSlashDotDot) == len(b) { + nn := bytes.LastIndexByte(b[:n], '/') + if nn < 0 { + return append(dst[:0], strSlash...) + } + b = b[:nn+1] } - b = b[:nn+1] } - } - + */ return b } diff --git a/uri_test.go b/uri_test.go index 5996bcb19c..23d454196c 100644 --- a/uri_test.go +++ b/uri_test.go @@ -151,6 +151,62 @@ func testURIUpdate(t *testing.T, base, update, result string) { } } +func TestURIPathNormalize_windows(t *testing.T) { + if runtime.GOOS != "windows" { + t.SkipNow() + } + + t.Parallel() + + var u URI + + testURIPathNormalize(t, &u, "\\aa\\\\bb", "/aa/bb") + + // triple slash + testURIPathNormalize(t, &u, "\\x\\\\\\y\\", "/x/y/") + + // multi slashes + testURIPathNormalize(t, &u, "\\abc\\\\de\\\\\\fg\\\\\\\\", "/abc/de/fg/") + + // encoded slashes + testURIPathNormalize(t, &u, "\\xxxx%2fyyy%2f%2F%2F", "/xxxx/yyy/") + + // dotdot + testURIPathNormalize(t, &u, "\\aaa\\..", "/") + + // dotdot with trailing slash + testURIPathNormalize(t, &u, "\\xxx\\yyy\\..\\", "/xxx/") + + // multi dotdots + testURIPathNormalize(t, &u, "\\aaa\\bbb\\ccc\\..\\..\\ddd", "/aaa/ddd") + + // dotdots separated by other data + testURIPathNormalize(t, &u, "\\a\\b\\..\\c\\d\\..\\e\\..", "/a/c/") + + // too many dotdots + testURIPathNormalize(t, &u, "\\aaa\\..\\..\\..\\..\\xxx", "/xxx") + testURIPathNormalize(t, &u, "\\..\\..\\..\\..\\..\\..", "/") + testURIPathNormalize(t, &u, "\\..\\..\\..\\..\\..\\..\\", "/") + + // encoded dotdots + testURIPathNormalize(t, &u, "\\aaa%2Fbbb%2F%2E.%2Fxxx", "/aaa/xxx") + + // double slash with dotdots + testURIPathNormalize(t, &u, "\\aaa\\\\\\\\..\\\\b", "/b") + + // fake dotdot + testURIPathNormalize(t, &u, "\\aaa\\..bbb\\ccc\\..", "/aaa/..bbb/") + + // single dot + testURIPathNormalize(t, &u, "\\a\\.\\b\\.\\.\\c\\.\\d.html", "/a/b/c/d.html") + testURIPathNormalize(t, &u, ".\\foo\\", "/foo/") + testURIPathNormalize(t, &u, ".\\..\\..\\.\\..\\..\\aaa\\bbb\\..\\..\\..\\.\\.\\..\\", "/") + testURIPathNormalize(t, &u, ".\\a\\.\\..\\.\\..\\b\\.\\foo.html", "/b/foo.html") + testURIPathNormalize(t, &u, `.\a\.\../.\..\b\.\foo.html`, "/b/foo.html") + testURIPathNormalize(t, &u, `.\a\./..\.\..\b\.\foo.html`, "/b/foo.html") + testURIPathNormalize(t, &u, `.\a\./..%5c.\..\b\.\foo.html`, "/b/foo.html") +} + func TestURIPathNormalize(t *testing.T) { if runtime.GOOS == "windows" { t.SkipNow() diff --git a/uri_unix.go b/uri_unix.go index 1226fc97a9..f1c0c040f7 100644 --- a/uri_unix.go +++ b/uri_unix.go @@ -10,3 +10,7 @@ func addLeadingSlash(dst, src []byte) []byte { return dst } + +func replaceSlashes(dst []byte) []byte { + return dst +} diff --git a/uri_windows.go b/uri_windows.go index 46f4b07a81..45d29f3dcb 100644 --- a/uri_windows.go +++ b/uri_windows.go @@ -9,3 +9,12 @@ func addLeadingSlash(dst, src []byte) []byte { return dst } + +func replaceSlashes(dst []byte) []byte { + for i := range dst { + if dst[i] == '\\' { + dst[i] = '/' + } + } + return dst +} diff --git a/uri_windows_test.go b/uri_windows_test.go index bec110e392..5fc32ae3c5 100644 --- a/uri_windows_test.go +++ b/uri_windows_test.go @@ -8,7 +8,7 @@ func TestURIPathNormalizeIssue86(t *testing.T) { // see https://github.com/valyala/fasthttp/issues/86 var u URI - testURIPathNormalize(t, &u, `C:\a\b\c\fs.go`, `C:\a\b\c\fs.go`) + testURIPathNormalize(t, &u, `C:\a\b\c\fs.go`, `C:/a/b/c/fs.go`) testURIPathNormalize(t, &u, `a`, `/a`)