Added refresh button to challenges and deny pages where reasonable, ensure no open redirect or other
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ .Title }}</title>
|
||||
<link rel="stylesheet" href="{{ .Path }}/embed/assets/static/anubis/style.css?cacheBust={{ .Random }}"/>
|
||||
<link rel="stylesheet" href="{{ .Path }}/assets/static/anubis/style.css?cacheBust={{ .Random }}"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
{{ range $key, $value := .Meta }}
|
||||
{{ if eq $key "refresh"}}
|
||||
@@ -151,7 +151,7 @@
|
||||
<img
|
||||
id="image"
|
||||
style="width:100%;max-width:256px;"
|
||||
src="{{ .Path }}/embed/assets/static/logo.png?cacheBust={{ .Random }}"
|
||||
src="{{ .Path }}/assets/static/logo.png?cacheBust={{ .Random }}"
|
||||
/>
|
||||
{{if .Challenge }}
|
||||
<p id="status">Loading challenge <em>{{ .Challenge }}</em>...</p>
|
||||
@@ -173,18 +173,24 @@
|
||||
<div></div>
|
||||
</div>
|
||||
{{end}}
|
||||
<details>
|
||||
<details style="padding-bottom: 2em;">
|
||||
<summary>Why am I seeing this?</summary>
|
||||
<p>You are seeing this because the administrator of this website has set up <a href="https://git.gammaspectra.live/git/go-away">go-away</a> to protect the server against the scourge of <a href="https://thelibre.news/foss-infrastructure-is-under-attack-by-ai-companies/">AI companies aggressively scraping websites</a>. This can and does cause downtime for the websites, which makes their resources inaccessible for everyone.</p>
|
||||
<p>Please note that some challenges requires the use of modern JavaScript features and some plugins may will disable. Please disable such plugins for this domain (for example, JShelter).</p>
|
||||
<p>If you have any issues contact the administrator and provide this request id: <em>{{ .Id }}</em></p>
|
||||
<p>If you have any issues contact the administrator and provide this Request Id: <em>{{ .Id }}</em></p>
|
||||
</details>
|
||||
|
||||
<noscript>
|
||||
<p>
|
||||
Sadly, you may need to enable JavaScript to get past this challenge. This is required because AI companies have changed
|
||||
the social contract around how website hosting works.
|
||||
</p>
|
||||
</noscript>
|
||||
|
||||
{{if .Redirect }}
|
||||
<a role="button" href="{{ .Redirect }}">Refresh page</a>
|
||||
{{end}}
|
||||
|
||||
<div id="testarea"></div>
|
||||
</div>
|
||||
|
||||
|
@@ -70,13 +70,26 @@
|
||||
{{end}}
|
||||
<div id="spinner"></div>
|
||||
|
||||
<details>
|
||||
<details style="padding-bottom: 2em;">
|
||||
<summary>Why am I seeing this?</summary>
|
||||
<p>You are seeing this because the administrator of this website has set up <a href="https://git.gammaspectra.live/git/go-away">go-away</a> to protect the server against the scourge of <a href="https://thelibre.news/foss-infrastructure-is-under-attack-by-ai-companies/">AI companies aggressively scraping websites</a>. This can and does cause downtime for the websites, which makes their resources inaccessible for everyone.</p>
|
||||
<p>Please note that some challenges requires the use of modern JavaScript features and some plugins may will disable. Please disable such plugins for this domain (for example, JShelter).</p>
|
||||
<p>If you have any issues contact the administrator and provide the request id: <em>{{ .Id }}</em></p>
|
||||
<p>If you have any issues contact the administrator and provide the Request Id: <em>{{ .Id }}</em></p>
|
||||
</details>
|
||||
|
||||
<noscript>
|
||||
<p>
|
||||
Sadly, you may need to enable JavaScript to get past this challenge. This is required because AI companies have changed
|
||||
the social contract around how website hosting works.
|
||||
</p>
|
||||
</noscript>
|
||||
|
||||
{{if .Redirect }}
|
||||
<div class="button-row">
|
||||
<a role="button" class="ui small primary button" href="{{ .Redirect }}">Refresh page</a>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
<div id="testarea"></div>
|
||||
</div>
|
||||
|
20
lib/http.go
20
lib/http.go
@@ -362,7 +362,13 @@ func (state *State) setupRoutes() error {
|
||||
state.Mux.Handle(fmt.Sprintf("GET %s/verify-challenge", c.Path), c.ServeVerifyChallenge)
|
||||
} else if c.Verify != nil {
|
||||
state.Mux.HandleFunc(fmt.Sprintf("GET %s/verify-challenge", c.Path), func(w http.ResponseWriter, r *http.Request) {
|
||||
err := func() (err error) {
|
||||
redirect, err := utils.EnsureNoOpenRedirect(r.FormValue("redirect"))
|
||||
if redirect == "" {
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusInternalServerError, err, "")
|
||||
return
|
||||
}
|
||||
|
||||
err = func() (err error) {
|
||||
|
||||
data := RequestDataFromContext(r.Context())
|
||||
|
||||
@@ -380,15 +386,15 @@ func (state *State) setupRoutes() error {
|
||||
state.addTiming(w, "challenge-verify", "Verify client challenge", time.Since(start))
|
||||
|
||||
if err != nil {
|
||||
state.logger(r).Error(fmt.Errorf("challenge error: %w", err).Error(), "challenge", c.Name, "redirect", r.FormValue("redirect"))
|
||||
state.logger(r).Error(fmt.Errorf("challenge error: %w", err).Error(), "challenge", c.Name, "redirect", redirect)
|
||||
return err
|
||||
} else if !ok {
|
||||
state.logger(r).Warn("challenge failed", "challenge", c.Name, "redirect", r.FormValue("redirect"))
|
||||
state.logger(r).Warn("challenge failed", "challenge", c.Name, "redirect", redirect)
|
||||
utils.ClearCookie(utils.CookiePrefix+c.Name, w)
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusForbidden, fmt.Errorf("access denied: failed challenge %s", c.Name), r.FormValue("redirect"))
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusForbidden, fmt.Errorf("access denied: failed challenge %s", c.Name), redirect)
|
||||
return nil
|
||||
}
|
||||
state.logger(r).Info("challenge passed", "challenge", c.Name, "redirect", r.FormValue("redirect"))
|
||||
state.logger(r).Info("challenge passed", "challenge", c.Name, "redirect", redirect)
|
||||
|
||||
token, err := c.IssueChallengeToken(state.privateKey, key, []byte(result), data.Expires)
|
||||
if err != nil {
|
||||
@@ -398,12 +404,12 @@ func (state *State) setupRoutes() error {
|
||||
}
|
||||
data.Challenges[c.Id] = challenge.VerifyResultPASS
|
||||
|
||||
http.Redirect(w, r, r.FormValue("redirect"), http.StatusTemporaryRedirect)
|
||||
http.Redirect(w, r, redirect, http.StatusTemporaryRedirect)
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
utils.ClearCookie(utils.CookiePrefix+c.Name, w)
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusInternalServerError, err, r.FormValue("redirect"))
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusInternalServerError, err, redirect)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
19
lib/state.go
19
lib/state.go
@@ -435,7 +435,14 @@ func NewState(p policy.Policy, settings StateSettings) (state *State, err error)
|
||||
|
||||
c.ServeVerifyChallenge = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
err := func() (err error) {
|
||||
redirect, err := utils.EnsureNoOpenRedirect(r.FormValue("redirect"))
|
||||
if err != nil {
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusInternalServerError, err, "")
|
||||
return
|
||||
}
|
||||
|
||||
err = func() (err error) {
|
||||
|
||||
data := RequestDataFromContext(r.Context())
|
||||
|
||||
key := state.GetChallengeKeyForRequest(challengeName, data.Expires, r)
|
||||
@@ -449,13 +456,13 @@ func NewState(p policy.Policy, settings StateSettings) (state *State, err error)
|
||||
if ok, err := c.Verify(key, result, r); err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
state.logger(r).Warn("challenge failed", "challenge", challengeName, "redirect", r.FormValue("redirect"))
|
||||
state.logger(r).Warn("challenge failed", "challenge", challengeName, "redirect", redirect)
|
||||
utils.ClearCookie(utils.CookiePrefix+challengeName, w)
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusForbidden, fmt.Errorf("access denied: failed challenge %s", challengeName), r.FormValue("redirect"))
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusForbidden, fmt.Errorf("access denied: failed challenge %s", challengeName), redirect)
|
||||
return nil
|
||||
}
|
||||
|
||||
state.logger(r).Warn("challenge passed", "challenge", challengeName, "redirect", r.FormValue("redirect"))
|
||||
state.logger(r).Warn("challenge passed", "challenge", challengeName, "redirect", redirect)
|
||||
|
||||
token, err := c.IssueChallengeToken(state.privateKey, key, []byte(result), data.Expires)
|
||||
if err != nil {
|
||||
@@ -467,7 +474,7 @@ func NewState(p policy.Policy, settings StateSettings) (state *State, err error)
|
||||
|
||||
switch httpCode {
|
||||
case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther, http.StatusTemporaryRedirect, http.StatusPermanentRedirect:
|
||||
http.Redirect(w, r, r.FormValue("redirect"), httpCode)
|
||||
http.Redirect(w, r, redirect, httpCode)
|
||||
default:
|
||||
w.Header().Set("Content-Type", mimeType)
|
||||
w.WriteHeader(httpCode)
|
||||
@@ -480,7 +487,7 @@ func NewState(p policy.Policy, settings StateSettings) (state *State, err error)
|
||||
}()
|
||||
if err != nil {
|
||||
utils.ClearCookie(utils.CookiePrefix+challengeName, w)
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusInternalServerError, err, r.FormValue("redirect"))
|
||||
_ = state.errorPage(w, r.Header.Get("X-Away-Id"), http.StatusInternalServerError, err, redirect)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
@@ -2,13 +2,33 @@ package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func EnsureNoOpenRedirect(redirect string) (string, error) {
|
||||
uri, err := url.Parse(redirect)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
uri.Scheme = ""
|
||||
uri.Host = ""
|
||||
uri.User = nil
|
||||
uri.Opaque = ""
|
||||
uri.OmitHost = true
|
||||
|
||||
if uri.Path != "" && !strings.HasPrefix(uri.Path, "/") {
|
||||
return "", errors.New("invalid redirect path")
|
||||
}
|
||||
|
||||
return uri.String(), nil
|
||||
}
|
||||
|
||||
func MakeReverseProxy(target string) (*httputil.ReverseProxy, error) {
|
||||
u, err := url.Parse(target)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user