Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions internal/conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,18 @@ func Init() error {
return errors.Wrap(err, "map 'mail'")
}

serviceSections := File.Section("service").ChildSections()
for _, serviceSection := range serviceSections {
serviceSection := serviceSection
var backend struct {
Prefix string `ini:"prefix"`
ForwardURL string `ini:"forward_url"`
}
if err := serviceSection.MapTo(&backend); err != nil {
return errors.Wrapf(err, "map 'service.%s'", serviceSection.Name())
}
Service.Backends = append(Service.Backends, backend)
}

return nil
}
7 changes: 7 additions & 0 deletions internal/conf/static.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,11 @@ var (
Port int `ini:"port"`
SMTP string `ini:"smtp"`
}

Service struct {
Backends []struct {
Prefix string `ini:"prefix"`
ForwardURL string `ini:"forward_url"`
}
}
)
2 changes: 2 additions & 0 deletions internal/route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/wuhan005/NekoBox/route/auth"
"github.com/wuhan005/NekoBox/route/pixel"
"github.com/wuhan005/NekoBox/route/question"
"github.com/wuhan005/NekoBox/route/service"
"github.com/wuhan005/NekoBox/route/user"
"github.com/wuhan005/NekoBox/static"
"github.com/wuhan005/NekoBox/templates"
Expand Down Expand Up @@ -141,6 +142,7 @@ func New() *flamego.Flame {
})

f.Any("/pixel/{**}", reqUserSignIn, pixel.Proxy)
f.Any("/service/{**}", service.Proxy)
}, context.APIEndpoint)
},
cache.Cacher(cache.Options{
Expand Down
84 changes: 84 additions & 0 deletions route/service/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package service

import (
"crypto/tls"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"

"github.com/sirupsen/logrus"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"

"github.com/wuhan005/NekoBox/internal/conf"
"github.com/wuhan005/NekoBox/internal/context"
)

func Proxy(ctx context.Context) error {
span := trace.SpanFromContext(ctx.Request().Context())

var userID uint
if ctx.IsLogged {
userID = ctx.User.ID
}

if span.SpanContext().IsValid() {
span.SetAttributes(
attribute.Int("nekobox.service.user-id", int(userID)),
)
}

uri := ctx.Param("**")
basePath := strings.Split(uri, "/")[0]
forwardPath := strings.TrimPrefix(uri, basePath)

var forwardURLStr string
for _, backend := range conf.Service.Backends {
if backend.Prefix == basePath {
forwardURLStr = backend.ForwardURL
break
}
}
if len(forwardURLStr) == 0 {
return ctx.JSONError(http.StatusNotFound, "页面不存在")
}

forwardURL, err := url.Parse(forwardURLStr)
if err != nil {
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to parse forward URL")
return ctx.JSONError(http.StatusInternalServerError, "服务网关内部错误")
}

reverseProxy := httputil.ReverseProxy{
Director: func(req *http.Request) {
req.URL = forwardURL
req.URL.Path = strings.TrimRight(req.URL.Path, "/") + forwardPath
req.Host = forwardURL.Host

traceHeaders := http.Header{}
otel.GetTextMapPropagator().Inject(ctx.Request().Context(), propagation.HeaderCarrier(traceHeaders))
for key := range traceHeaders {
req.Header.Set(key, traceHeaders.Get(key))
}

req.Header.Set("X-NekoBox-From", "nekobox-gateway")
req.Header.Set("X-NekoBox-User-ID", strconv.Itoa(int(userID)))
},
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
Comment on lines +71 to +75
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CRITICAL: TLS certificate verification is disabled.

The InsecureSkipVerify: true setting completely disables TLS certificate verification, making the proxy vulnerable to man-in-the-middle attacks. This should never be used in production code.

Additionally, the TLS configuration is missing MinVersion, which should be set to at least tls.VersionTLS12 (or preferably tls.VersionTLS13).

🔎 Required security fix

Remove InsecureSkipVerify and add proper TLS configuration:

 Transport: &http.Transport{
     TLSClientConfig: &tls.Config{
-        InsecureSkipVerify: true,
+        MinVersion: tls.VersionTLS12,
     },
 },

If you need to support self-signed certificates in development environments, use a proper certificate pool or make this configurable per-backend with clear warnings, but never use InsecureSkipVerify in production.

Based on static analysis hints: CWE-327, OWASP A02:2021 - Cryptographic Failures.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
},
🧰 Tools
🪛 ast-grep (0.40.3)

[warning] 71-73: MinVersionis missing from this TLS configuration. By default, TLS 1.2 is currently used as the minimum when acting as a client, and TLS 1.0 when acting as a server. General purpose web applications should default to TLS 1.3 with all other protocols disabled. Only where it is known that a web server must support legacy clients with unsupported an insecure browsers (such as Internet Explorer 10), it may be necessary to enable TLS 1.0 to provide support. AddMinVersion: tls.VersionTLS13' to the TLS configuration to bump the minimum version to TLS 1.3.
Context: tls.Config{
InsecureSkipVerify: true,
}
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
https://owasp.org/Top10/A02_2021-Cryptographic_Failures

(missing-ssl-minversion-go)

🪛 GitHub Check: CodeQL

[failure] 73-73: Disabled TLS certificate check
InsecureSkipVerify should not be used in production code.

🤖 Prompt for AI Agents
In route/service/proxy.go around lines 71 to 75, the TLS config currently sets
InsecureSkipVerify: true which disables certificate validation; remove that
setting, set TLSConfig.MinVersion to at least tls.VersionTLS12 (prefer
tls.VersionTLS13), and populate RootCAs with a proper x509.CertPool if you need
to trust self-signed certs for dev/testing or make an explicit per-backend
“allow insecure” flag (off by default) that uses a separate, clearly documented
config path for non-production use; do not leave InsecureSkipVerify enabled in
production.

ErrorHandler: func(writer http.ResponseWriter, request *http.Request, err error) {
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to handle reverse proxy request")
_ = ctx.JSONError(http.StatusInternalServerError, "服务网关内部错误")
},
}

reverseProxy.ServeHTTP(ctx.ResponseWriter(), ctx.Request().Request)
return nil
}
Loading