npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

gdpr-captcha

v1.0.0

Published

Self-hosted GDPR-friendly CAPTCHA service with a lightweight client widget.

Readme

GDPR-Friendly Self-Hosted CAPTCHA

Un CAPTCHA auto‑hébergé, respectueux de la vie privée, facile à intégrer sur n’importe quel site. Aucune dépendance tierce, pas de tracking, pas de cookies par défaut.

  • Vérification stateless via jetons signés (pas de stockage serveur de la solution)
  • Défis en SVG (pas de dépendances natives)
  • Honeypot + temps minimum de résolution (anti-bot)
  • Widget client simple + pages de démo

Démarrage rapide (Windows PowerShell)

  1. Créez un .env depuis .env.example et définissez un JWT_SECRET fort.

  2. Installez puis lancez:

Set-Location "c:\\Users\\leoba\\Downloads\\Capcha"
if (-Not (Test-Path .env)) { Copy-Item .env.example .env }
npm.cmd install
npm.cmd start
  1. Ouvrez la démo: http://localhost:8787/demo/

Endpoints API

  • GET /api/captcha/init{ token, svg, minMs }
    • Retourne un défi sous forme d’SVG et un jeton signé encapsulant le hash attendu
  • POST /api/captcha/verify body { token, answer, elapsedMs, honeypot }{ ok, passToken? }
    • Vérifie la réponse; si correcte, renvoie un passToken court‑terme
  • POST /api/captcha/validate body { passToken }{ ok }
    • À appeler côté serveur de votre site avant d’accepter la soumission

Intégration sur vos sites

Incluez les assets du widget depuis votre serveur CAPTCHA (ou même origine):

<link rel="stylesheet" href="https://votre-domaine-captcha/captcha/widget.css">
<script src="https://votre-domaine-captcha/captcha/widget.js" defer></script>

Montez le widget dans votre formulaire:

<form method="post" action="/submit">
  <!-- … vos champs … -->
  <div id="captcha"></div>
  <button type="submit">Envoyer</button>
</form>
<script>
  // Si votre CSP interdit l’inline JS, placez ce code dans un fichier .js séparé
  GdprCaptcha.mount('#captcha', { endpoint: 'https://votre-domaine-captcha', locale: 'fr' });
//</script>

Au submit, le widget ajoute un input caché captcha_pass contenant un passToken. Votre backend doit le valider avant tout traitement.

Validation côté serveur (exemples)

Node/Express (via l’endpoint de validation):

// Exemple de route Express dans VOTRE site
app.post('/submit', async (req, res) => {
  const passToken = req.body.captcha_pass;
  const r = await fetch('https://votre-domaine-captcha/api/captcha/validate', {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ passToken })
  });
  const data = await r.json();
  if (!data.ok) return res.status(400).send('Captcha invalide');
  // Continuer le traitement
  res.send('OK');
});

PHP (cURL):

$passToken = $_POST['captcha_pass'] ?? '';
$ch = curl_init('https://votre-domaine-captcha/api/captcha/validate');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode(['passToken' => $passToken]),
  CURLOPT_RETURNTRANSFER => true,
]);
$out = curl_exec($ch);
$data = json_decode($out, true);
if (!isset($data['ok']) || !$data['ok']) {
  http_response_code(400);
  exit('Captcha invalide');
}
// Continuer le traitement

Python (Django/Flask):

import requests

def validate_captcha(pass_token: str) -> bool:
    r = requests.post('https://votre-domaine-captcha/api/captcha/validate', json={'passToken': pass_token}, timeout=5)
    r.raise_for_status()
    return r.json().get('ok', False)

CORS et CSP

  • Si votre site est sur un autre domaine, ajoutez son origine dans .envALLOWED_ORIGINS=https://site1.com,https://site2.com
  • CSP recommandée (via Helmet) côté serveur CAPTCHA:
// Exemple: restreindre les scripts aux fichiers statiques (pas d'inline)
app.use(helmet.contentSecurityPolicy({
  useDefaults: true,
  directives: {
    "script-src": ["'self'"],
    "style-src": ["'self'", "'unsafe-inline'"],
    "img-src": ["'self'", "data:"]
  }
}));

Durcir la détection anti-bot (mode pro)

Les recommandations suivantes rendent l’automatisation plus coûteuse tout en restant RGPD‑friendly:

  1. Limitation de débit côté edge/proxy
  • NGINX (exemple):
limit_req_zone $binary_remote_addr zone=captcha:10m rate=30r/m;
server {
  location /api/captcha/ { limit_req zone=captcha burst=10 nodelay; }
}
  • Node (Express):
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({ windowMs: 60_000, max: 60 });
app.use('/api/captcha/', limiter);
  1. Temps minimum et délais
  • Augmentez CAPTCHA_MIN_MS (ex: 3000–6000ms). Les scripts rapides échouent.
  • Introduisez, côté frontend, un petit délai progressif après plusieurs erreurs.
  1. Honeypots et champs dynamiques
  • Le widget intègre un honeypot; ajoutez aussi un champ honeypot côté formulaire de votre site.
  • Faites tourner les noms d’inputs (ex: contact_phonec_phone_923) et ignorez les soumissions sans captcha_pass.
  1. Validation stricte côté serveur de votre site
  • Rejetez si: pas de captcha_pass, token expiré, trop d’essais récents, user‑agent suspect.
  • Vérifiez l’en‑tête Origin/Referer lorsque c’est pertinent.
  1. Variantes de défi (alterner régulièrement)
  • Texte tordu (par défaut, via svg-captcha).
  • Expression mathématique simple (ex: « 3 + 7 = ? ») rendue en SVG.
  • Question‑réponse courte personnalisée (ex: « Couleur du ciel ? » → « bleu »).

Astuce: alternez aléatoirement le type de défi et le thème visuel (couleurs, bruit, police) pour éviter l’apprentissage automatique sur un seul format.

  1. Jetons et sécurité
  • Faites tourner JWT_SECRET périodiquement.
  • Réduisez PASS_TOKEN_TTL_S (ex: 5–10 min) selon vos besoins.
  • Envisagez de lier le passToken à un contexte serveur (ex: session interne) au moment de votre validation.
  1. Observabilité sans données perso
  • Gardez des compteurs d’erreurs globales par minute (sans IPs) pour déclencher du throttling.
  • Journalisez seulement les raisons techniques (ex: too_fast, wrong_answer).

Accessibilité

  • Entrée texte accessible clavier, ARIA live pour le statut.
  • Prévoyez une alternative de contact pour les personnes ayant une déficience visuelle.

Configuration

.env:

  • PORT (défaut 8787) — port du serveur
  • JWT_SECRET — secret fort requis pour signer les jetons
  • ALLOWED_ORIGINS — origines autorisées par CORS (séparées par virgules)
  • CAPTCHA_MIN_MS — temps minimal (ms) avant d’accepter la vérification
  • CAPTCHA_TOKEN_TTL_S — validité des jetons de défi
  • PASS_TOKEN_TTL_S — validité des jetons de passage
  • CAPTCHA_TYPES — types de défis autorisés (ex: digits,math). Par défaut: digits,math pour éviter tout texte alphabétique.

Notes RGPD

  • Aucun appel tiers; pas de tracking; pas de cookies par défaut.
  • Données traitées: réponse saisie, chronométrage (ms), états de jetons.
  • IP non stockées par défaut; appliquez la limitation au niveau du proxy.
  • Voir PRIVACY.md.

Licence

MIT