altcha

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 30, 2024 License: BSD-3-Clause Imports: 15 Imported by: 0

README

go-altcha

ALTCHA is a simple self-hosted alternative to CAPTCHA.

This is an implementation of the server side in Go.

Caveats

This implementation deviates from the specification in two minor ways:

  1. The signatures are encoded using base64 instead of hex. This provides a more compact representation, and is still compatible.

  2. The HTTP handler middleware adds a WWW-Authenticate header when outputting the JSON formatted challenge, and returns a status code of 200. This is unnecessary, and technically incorrect. However, it allows for using the widget and the M2M variant with the same endpoint.

This was written for testing and comparison with other alternatives for CAPTCHA. The code is intended to be suitable for production use. However, I have not evaluated the effectiveness of this challenge/response algorithm against real world adversaries. Similar proof-of-work challenges exist, such as Hashcash, which I have seen implemented in production systems with varying success.

Usage

The simplest way to use this code is as a HTTP handler middleware.

package main

import (
	"github.com/k42-software/go-altcha/http" // altcha
	"net/http"
)

func main() {
	fileServer := http.FileServer(http.Dir("."))
	http.HandleFunc("/altcha.min.js", altcha.ServeJavascript)
	http.Handle("/protected.html", altcha.ProtectForm(fileServer))
	http.Handle("/", fileServer)
	_ = http.ListenAndServe(":3003", http.DefaultServeMux)
}

See the example directory for a more detailed working example.

Alternatively, you can call the ALTCHA library functions directly.

package main

import "github.com/k42-software/go-altcha"

// Generate a challenge
challenge := altcha.NewChallengeEncoded()

// Generate a response
response, ok := altcha.SolveChallenge(challenge, altcha.DefaultComplexity)
if !ok {
    panic("failed to solve challenge")
}

// Validate the response (true for replay prevention)
valid := altcha.ValidateResponse(response, true)

if valid {
    // Success
} else {
    // Failure
}

License

This project is covered by a BSD-style license that can be found in the LICENSE file.

Documentation

Index

Constants

View Source
const DefaultComplexity = 100000

DefaultComplexity is the default complexity used. This should be increased to make the challenge harder to solve. @see https://altcha.org/docs/complexity

View Source
const MinimumComplexity = 1000

MinimumComplexity is the minimum complexity allowed. @see https://altcha.org/docs/complexity

View Source
const TextPrefix = "Altcha "

TextPrefix is the prefix used for the text encoding of the message.

Variables

This section is empty.

Functions

func AddSecretsRotationCallback

func AddSecretsRotationCallback(callback func())

AddSecretsRotationCallback adds a callback function which is called when the secrets are rotated. It is run in a separate goroutine, so that the mutex is not held or locked when the callback is run.

func BanSignature

func BanSignature(signature string)

BanSignature adds the given signature to the list of banned signatures.

func GetSecrets

func GetSecrets() (current, previous string)

GetSecrets returns the current and previous secrets used for the hmac.

func IsSignatureBanned

func IsSignatureBanned(signature string) bool

IsSignatureBanned checks if the given signature is banned.

func NewChallengeEncoded

func NewChallengeEncoded() string

NewChallengeEncoded creates a new challenge with default parameters and encoded for the client.

func RotateSecrets

func RotateSecrets()

RotateSecrets immediately generates a new secret and replaces the previous secret with the current secret. This is concurrency safe and will block until complete.

func SetSecretsRotationInterval

func SetSecretsRotationInterval(interval time.Duration)

SetSecretsRotationInterval sets the interval at which secrets are automatically rotated. Setting the interval to 0 will disable automatic rotation.

func Sign

func Sign(algo Algorithm, text string) string

Sign generates a signature for the given text.

func SolveChallenge

func SolveChallenge(challenge string, maximumComplexity int) (response string, ok bool)

SolveChallenge is a convenience function which decodes the challenge, solves it, and returns the response.

func ValidateResponse

func ValidateResponse(encoded string, preventReplay bool) (ok bool)

ValidateResponse decodes and validates the response from the client.

func VerifySignature

func VerifySignature(algo Algorithm, text string, signature string) (valid bool)

VerifySignature checks if the given signature is valid for the given text.

Types

type Algorithm

type Algorithm int
const (
	UnknownAlgorithm Algorithm = iota
	SHA256
	SHA384
	SHA512
)

func AlgorithmFromString

func AlgorithmFromString(algo string) (Algorithm, bool)

func (Algorithm) String

func (algorithm Algorithm) String() string

type Message

type Message struct {

	// Algorithm is the hashing algorithm used to generate the challenge.
	// Supported algorithms are SHA-256, SHA-384, and SHA-512.
	Algorithm string `json:"algorithm"`

	// Salt is a random string used to generate the challenge.
	// The minimum length is 10 characters.
	Salt string `json:"salt"`

	// Number is the secret number which the client must solve for.
	Number int `json:"number,omitempty"`

	// Challenge is the hash which the client must solve for.
	// The minimum length is 40 characters.
	Challenge string `json:"challenge"`

	// Signature is the signature of the challenge.
	Signature string `json:"signature"`
}

Message represents the messages between the client and server.

func DecodeChallenge

func DecodeChallenge(encoded string) (msg Message, err error)

DecodeChallenge decodes output from NewChallengeEncoded.

func DecodeJSON

func DecodeJSON(encoded []byte) (msg Message, err error)

DecodeJSON decodes a Message stored in JSON format.

func DecodeResponse

func DecodeResponse(encoded string) (msg Message, err error)

DecodeResponse decodes the response Message from the client.

func DecodeText

func DecodeText(encoded string) (msg Message, err error)

DecodeText decodes the output from message.String().

func NewChallenge

func NewChallenge() (msg Message)

NewChallenge creates a new challenge with default parameters.

func NewChallengeWithParams

func NewChallengeWithParams(params Parameters) (msg Message)

NewChallengeWithParams creates a new challenge with the given parameters.

func (Message) Encode

func (message Message) Encode() string

Encode returns the message ready to be sent to the client. The client is expecting the message in raw JSON format.

func (Message) EncodeWithBase64

func (message Message) EncodeWithBase64() string

EncodeWithBase64 returns the message ready to be sent back to the server. The server is expecting the message to be JSON wrapped in base64 encoding.

func (Message) IsValidResponse

func (message Message) IsValidResponse() bool

IsValidResponse is used to validate a decoded response from the client.

func (Message) Solve

func (message Message) Solve(maximumComplexity int) (number int, ok bool)

Solve attempts to solve the challenge within the given maximum complexity.

func (Message) String

func (message Message) String() string

String returns a textual representation of the message in the format defined in the M2M Altcha specification. It is used for both server and client. @see https://altcha.org/docs/m2m-altcha

type Parameters

type Parameters struct {

	// Algorithm is the hashing algorithm used to generate the challenge.
	// Supported algorithms are SHA-256, SHA-384, and SHA-512.
	Algorithm string `json:"algorithm"`

	// Salt is a random string used to generate the challenge.
	// The minimum length is 10 characters.
	Salt string `json:"salt"`

	// Complexity is the number of iterations used to generate the challenge.
	// This is only considered when Number is not provided.
	Complexity int `json:"complexity,omitempty"`

	// Number is the secret number which the client must solve for.
	Number int `json:"number,omitempty"`
}

Parameters are the parameters used to generate a challenge. If any of the parameters are missing, they will be generated.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL