gofiber/fiber · error

decode SHA256 password: %w

Error message

decode SHA256 password: %w

What it means

Returned by parseHashedPassword when a Users value starts with '{SHA256}' but the trailing bytes are not decodable as standard base64. Equivalent to the SHA512 case but expects a 32-byte SHA-256 digest encoded with standard base64 (the Apache '{SHA}' htpasssd style but for SHA-256). Causes startup setup() to fail.

Source

Thrown at middleware/basicauth/config.go:288

	case strings.HasPrefix(h, "{SHA512}"):
		b, err := base64.StdEncoding.DecodeString(h[len("{SHA512}"):])
		if err != nil {
			return nil, fmt.Errorf("decode SHA512 password: %w", err)
		}
		// A digest of the wrong size can never equal a SHA-512 sum, so
		// accepting it would silently reject every password for this user.
		// Report it instead, which surfaces as a panic at startup.
		if len(b) != sha512.Size {
			return nil, ErrInvalidSHA512PasswordLength
		}
		return func(p string) bool {
			sum := sha512.Sum512([]byte(p))
			return subtle.ConstantTimeCompare(sum[:], b) == 1
		}, nil
	case strings.HasPrefix(h, "{SHA256}"):
		b, err := base64.StdEncoding.DecodeString(h[len("{SHA256}"):])
		if err != nil {
			return nil, fmt.Errorf("decode SHA256 password: %w", err)
		}
		if len(b) != sha256.Size {
			return nil, ErrInvalidSHA256PasswordLength
		}
		return func(p string) bool {
			sum := sha256.Sum256([]byte(p))
			return subtle.ConstantTimeCompare(sum[:], b) == 1
		}, nil
	default:
		b, err := hex.DecodeString(h)
		if err != nil || len(b) != sha256.Size {
			if b, err = base64.StdEncoding.DecodeString(h); err != nil {
				return nil, fmt.Errorf("decode SHA256 password: %w", err)
			}
			if len(b) != sha256.Size {
				return nil, ErrInvalidSHA256PasswordLength
			}
		}

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Produce the expected encoding: printf '%s' "$PW" | openssl dgst -sha256 -binary | base64, prefix with '{SHA256}'.
  2. Remove any embedded whitespace/newlines from the digest before storing.
  3. Use standard base64 (A–Z, a–z, 0–9, +, /, '=' padding); avoid URL-safe or hex.
  4. Verify decoded length is 32 bytes to avoid the follow-on ErrInvalidSHA256PasswordLength.

Example fix

// before: 'sha256sum' hex output after the prefix
users := map[string]string{"bob": "{SHA256}" + hexDigest}

// after: raw SHA-256 bytes in standard base64
// $ printf '%s' 'hunter2' | openssl dgst -sha256 -binary | base64
users := map[string]string{"bob": "{SHA256}" + b64Digest}
Defensive patterns

Strategy: validation

Validate before calling

func validSHA256Entry(v string) error {
    const p = "{SHA256}"
    if !strings.HasPrefix(v, p) {
        return fmt.Errorf("missing %q prefix", p)
    }
    b, err := base64.StdEncoding.DecodeString(v[len(p):])
    if err != nil {
        return fmt.Errorf("not standard base64: %w", err)
    }
    if len(b) != sha256.Size {
        return fmt.Errorf("decoded length %d != %d", len(b), sha256.Size)
    }
    return nil
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Users map entry like "{SHA256}" + a hex string, a URL-safe base64 string, a value with truncated padding, or stray whitespace. Also triggered by feeding a digest generated by a tool that defaults to hex (sha256sum) without re-encoding to base64.

Common situations: Running 'echo -n pass | sha256sum' (hex) and pasting after '{SHA256}'; copy/paste dropping '=' padding; YAML/JSON parser stripping trailing '='; using base64 -w0 with URL-safe mode (-e vs -u confusion).

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/2e507eed61a54cc6.json. Report an issue: GitHub.