Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent double decoding of % in url params #17997

Merged
merged 4 commits into from
Dec 16, 2021

Conversation

zeripath
Copy link
Contributor

@zeripath zeripath commented Dec 15, 2021

There was an unfortunate regression in #14293 which has led to the double decoding
of url parameter elements if they contain a '%'. This is due to an issue
with the way chi decodes its RoutePath. In detail the problem lies in
mux.go where the routeHTTP path uses the URL.RawPath or even the
URL.Path instead of the escaped path to do routing.

This PR simply forcibly sets the routePath to that of the EscapedPath.

Fix #17938

⚠️ BREAKING ⚠️

Although this fixes a bug - users may have worked around this bug by creating links that would make this work - especially in the wiki. As users may need to be aware of this I am marking this PR as breaking in order to highlight this issue.

Signed-off-by: Andrew Thornton art27@cantab.net

@zeripath zeripath added type/bug pr/breaking Merging this PR means builds will break. Needs a description what exactly breaks, and how to fix it! backport/v1.15 labels Dec 15, 2021
@zeripath zeripath added this to the 1.16.0 milestone Dec 15, 2021
@zeripath
Copy link
Contributor Author

Reviewers please check this PR with your own specific evil test cases.

We will need to double check the redirects are correct too.

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Dec 15, 2021
@zeripath

This comment has been minimized.

@zeripath zeripath changed the title Remove double decoding of url params WIP: Remove double decoding of url params Dec 15, 2021
@zeripath zeripath marked this pull request as draft December 15, 2021 19:43
@zeripath
Copy link
Contributor Author

Ah ok the problem is much more complicated...

The issue is that chi has:

// routeHTTP routes a http.Request through the Mux routing tree to serve
// the matching handler for a particular http method.
func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) {
	// Grab the route context object
	rctx := r.Context().Value(RouteCtxKey).(*Context)

	// The request routing path
	routePath := rctx.RoutePath
	if routePath == "" {
		if r.URL.RawPath != "" {
			routePath = r.URL.RawPath
		} else {
			routePath = r.URL.Path
		}
		if routePath == "" {
			routePath = "/"
		}
	}
...

The routePath should really use the r.URL.EscapedPath() not r.URL.RawPath or even r.URL.Path

There was an unfortunate regression in go-gitea#14293 which has led to the double decoding
of url parameter elements if they contain a '%'. This is due to an issue
with the way chi decodes its RoutePath. In detail the problem lies in
mux.go where the routeHTTP path uses the URL.RawPath or even the
URL.Path instead of the escaped path to do routing.

This PR simply forcibly sets the routePath to that of the EscapedPath.

Fix go-gitea#17938

Signed-off-by: Andrew Thornton <art27@cantab.net>
@zeripath zeripath force-pushed the fix-17938-fix-double-decoding branch from 600e9c3 to 4298560 Compare December 15, 2021 20:33
@zeripath zeripath changed the title WIP: Remove double decoding of url params Prevent double decoding of % in url params Dec 15, 2021
@zeripath zeripath marked this pull request as ready for review December 15, 2021 20:35
@KN4CK3R
Copy link
Member

KN4CK3R commented Dec 15, 2021

Are you sure we are not using chi in the wrong way? It looks like a workaround now.

@zeripath
Copy link
Contributor Author

zeripath commented Dec 15, 2021

If you don't use the escapedpath you use partially decoded URLs for routing and get partially decoded results assigned to ctx.Params. (partially decoded in that you can't safely decode or just use them as is because some things have been decoded and somethings haven't. The result is that %25 will often be predecoded to % but other things won't be - and because you don't have the original URL (except as the full RequestURI) you don't really know how to interpret a % encoded string.

The only consistent (and safe) thing to do is to use the encoded URL for routing.

Feel free to play around with it though.

Here are few (unencoded) filenames you might want to try to make it possible to route to:

10%.md
£15&$6.txt
%3Fis?and#afile
%253Fisnotaquestion%253F
This+file%20has 1space
%%2525mightnotplaywell

@wxiaoguang
Copy link
Contributor

wxiaoguang commented Dec 16, 2021

Nice catch.

Should we add some documents and unit tests to describe the expected behavior?

And I am not sure how most reverse proxies process these encoded URLs (what will be passed to backend)

  • /%.html (it should be /%.html, although it is incorrect)
  • /%25.html (questionable, /%.html or /%25.html for backend ? )
  • /%2525.html ...

@lunny
Copy link
Member

lunny commented Dec 16, 2021

Isn't this an upstream problem?

Signed-off-by: Andrew Thornton <art27@cantab.net>
@zeripath
Copy link
Contributor Author

Not sure if it's definitely an upstream problem - this may be by design.

Partial decoding of the urls may or may not be nice depending on your application - in our case it's bad because we're rendering urls that we have little to no control over so we have to have a simple mapping. Other users may not mind that if you want to have a % in your url it needs to be double escaped.

The solution proposed here is to use the mechanisms already available to us as provided by chi to tell it to use the EscapedPath instead of the partially escaped one.

@zeripath
Copy link
Contributor Author

zeripath commented Dec 16, 2021

Nice catch.

Should we add some documents and unit tests to describe the expected behavior?

I've added unit tests. I'm not sure what kind of document would be beneficial - this PR is simply making sure that ctx.Params() always gives you the non-url encoded parameter from the url.

And I am not sure how most reverse proxies process these encoded URLs (what will be passed to backend)

That's a configuration issue. The reverse proxy should be passing the escaped url correctly - if it's de-escaping things then that's incorrect configuration.

  • /%.html (it should be /%.html, although it is incorrect)

It's simple: a file that is named /%.html should be exposed as an url at /%25.html and if you send GET /owner/repo/src/branch/master/%25.html you should get that file. If you send a request GET /owner/repo/src/branch/master/%.html then go's url handler will clean that up to .../%25.html and you'll get the same file.

  • /%25.html (questionable, /%.html or /%25.html for backend ? )

a file that is named /%25.html should be exposed as an url at .../%2525.html and if you send GET /owner/repo/src/branch/master/%2525.html you should get that file. If you send a request GET /owner/repo/src/branch/master/%25.html then you should get %.html not %25.html.

  • /%2525.html ...

a file that is named /%2525.html should be exposed as an url at /%252525.html and if you send GET /owner/repo/src/branch/master/%252525.html you should get that file. If you send a request GET /owner/repo/src/branch/master/%2525.html then you should get %25.html not %2525.html and certainly not %.html.

@wxiaoguang
Copy link
Contributor

wxiaoguang commented Dec 16, 2021

@zeripath I didn't say clearly. Let's see an example:

For a simple Go http server:

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
		_, _ = fmt.Fprintf(w, "URI: %s\n", r.RequestURI)
		_, _ = fmt.Fprintf(w, "URL: %s\n", r.URL.Path)
	})
	_ = http.ListenAndServe(":3000", nil)
}

nginx config:

server {
    listen 127.0.0.1:80;
    listen 127.0.0.1:443 ssl http2;
    location / {
        proxy_pass http://127.0.0.1:3000/;
    }
}

We see different behaviors:

image

Reference: https://serverfault.com/questions/459369/disabling-url-decoding-in-nginx-proxy

That's what I mean about document. Current Gitea document for nginx (with sub-path) will cause such double-decoding.

Users should tune their reverse proxy carefully.

Copy link
Member

@techknowlogick techknowlogick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ the tests

@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Dec 16, 2021
@zeripath
Copy link
Contributor Author

zeripath commented Dec 16, 2021

@wxiaoguang this partial decoding of URI paths containing %2f is not an actual problem. It would only be a problem if filenames were allowed to contain / in them (which they're not - even on Windows) - and we don't use the URI but the escaped URL which is the same in both of these cases. (IIRC URLs aren't actually allowed %2f in path components.)

It would be a problem if %252f in an URI became %2f in an URL because then that would map to looking at a file with '/' in its name and prevent looking at files with %2f in their names.

@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Dec 16, 2021
@zeripath zeripath merged commit 6e7d28c into go-gitea:main Dec 16, 2021
@zeripath zeripath deleted the fix-17938-fix-double-decoding branch December 16, 2021 17:40
zeripath added a commit to zeripath/gitea that referenced this pull request Dec 16, 2021
There was an unfortunate regression in go-gitea#14293 which has led to the double decoding
of url parameter elements if they contain a '%'. This is due to an issue
with the way chi decodes its RoutePath. In detail the problem lies in
mux.go where the routeHTTP path uses the URL.RawPath or even the
URL.Path instead of the escaped path to do routing.

This PR simply forcibly sets the routePath to that of the EscapedPath.

Fix go-gitea#17938

Signed-off-by: Andrew Thornton <art27@cantab.net>
@zeripath zeripath added the backport/done All backports for this PR have been created label Dec 16, 2021
zeripath added a commit to zeripath/gitea that referenced this pull request Dec 19, 2021
 ## [1.15.8](https://github.com/go-gitea/gitea/releases/tag/v1.15.8) - 2021-12-19

* BUGFIXES
  * Reset locale on login (go-gitea#18023) (go-gitea#18025)
  * Fix reset password email template (go-gitea#17025) (go-gitea#18022)
  * Fix outType on gitea dump (go-gitea#18000) (go-gitea#18016)
  * Ensure complexity, minlength and isPwned are checked on password setting (go-gitea#18005) (go-gitea#18015)
  * Fix rename notification bug (go-gitea#18011)
  * Prevent double decoding of % in url params  (go-gitea#17997) (go-gitea#18001)
  * Prevent hang in git cat-file if the repository is not a valid repository (Partial go-gitea#17991) (go-gitea#17992)
  * Prevent deadlock in create issue (go-gitea#17970) (go-gitea#17982)
* TESTING
  * Use non-expiring key. (go-gitea#17984) (go-gitea#17985)

Signed-off-by: Andrew Thornton <art27@cantab.net>
@zeripath zeripath mentioned this pull request Dec 19, 2021
lafriks pushed a commit that referenced this pull request Dec 20, 2021
## [1.15.8](https://github.com/go-gitea/gitea/releases/tag/v1.15.8) - 2021-12-19

* BUGFIXES
  * Reset locale on login (#18023) (#18025)
  * Fix reset password email template (#17025) (#18022)
  * Fix outType on gitea dump (#18000) (#18016)
  * Ensure complexity, minlength and isPwned are checked on password setting (#18005) (#18015)
  * Fix rename notification bug (#18011)
  * Prevent double decoding of % in url params  (#17997) (#18001)
  * Prevent hang in git cat-file if the repository is not a valid repository (Partial #17991) (#17992)
  * Prevent deadlock in create issue (#17970) (#17982)
* TESTING
  * Use non-expiring key. (#17984) (#17985)

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Update CHANGELOG.md

Co-authored-by: 6543 <6543@obermui.de>
zeripath added a commit to zeripath/gitea that referenced this pull request Dec 21, 2021
PR go-gitea#17997 means that urls with terminal '/' are no longer immediately mapped
to the url without a terminal slash. However, it has revealed that the NotFound handler
appears to have been lost.

This PR adds back in a NotFound handler that simply redirects to a path without the
terminal slash or runs the NotFound handler.

Fix go-gitea#18060

Signed-off-by: Andrew Thornton <art27@cantab.net>
@zeripath zeripath mentioned this pull request Dec 21, 2021
@zeripath zeripath added the topic/security Something leaks user information or is otherwise vulnerable. Should be fixed! label Dec 22, 2021
zeripath added a commit that referenced this pull request Dec 22, 2021
PR #17997 means that urls with terminal '/' are no longer immediately mapped
to the url without a terminal slash. However, it has revealed that the NotFound handler
appears to have been lost.

This PR adds back in a NotFound handler that simply redirects to a path without the
terminal slash or runs the NotFound handler.

Fix #18060

Signed-off-by: Andrew Thornton <art27@cantab.net>
zeripath added a commit to zeripath/gitea that referenced this pull request Dec 22, 2021
Backport go-gitea#18062

PR go-gitea#17997 means that urls with terminal '/' are no longer immediately mapped
to the url without a terminal slash. However, it has revealed that the NotFound handler
appears to have been lost.

This PR adds back in a NotFound handler that simply redirects to a path without the
terminal slash or runs the NotFound handler.

Fix go-gitea#18060

Signed-off-by: Andrew Thornton <art27@cantab.net>
zeripath added a commit that referenced this pull request Dec 22, 2021
Backport #18062

PR #17997 means that urls with terminal '/' are no longer immediately mapped
to the url without a terminal slash. However, it has revealed that the NotFound handler
appears to have been lost.

This PR adds back in a NotFound handler that simply redirects to a path without the
terminal slash or runs the NotFound handler.

Fix #18060

Signed-off-by: Andrew Thornton <art27@cantab.net>
zeripath added a commit to zeripath/gitea that referenced this pull request Dec 23, 2021
A consequence of forcibly setting the RoutePath to the escaped url is that the
auto routing to endpoints without terminal slashes fails (Causing go-gitea#18060.) This
failure raises the possibility that forcibly setting the RoutePath causes other
unexpected behaviours too.

Therefore, instead we should simply pre-escape the URL in the process registering
handler. Then the request URL will be properly escaped for all the following calls.

Fix go-gitea#17938
Fix go-gitea#18060
Replace go-gitea#18062
Replace go-gitea#17997

Signed-off-by: Andrew Thornton <art27@cantab.net>
wxiaoguang pushed a commit that referenced this pull request Dec 24, 2021
A consequence of forcibly setting the RoutePath to the escaped url is that the
auto routing to endpoints without terminal slashes fails (Causing #18060.) This
failure raises the possibility that forcibly setting the RoutePath causes other
unexpected behaviors too.

Therefore, instead we should simply pre-escape the URL in the process registering
handler. Then the request URL will be properly escaped for all the following calls.

Fix #17938
Fix #18060
Replace #18062
Replace #17997

Signed-off-by: Andrew Thornton <art27@cantab.net>
zeripath added a commit to zeripath/gitea that referenced this pull request Dec 25, 2021
…ea#18086)

Backport go-gitea#18086

A consequence of forcibly setting the RoutePath to the escaped url is that the
auto routing to endpoints without terminal slashes fails (Causing go-gitea#18060.) This
failure raises the possibility that forcibly setting the RoutePath causes other
unexpected behaviors too.

Therefore, instead we should simply pre-escape the URL in the process registering
handler. Then the request URL will be properly escaped for all the following calls.

Fix go-gitea#17938
Fix go-gitea#18060
Replace go-gitea#18062
Replace go-gitea#17997

Signed-off-by: Andrew Thornton <art27@cantab.net>
zeripath added a commit that referenced this pull request Dec 26, 2021
#18098)

Backport #18086

A consequence of forcibly setting the RoutePath to the escaped url is that the
auto routing to endpoints without terminal slashes fails (Causing #18060.) This
failure raises the possibility that forcibly setting the RoutePath causes other
unexpected behaviors too.

Therefore, instead we should simply pre-escape the URL in the process registering
handler. Then the request URL will be properly escaped for all the following calls.

Fix #17938
Fix #18060
Replace #18062
Replace #17997

Signed-off-by: Andrew Thornton <art27@cantab.net>
Chianina pushed a commit to Chianina/gitea that referenced this pull request Mar 28, 2022
There was an unfortunate regression in go-gitea#14293 which has led to the double decoding
of url parameter elements if they contain a '%'. This is due to an issue
with the way chi decodes its RoutePath. In detail the problem lies in
mux.go where the routeHTTP path uses the URL.RawPath or even the
URL.Path instead of the escaped path to do routing.

This PR simply forcibly sets the routePath to that of the EscapedPath.

Fix go-gitea#17938

Signed-off-by: Andrew Thornton <art27@cantab.net>
Chianina pushed a commit to Chianina/gitea that referenced this pull request Mar 28, 2022
PR go-gitea#17997 means that urls with terminal '/' are no longer immediately mapped
to the url without a terminal slash. However, it has revealed that the NotFound handler
appears to have been lost.

This PR adds back in a NotFound handler that simply redirects to a path without the
terminal slash or runs the NotFound handler.

Fix go-gitea#18060

Signed-off-by: Andrew Thornton <art27@cantab.net>
Chianina pushed a commit to Chianina/gitea that referenced this pull request Mar 28, 2022
…ea#18086)

A consequence of forcibly setting the RoutePath to the escaped url is that the
auto routing to endpoints without terminal slashes fails (Causing go-gitea#18060.) This
failure raises the possibility that forcibly setting the RoutePath causes other
unexpected behaviors too.

Therefore, instead we should simply pre-escape the URL in the process registering
handler. Then the request URL will be properly escaped for all the following calls.

Fix go-gitea#17938
Fix go-gitea#18060
Replace go-gitea#18062
Replace go-gitea#17997

Signed-off-by: Andrew Thornton <art27@cantab.net>
@go-gitea go-gitea locked and limited conversation to collaborators Apr 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
backport/done All backports for this PR have been created lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. pr/breaking Merging this PR means builds will break. Needs a description what exactly breaks, and how to fix it! topic/security Something leaks user information or is otherwise vulnerable. Should be fixed! type/bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

WebGUI: Escape character in folder or file name prevents access
6 participants