Skip to content

Conversation

@max-schaefer
Copy link
Contributor

No description provided.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 15, 2024

QHelp previews:

go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.qhelp

Incomplete regular expression for hostnames

Sanitizing untrusted URLs is an important technique for preventing attacks such as request forgeries and malicious redirections. Often, this is done by checking that the host of a URL is in a set of allowed hosts.

If a regular expression implements such a check, it is easy to accidentally make the check too permissive by not escaping regular-expression meta-characters such as ..

Even if the check is not used in a security-critical context, the incomplete check may still cause undesirable behavior when it accidentally succeeds.

Recommendation

Escape all meta-characters appropriately when constructing regular expressions for security checks, paying special attention to the . meta-character.

Example

The following example code checks that a URL redirection will reach the example.com domain, or one of its subdomains.

package main import ( "errors" "net/http" "regexp" ) func checkRedirect(req *http.Request, via []*http.Request) error { // BAD: the host of `req.URL` may be controlled by an attacker re := "^((www|beta).)?example.com/" if matched, _ := regexp.MatchString(re, req.URL.Host); matched { return nil	} return errors.New("Invalid redirect") }

The check is however easy to bypass because the unescaped . allows for any character before example.com, effectively allowing the redirect to go to an attacker-controlled domain such as wwwXexample.com.

Address this vulnerability by escaping . appropriately:

package main import ( "errors" "net/http" "regexp" ) func checkRedirectGood(req *http.Request, via []*http.Request) error { // GOOD: the host of `req.URL` must be `example.com`, `www.example.com` or `beta.example.com` re := "^((www|beta)\\.)?example\\.com/" if matched, _ := regexp.MatchString(re, req.URL.Host); matched { return nil	} return errors.New("Invalid redirect") }

You may also want to consider using raw string literals to avoid having to escape backslashes:

package main import ( "errors" "net/http" "regexp" ) func checkRedirectGood(req *http.Request, via []*http.Request) error { // GOOD: the host of `req.URL` must be `example.com`, `www.example.com` or `beta.example.com` re := `^((www|beta)\.)?example\.com/` if matched, _ := regexp.MatchString(re, req.URL.Host); matched { return nil	} return errors.New("Invalid redirect") }

References

@max-schaefer max-schaefer force-pushed the max-schaefer/go-incomplete-hostname-regex branch from 58ebd0c to d3e0a90 Compare March 15, 2024 11:22
@max-schaefer max-schaefer added no-change-note-required This PR does not need a change note ready-for-doc-review This PR requires and is ready for review from the GitHub docs team. labels Mar 15, 2024
@max-schaefer max-schaefer merged commit daee22d into main Mar 15, 2024
@max-schaefer max-schaefer deleted the max-schaefer/go-incomplete-hostname-regex branch March 15, 2024 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Go no-change-note-required This PR does not need a change note ready-for-doc-review This PR requires and is ready for review from the GitHub docs team.

4 participants