gofiber/fiber · error

decode SHA256 password

Error message

decode SHA256 password: %w

What it means

Returned by parseHashedPassword when a '{SHA256}'-prefixed password string cannot be base64-decoded. Same shape as the SHA512 case but for the 32-byte SHA-256 digest path. The {SHA256} prefix is what Apache's htpasswd emits for SHA-256, so mis-serialized htpasswd entries are the most common cause.

Solutions

  1. Regenerate with standard base64 of the raw 32-byte SHA-256 digest.
  2. Verify with htpasswd -vb that the entry is well-formed before pasting.
  3. Strip whitespace and confirm exactly one '='-padded token follows {SHA256}.
  4. Ensure the decoded length is 32 bytes (sha256.Size) to avoid the next guard's ErrInvalidSHA256PasswordLength.
  5. Quote the value in YAML/JSON to prevent '+/=' interpretation.

Example fix

// before
"{SHA256}" + strings.ReplaceAll(stdB64, "+", "-") // accidentally URL-safe

// after
"{SHA256}" + base64.StdEncoding.EncodeToString(sum[:])
Defensive patterns

Strategy: validation

Validate before calling

func validateSHA256Hash(h string) error {
    if !strings.HasPrefix(h, "{SHA256}") { return nil }
    b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(h, "{SHA256}"))
    if err != nil { return fmt.Errorf("bad SHA256 base64: %w", err) }
    if len(b) != sha256.Size { return fmt.Errorf("SHA256 digest must be %d bytes, got %d", sha256.Size, len(b)) }
    return nil
}

Prevention

When it happens

Trigger: Users map value is '{SHA256}<not-base64>': bad characters, URL-safe alphabet, missing padding, or trailing newline. Frequently seen when migrating entries from Apache htpasswd that were re-encoded or truncated.

Common situations: htpasswd -s output pasted with a trailing newline; secret manager base64'd the already-base64 value; encoding alphabet mismatch (URL-safe vs standard); YAML loader that interpreted '+/=' as special.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/2e507eed61a54cc6. Report an issue: GitHub.

Appendix: 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 a105acad6c)