CodeQL 文档

缺少正则表达式定位符

ID: go/regex/missing-regexp-anchor
Kind: problem
Security severity: 7.8
Severity: warning
Precision: high
Tags:
   - correctness
   - security
   - external/cwe/cwe-20
Query suites:
   - go-code-scanning.qls
   - go-security-extended.qls
   - go-security-and-quality.qls

点击查看 CodeQL 存储库中的查询

使用正则表达式清理不受信任的输入是一种常见技术。但是,在没有定位符(如 ^$)的情况下,将不受信任的输入与正则表达式进行匹配很容易出错。恶意输入可以通过在意外位置嵌入允许的模式之一来绕过此类安全检查。

即使匹配不是在安全关键型上下文中完成的,当正则表达式意外匹配时,它仍然可能导致不希望的行为。

建议

使用定位符确保正则表达式在预期位置匹配。

示例

以下示例代码检查 URL 重定向是否会到达 example.com 域或其子域之一,而不是某个恶意站点。

package main

import (
	"errors"
	"net/http"
	"regexp"
)

func checkRedirect2(req *http.Request, via []*http.Request) error {
	// BAD: the host of `req.URL` may be controlled by an attacker
	re := "https?://www\\.example\\.com/"
	if matched, _ := regexp.MatchString(re, req.URL.String()); matched {
		return nil
	}
	return errors.New("Invalid redirect")
}

但是,使用正则表达式匹配的检查很容易绕过。例如,字符串 http://example.com/ 可以嵌入到查询字符串组件中:http://evil-example.net/?x=http://example.com/

通过在正则表达式中使用定位符来解决这些缺陷

package main

import (
	"errors"
	"net/http"
	"regexp"
)

func checkRedirect2Good(req *http.Request, via []*http.Request) error {
	// GOOD: the host of `req.URL` cannot be controlled by an attacker
	re := "^https?://www\\.example\\.com/"
	if matched, _ := regexp.MatchString(re, req.URL.String()); matched {
		return nil
	}
	return errors.New("Invalid redirect")
}

一个相关的错误是编写具有多个备选项的正则表达式,但只定位其中一个备选项。例如,正则表达式 ^www\.example\.com|beta\.example\.com 将匹配主机 evil.beta.example.com,因为该正则表达式被解析为 (^www\.example\.com)|(beta\.example\.com)/,因此第二个备选项 beta\.example\.com 没有定位在字符串的开头。

在检查可能具有子域的域名时,定位正则表达式或确保域名以点为前缀非常重要。

package main

import (
	"regexp"
)

func checkSubdomain(domain String) {
	// Checking strictly that the domain is `example.com`.
	re := "^example\\.com$"
	if matched, _ := regexp.MatchString(re, domain); matched {
		// domain is good.
	}

	// GOOD: Alternatively, check the domain is `example.com` or a subdomain of `example.com`.
	re2 := "(^|\\.)example\\.com$"

	if matched, _ := regexp.MatchString(re2, domain); matched {
		// domain is good.
	}
}

参考

  • ©GitHub, Inc.
  • 条款
  • 隐私