4 Challenges
WeebDataHoarder edited this page 2025-05-04 13:55:01 +02:00

Challenges

Operators can choose to serve a challenge to incoming requests or client, depending on conditions or other rules.

Challenges can be transparent (not shown to user, depends on backend or other logic), non-JavaScript (challenges common browser properties), or custom JavaScript (from Proof of Work to fingerprinting or Captcha is supported)

The following examples are defined in policy snippets and are ready to use. Challenges can be redefined or new ones entirely can be added with different configuration.

Transparent

http

Verify incoming requests against a specified backend to allow the user through. Cookies and some other headers are passed.

For example, this allows verifying the user cookies against the backend to have the user skip all other challenges.

Most request headers (including cookies / authorization) and other general headers from go-away are sent, some additional ones are set, of note:

Header Description
X-Away-Method Original request HTTP Method
X-Away-Host Original request HTTP Host
X-Away-Path Original request URL Path
X-Away-Query Original request URL Query

Example on Forgejo, checks that current user is authenticated:

challenges:
  http-cookie-check:
    runtime: http
    parameters:
      http-url: http://forgejo:3000/user/stopwatches
      # http-url: http://forgejo:3000/repo/search
      # http-url: http://forgejo:3000/notifications/new
      http-method: GET
      http-cookie: i_like_gitea
      http-code: 200
      verify-probability: 0.1

Requires HTTP/2+ response parsing and logic, silent challenge (does not display a challenge page).

Browsers that support 103 Early Hints are indicated to fetch a CSS resource via Link preload that solves the challenge.

The server waits until solved or defined timeout, then continues on other challenges if failed. Configured default is 2 seconds.

Example:

challenges:
  preload-link:
    condition: '"Sec-Fetch-Mode" in headers && headers["Sec-Fetch-Mode"] == "navigate"'
    runtime: "preload-link"
    parameters:
      preload-early-hint-deadline: 2s

dnsbl

You can configure a DNSBL (Domain Name System blocklist) to be queried.

This allows you to serve harder or different challenges to higher risk clients, or block them from specific sections.

Only rules that match a DNSBL challenge will cause a query to be sent, meaning the bulk of requests will not be sent to this service upstream.

Results will be temporarily cached.

By default, DroneBL is used.

Example challenge definition and rule:

challenges:
  dnsbl:
  runtime: dnsbl
  parameters:
    # dnsbl-host: "dnsbl.dronebl.org"
    dnsbl-decay: 1h
    dnsbl-timeout: 1s
    
rules:
  # check DNSBL and serve harder challenges
  - name: undesired-dnsbl
    action: check
    settings:
      challenges: [dnsbl]
      # if DNSBL fails, check additional challenges
      fail: check
      fail-settings:
        challenges: [js-pow-sha256]

Non-JavaScript

Requires HTTP parsing and a Cookie Jar, silent challenge (does not display a challenge page unless failed).

Serves the client with a Set-Cookie that solves the challenge, and redirects it back to the same page. Browser must present the cookie to load.

Several tools implement this, but usually not mass scrapers.

Example:

challenges:
  cookie:
    runtime: "cookie"

refresh

Requires HTTP response parsing and logic, displays challenge site instantly.

refresh-via: header

Have the browser solve the challenge by following the URL listed on HTTP Refresh instantly.

Example:

challenges:
  header-refresh:
    runtime: "refresh"
    parameters:
      refresh-via: "header"

refresh-via: meta

Have the browser solve the challenge by following the URL listed on HTML <meta http-equiv=refresh> tag instantly. Equivalent to above.

Example:

challenges:
  header-refresh:
    runtime: "refresh"
    parameters:
      refresh-via: "meta"

resource-load

Requires HTTP and HTML response parsing and logic, displays challenge site.

Servers a challenge page with a linked resource that is loaded by the browser, which solves the challenge. Page refreshes a few seconds later via Refresh.

Example:

challenges:
  resource-load:
    runtime: "resource-load"

Custom JavaScript

refresh

refresh-via: javascript

Have the browser solve the challenge by following the URL listed on a <script> tag with window.location instantly.

Example:

challenges:
  js-refresh:
    runtime: "refresh"
    parameters:
      refresh-via: "javascript"

js-pow-sha256

Requires JavaScript and workers, displays challenge site.

Has the user solve a Proof of Work using SHA256 hashes, with configurable difficulty.

Example:

challenges:
  js-pow-sha256:
    runtime: js
    parameters:
      path: "js-pow-sha256"
      js-loader: load.mjs
      wasm-runtime: runtime.wasm
      wasm-runtime-settings:
        difficulty: 20
      verify-probability: 0.02

Other Custom Runtimes

Custom WASM runtime modules follow the WASI wasip1 preview syscall API.

It is recommended using TinyGo to compile / refresh modules, and some function helpers are provided.

If you want to use a different language or compiler, enable wasip1 and the following interface must be exported:

// Allocation is a combination of pointer location in WASM memory and size of it
type Allocation uint64

func (p Allocation) Pointer() uint32 {
	return uint32(p >> 32)
}
func (p Allocation) Size() uint32 {
	return uint32(p)
}


// MakeChallenge MakeChallengeInput / MakeChallengeOutput are valid JSON.
// See lib/challenge/wasm/interface/interface.go for a definition
func MakeChallenge(in Allocation[MakeChallengeInput]) Allocation[MakeChallengeOutput]

// VerifyChallenge VerifyChallengeInput is valid JSON.
// See lib/challenge/wasm/interface/interface.go for a definition
func VerifyChallenge(in Allocation[VerifyChallengeInput]) VerifyChallengeOutput

func malloc(size uint32) uintptr
func free(size uintptr)

Modules will be recreated for each call, so there is no state leftover.

Example:

challenges:
  js-custom:
    runtime: js
    parameters:
      # specifies the folder path that assets are under
      # can be either embedded or external path
      # defaults to name of challenge
      path: "/my/custom/module/path"
      # needs to be under static folder (within above path)
      js-loader: load.mjs
      # needs to be under runtime folder (within above path)
      wasm-runtime: runtime.wasm
      wasm-runtime-settings:
        custom-setting: true
      verify-probability: 0.1