开源前加固认证与部署契约:加密随机 JWT、改密事务吊销、登录 CSRF,并禁止生产回落 localhost。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-23 02:07:34 +08:00
parent 6f054a903c
commit 3b550f2124
24 changed files with 372 additions and 355 deletions

View File

@@ -67,6 +67,12 @@ func (s *SettingService) ImportTimelineFromGit(urls []string, followPages bool)
client := &http.Client{
Timeout: timelineGitHTTPTimeout,
Transport: &http.Transport{
// 解析后按公网 IP 拨号,避免 DNS rebinding(与 SMTP/S3 safeDial 同思路)
DialContext: publicOnlyDial,
TLSHandshakeTimeout: timelineGitHTTPTimeout,
ForceAttemptHTTP2: true,
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) >= 3 {
return errors.New("重定向过多")
@@ -656,6 +662,35 @@ func assertSafeHTTPSURL(u *url.URL) error {
return nil
}
// publicOnlyDial 先解析主机、拒绝私网,再按 IP 拨号,关闭 DNS rebinding 窗口。
func publicOnlyDial(ctx context.Context, network, address string) (net.Conn, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, errors.New("连接地址无效")
}
ips, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil || len(ips) == 0 {
return nil, errors.New("地址解析失败")
}
var last error
dialer := &net.Dialer{Timeout: timelineGitHTTPTimeout}
for _, a := range ips {
if !isPublicIP(a.IP) {
last = errors.New("禁止访问内网地址")
continue
}
c, e := dialer.DialContext(ctx, network, net.JoinHostPort(a.IP.String(), port))
if e == nil {
return c, nil
}
last = e
}
if last == nil {
last = errors.New("禁止访问内网地址")
}
return nil, last
}
func isPublicIP(ip net.IP) bool {
if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsMulticast() || ip.IsUnspecified() {
return false
@@ -664,6 +699,10 @@ func isPublicIP(ip net.IP) bool {
if ip4[0] == 169 && ip4[1] == 254 {
return false
}
// CGNAT / 文档网段等
if ip4[0] == 100 && ip4[1] >= 64 && ip4[1] <= 127 {
return false
}
}
return true
}