fix: fix path-traversal bug (#229)

This commit is contained in:
ruokeqx 2022-09-08 16:48:42 +08:00 committed by GitHub
parent 690c346ee9
commit dcb0b5a186
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 19 deletions

View File

@ -28,6 +28,7 @@ var (
)
var (
StrBackSlash = []byte("\\")
StrSlash = []byte("/")
StrSlashSlash = []byte("//")
StrSlashDotDot = []byte("/..")

View File

@ -43,6 +43,7 @@ package protocol
import (
"bytes"
"path/filepath"
"sync"
"github.com/cloudwego/hertz/internal/bytesconv"
@ -318,9 +319,9 @@ func (u *URI) SetHostBytes(host []byte) {
//
// Examples:
//
// * For /foo/bar/baz.html path returns baz.html.
// * For /foo/bar/ returns empty byte slice.
// * For /foobar.js returns foobar.js.
// - For /foo/bar/baz.html path returns baz.html.
// - For /foo/bar/ returns empty byte slice.
// - For /foobar.js returns foobar.js.
func (u *URI) LastPathSegment() []byte {
path := u.Path()
n := bytes.LastIndexByte(path, '/')
@ -334,14 +335,14 @@ func (u *URI) LastPathSegment() []byte {
//
// The following newURI types are accepted:
//
// * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
// uri is replaced by newURI.
// * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
// the original scheme is preserved.
// * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
// of the original uri is replaced.
// * Relative path, i.e. xx?yy=abc . In this case the original RequestURI
// is updated according to the new relative path.
// - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
// uri is replaced by newURI.
// - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
// the original scheme is preserved.
// - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
// of the original uri is replaced.
// - Relative path, i.e. xx?yy=abc . In this case the original RequestURI
// is updated according to the new relative path.
func (u *URI) Update(newURI string) {
u.UpdateBytes(bytesconv.S2b(newURI))
}
@ -350,14 +351,14 @@ func (u *URI) Update(newURI string) {
//
// The following newURI types are accepted:
//
// * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
// uri is replaced by newURI.
// * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
// the original scheme is preserved.
// * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
// of the original uri is replaced.
// * Relative path, i.e. xx?yy=abc . In this case the original RequestURI
// is updated according to the new relative path.
// - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
// uri is replaced by newURI.
// - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
// the original scheme is preserved.
// - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
// of the original uri is replaced.
// - Relative path, i.e. xx?yy=abc . In this case the original RequestURI
// is updated according to the new relative path.
func (u *URI) UpdateBytes(newURI []byte) {
u.requestURI = u.updateBytes(newURI, u.requestURI)
}
@ -484,6 +485,18 @@ func normalizePath(dst, src []byte) []byte {
dst = addLeadingSlash(dst, src)
dst = decodeArgAppendNoPlus(dst, src)
// Windows server need to replace all backslashes with
// forward slashes to avoid path traversal attacks.
if filepath.Separator == '\\' {
for {
n := bytes.IndexByte(dst, '\\')
if n < 0 {
break
}
dst[n] = '/'
}
}
// remove duplicate slashes
b := dst
bSize := len(b)

View File

@ -43,6 +43,7 @@ package protocol
import (
"fmt"
"path/filepath"
"reflect"
"testing"
@ -228,3 +229,20 @@ func testURIFullURI(t *testing.T, scheme, host, path, hash string, args *Args, e
t.Fatalf("Unexpected URI: %q. Expected %q", uri, expectedURI)
}
}
func TestParsePathWindows(t *testing.T) {
t.Parallel()
testParsePathWindows(t, "/../../../../../foo", "/foo")
testParsePathWindows(t, "/..\\..\\..\\..\\..\\foo", "/foo")
testParsePathWindows(t, "/..%5c..%5cfoo", "/foo")
}
func testParsePathWindows(t *testing.T, path, expectedPath string) {
var u URI
u.Parse(nil, []byte(path))
parsedPath := u.Path()
if filepath.Separator == '\\' && string(parsedPath) != expectedPath {
t.Fatalf("Unexpected Path: %q. Expected %q", parsedPath, expectedPath)
}
}