diff --git a/internal/web/actions/default/settings/security/index.go b/internal/web/actions/default/settings/security/index.go index f9071791..0632364b 100644 --- a/internal/web/actions/default/settings/security/index.go +++ b/internal/web/actions/default/settings/security/index.go @@ -29,14 +29,14 @@ func (this *IndexAction) RunGet(params struct{}) { } // 国家和地区 - countryMaps := []maps.Map{} + var countryMaps = []maps.Map{} for _, countryId := range config.AllowCountryIds { countryResp, err := this.RPC().RegionCountryRPC().FindEnabledRegionCountry(this.AdminContext(), &pb.FindEnabledRegionCountryRequest{RegionCountryId: countryId}) if err != nil { this.ErrorPage(err) return } - country := countryResp.RegionCountry + var country = countryResp.RegionCountry if country != nil { countryMaps = append(countryMaps, maps.Map{ "id": country.Id, @@ -47,14 +47,14 @@ func (this *IndexAction) RunGet(params struct{}) { this.Data["countries"] = countryMaps // 省份 - provinceMaps := []maps.Map{} + var provinceMaps = []maps.Map{} for _, provinceId := range config.AllowProvinceIds { provinceResp, err := this.RPC().RegionProvinceRPC().FindEnabledRegionProvince(this.AdminContext(), &pb.FindEnabledRegionProvinceRequest{RegionProvinceId: provinceId}) if err != nil { this.ErrorPage(err) return } - province := provinceResp.RegionProvince + var province = provinceResp.RegionProvince if province != nil { provinceMaps = append(provinceMaps, maps.Map{ "id": province.Id, @@ -76,6 +76,11 @@ func (this *IndexAction) RunPost(params struct { AllowIPs []string AllowRememberLogin bool + DenySearchEngines bool + DenySpiders bool + + DomainsJSON []byte + Must *actions.Must CSRF *actionutils.CSRF }) { @@ -91,7 +96,7 @@ func (this *IndexAction) RunPost(params struct { config.Frame = params.Frame // 国家和地区 - countryIds := []int64{} + var countryIds = []int64{} if len(params.CountryIdsJSON) > 0 { err = json.Unmarshal(params.CountryIdsJSON, &countryIds) if err != nil { @@ -102,7 +107,7 @@ func (this *IndexAction) RunPost(params struct { config.AllowCountryIds = countryIds // 省份 - provinceIds := []int64{} + var provinceIds = []int64{} if len(params.ProvinceIdsJSON) > 0 { err = json.Unmarshal(params.ProvinceIdsJSON, &provinceIds) if err != nil { @@ -128,6 +133,20 @@ func (this *IndexAction) RunPost(params struct { // 允许本地 config.AllowLocal = params.AllowLocal + // 禁止搜索引擎和爬虫 + config.DenySearchEngines = params.DenySearchEngines + config.DenySpiders = params.DenySpiders + + // 允许的域名 + var domains = []string{} + if len(params.DomainsJSON) > 0 { + err = json.Unmarshal(params.DomainsJSON, &domains) + if err != nil { + this.Fail("解析允许访问的域名失败:" + err.Error()) + } + } + config.AllowDomains = domains + // 允许记住登录 config.AllowRememberLogin = params.AllowRememberLogin diff --git a/internal/web/helpers/user_must_auth.go b/internal/web/helpers/user_must_auth.go index 8e43f5b1..88d51ec4 100644 --- a/internal/web/helpers/user_must_auth.go +++ b/internal/web/helpers/user_must_auth.go @@ -75,6 +75,13 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam return false } + // 检查请求 + if !checkRequestSecurity(securityConfig, action.Request) { + action.ResponseWriter.WriteHeader(http.StatusForbidden) + return false + } + + // 检查系统是否已经配置过 if !setup.IsConfigured() { action.RedirectURL("/setup") diff --git a/internal/web/helpers/user_should_auth.go b/internal/web/helpers/user_should_auth.go index 56bfdad6..5a3ec5af 100644 --- a/internal/web/helpers/user_should_auth.go +++ b/internal/web/helpers/user_should_auth.go @@ -22,7 +22,7 @@ func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramN this.action = actionPtr.Object() // 安全相关 - action := this.action + var action = this.action securityConfig, _ := configloaders.LoadSecurityConfig() if securityConfig == nil { action.AddHeader("X-Frame-Options", "SAMEORIGIN") @@ -42,6 +42,12 @@ func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramN return false } + // 检查请求 + if !checkRequestSecurity(securityConfig, action.Request) { + action.ResponseWriter.WriteHeader(http.StatusForbidden) + return false + } + return true } diff --git a/internal/web/helpers/utils.go b/internal/web/helpers/utils.go index bc41183b..8dd3d0e5 100644 --- a/internal/web/helpers/utils.go +++ b/internal/web/helpers/utils.go @@ -9,6 +9,8 @@ import ( "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/logs" "net" + "net/http" + "regexp" "sync" ) @@ -106,3 +108,41 @@ func checkIPWithoutCache(config *systemconfigs.SecurityConfig, ipAddr string) bo return true } + +// 请求检查相关正则 +var searchEngineRegex = regexp.MustCompile(`60spider|adldxbot|adsbot-google|applebot|admantx|alexa|baidu|bingbot|bingpreview|facebookexternalhit|googlebot|proximic|slurp|sogou|twitterbot|yandex`) +var spiderRegexp = regexp.MustCompile(`python|pycurl|http-client|httpclient|apachebench|nethttp|http_request|java|perl|ruby|scrapy|php|rust|curl|wget`) // 其中增加了curl和wget + +// 检查请求 +func checkRequestSecurity(securityConfig *systemconfigs.SecurityConfig, req *http.Request) bool { + if securityConfig == nil { + return true + } + + var userAgent = req.UserAgent() + var referer = req.Referer() + + // 检查搜索引擎 + if securityConfig.DenySearchEngines && (len(userAgent) == 0 || searchEngineRegex.MatchString(userAgent) || (len(referer) > 0 && searchEngineRegex.MatchString(referer))) { + return false + } + + // 检查爬虫 + if securityConfig.DenySpiders && (len(userAgent) == 0 || spiderRegexp.MatchString(userAgent) || (len(referer) > 0 && spiderRegexp.MatchString(referer))) { + return false + } + + // 检查允许访问的域名 + if len(securityConfig.AllowDomains) > 0 { + var domain = req.Host + realDomain, _, err := net.SplitHostPort(domain) + if err == nil && len(realDomain) > 0 { + domain = realDomain + } + if !lists.ContainsString(securityConfig.AllowDomains, domain) { + return false + } + } + + return true +} diff --git a/web/views/@default/settings/security/index.html b/web/views/@default/settings/security/index.html index ecf4f597..1ab25300 100644 --- a/web/views/@default/settings/security/index.html +++ b/web/views/@default/settings/security/index.html @@ -40,7 +40,7 @@ 允许局域网访问 -

选中表示允许在本机和局域网访问。

+

选中表示总是允许在本机和局域网访问,不需要其他限制条件。

@@ -50,6 +50,34 @@

选中表示允许在登录界面可以选择记住登录。

+ + + + + + + + 禁止搜索引擎 + + +

禁止常见的搜索引擎访问。

+ + + + 禁止爬虫 + + +

禁止常见的爬虫访问。

+ + + + 允许访问的域名 + + +

只允许通过这些域名(或者IP作为主机地址)访问当前管理系统,不填表示没有限制。

+ + + \ No newline at end of file