Backend: rate limiter, security headers, blockchain cache service avec RPC, public API (7 endpoints read-only), WebSocket auth + heartbeat, DB connection pooling, structured logging, health check DB. Frontend: API retry/timeout, WebSocket auth + heartbeat + typed events, notifications toast, mobile hamburger + drawer, error boundary, offline banner, loading skeletons, dashboard enrichi. Documentation: guides utilisateur complets (demarrage, vote, sanctuaire, FAQ 30+), guide deploiement, politique securite. 123 tests, 155 fichiers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
279 lines
11 KiB
Markdown
279 lines
11 KiB
Markdown
---
|
|
title: Securite
|
|
description: Politique de securite et mesures de protection de Glibredecision
|
|
---
|
|
|
|
# Securite
|
|
|
|
Ce document decrit les mesures de securite implementees dans Glibredecision pour proteger l'integrite de la plateforme, des votes et des donnees des utilisateurs.
|
|
|
|
## Authentification Duniter V2 (Ed25519 challenge-response)
|
|
|
|
### Principe
|
|
|
|
Glibredecision n'utilise ni mot de passe ni systeme d'inscription classique. L'authentification repose entierement sur la cryptographie Ed25519 de la blockchain Duniter V2.
|
|
|
|
### Flux challenge-response
|
|
|
|
```
|
|
Client Serveur
|
|
| |
|
|
|-- POST /auth/challenge ----------->|
|
|
| { address: "5Grw..." } |
|
|
| |-- Genere challenge (64 hex)
|
|
| |-- Stocke en memoire (TTL 5 min)
|
|
|<-------- { challenge: "a1b2..." } -|
|
|
| |
|
|
|-- Signe le challenge localement |
|
|
| (cle privee Ed25519) |
|
|
| |
|
|
|-- POST /auth/verify ------------->|
|
|
| { address, challenge, signature}|
|
|
| |-- Verifie signature Ed25519
|
|
| |-- Verifie identite WoT
|
|
| |-- Cree/retrouve DuniterIdentity
|
|
| |-- Genere token de session
|
|
|<-------- { token: "..." } --------|
|
|
```
|
|
|
|
### Garanties de securite
|
|
|
|
| Propriete | Mecanisme |
|
|
| --------- | --------- |
|
|
| **Cle privee jamais transmise** | Seule la signature est envoyee, pas la cle |
|
|
| **Anti-replay** | Chaque challenge est usage unique, expire apres 5 minutes |
|
|
| **Anti-interception** | HTTPS/TLS obligatoire en production |
|
|
| **Identite verifiee** | L'adresse SS58 est verifiee sur la blockchain via `substrate-interface` |
|
|
|
|
### Verification de la signature
|
|
|
|
```python
|
|
from substrateinterface import Keypair
|
|
|
|
keypair = Keypair(ss58_address=address)
|
|
is_valid = keypair.verify(challenge_bytes, signature_bytes)
|
|
```
|
|
|
|
La cle publique est derivee de l'adresse SS58 sans aucun appel reseau. La verification est locale et instantanee.
|
|
|
|
## Integrite des votes
|
|
|
|
### Signature cryptographique des votes
|
|
|
|
Chaque vote soumis est accompagne d'une **signature Ed25519** qui garantit son authenticite et son integrite.
|
|
|
|
### Payload signe
|
|
|
|
Le payload signe contient :
|
|
|
|
```json
|
|
{
|
|
"session_id": "uuid-de-la-session",
|
|
"vote_value": "for",
|
|
"timestamp": "2026-02-28T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
Ce payload est signe par la cle privee du votant. La signature est stockee en base de donnees avec le vote, permettant une verification independante a tout moment.
|
|
|
|
### Proprietes garanties
|
|
|
|
| Propriete | Description |
|
|
| --------- | ----------- |
|
|
| **Authenticite** | Seul le proprietaire de l'adresse Duniter peut soumettre un vote en son nom |
|
|
| **Integrite** | Le payload ne peut pas etre modifie apres signature sans invalider la signature |
|
|
| **Non-repudiation** | Le votant ne peut pas nier avoir vote ; la preuve cryptographique est publique |
|
|
| **Transparence** | Les votes signes sont publics et verifiables par quiconque |
|
|
|
|
### Verification d'un vote
|
|
|
|
```python
|
|
from substrateinterface import Keypair
|
|
|
|
keypair = Keypair(ss58_address=voter_address)
|
|
is_valid = keypair.verify(signed_payload.encode(), signature_bytes)
|
|
```
|
|
|
|
### Protection contre la modification
|
|
|
|
Quand un votant modifie son vote, l'ancien vote est marque `is_active = false` mais conserve en base. Les deux votes (ancien et nouveau) conservent leur signature. L'historique complet est auditable.
|
|
|
|
## Rate limiting
|
|
|
|
### Limites par endpoint
|
|
|
|
Pour prevenir les abus et les attaques par deni de service, des limites de taux sont appliquees :
|
|
|
|
| Categorie | Endpoints | Limite | Fenetre |
|
|
| --------- | --------- | ------ | ------- |
|
|
| Authentification | `/auth/challenge`, `/auth/verify` | 10 requetes | 1 minute |
|
|
| Vote | `/votes/sessions/{id}/vote` | 5 requetes | 1 minute |
|
|
| Ecriture | `POST`, `PUT`, `DELETE` | 30 requetes | 1 minute |
|
|
| Lecture | `GET` | 200 requetes | 1 minute |
|
|
| WebSocket | `/ws` | 3 connexions | simultanees par IP |
|
|
|
|
### Reponse en cas de depassement
|
|
|
|
Le serveur retourne un code HTTP **429 Too Many Requests** avec un header `Retry-After` indiquant le temps d'attente en secondes.
|
|
|
|
## En-tetes de securite
|
|
|
|
Les en-tetes HTTP suivants sont configures en production :
|
|
|
|
| En-tete | Valeur | Description |
|
|
| ------- | ------ | ----------- |
|
|
| `Strict-Transport-Security` | `max-age=63072000; includeSubDomains; preload` | Force HTTPS pendant 2 ans, inclut les sous-domaines |
|
|
| `Content-Security-Policy` | `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' wss:; img-src 'self' data: https://ipfs.io` | Restreint les sources de contenu autorisees |
|
|
| `X-Content-Type-Options` | `nosniff` | Empeche le navigateur de deviner le type MIME |
|
|
| `X-Frame-Options` | `DENY` | Empeche l'inclusion dans un iframe (anti-clickjacking) |
|
|
| `X-XSS-Protection` | `1; mode=block` | Active la protection XSS du navigateur |
|
|
| `Referrer-Policy` | `strict-origin-when-cross-origin` | Limite les informations de referer |
|
|
| `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | Desactive les APIs sensibles non necessaires |
|
|
|
|
### CSP detaillee
|
|
|
|
La Content Security Policy est configuree pour :
|
|
|
|
- Autoriser les scripts uniquement depuis l'origine (`'self'`)
|
|
- Autoriser les styles inline (necessaires pour UnoCSS)
|
|
- Autoriser les connexions WebSocket (`wss:`)
|
|
- Autoriser les images depuis IPFS gateway (`https://ipfs.io`) pour l'affichage des contenus archives
|
|
- Bloquer tout le reste
|
|
|
|
## Sanctuaire : triple preuve d'integrite
|
|
|
|
### Chaine de verification
|
|
|
|
```
|
|
Contenu --> [SHA-256] --> hash
|
|
|
|
|
+---> [IPFS] --> CID (content-addressable)
|
|
|
|
|
+---> [system.remark on-chain] --> preuve horodatee
|
|
|
|
|
v
|
|
Triple preuve :
|
|
1. Hash local = hash enregistre (integrite)
|
|
2. CID IPFS = contenu identique (distribution)
|
|
3. Remark on-chain = hash confirme (immutabilite temporelle)
|
|
```
|
|
|
|
### Garanties
|
|
|
|
| Preuve | Ce qu'elle garantit | Point de defaillance |
|
|
| ------ | ------------------- | -------------------- |
|
|
| SHA-256 | Le contenu n'a pas ete modifie | Aucun (mathematique) |
|
|
| IPFS CID | Le contenu est distribue et adressable par contenu | Disponibilite des noeuds IPFS |
|
|
| On-chain remark | Le hash existait a la date du bloc | Securite de la blockchain Duniter |
|
|
|
|
### Format du remark on-chain
|
|
|
|
```
|
|
glibredecision:sanctuary:{content_hash_sha256}
|
|
```
|
|
|
|
Le prefixe `glibredecision:sanctuary:` permet d'identifier les ancrages de Glibredecision parmi tous les remarks de la blockchain.
|
|
|
|
## WebSocket : authentification et securite
|
|
|
|
### Authentification par token
|
|
|
|
La connexion WebSocket est authentifiee via le token de session obtenu lors du challenge-response :
|
|
|
|
```
|
|
ws://server/api/v1/ws?token={session_token}
|
|
```
|
|
|
|
Le serveur verifie le token avant d'accepter la connexion. Les connexions non authentifiees sont rejetees.
|
|
|
|
### Protections WebSocket
|
|
|
|
| Protection | Description |
|
|
| ---------- | ----------- |
|
|
| Token valide requis | Pas de connexion anonyme |
|
|
| Limite de connexions | Maximum 3 connexions simultanees par IP |
|
|
| Heartbeat | Ping/pong periodique pour detecter les connexions mortes |
|
|
| Taille maximale des messages | Limite a 64 Ko pour prevenir les abus |
|
|
| Broadcast read-only | Les clients recoivent les mises a jour mais ne peuvent pas modifier l'etat via WebSocket |
|
|
|
|
## Session management
|
|
|
|
### Tokens de session
|
|
|
|
| Propriete | Valeur |
|
|
| --------- | ------ |
|
|
| Duree de vie (TTL) | 24 heures |
|
|
| Stockage serveur | Hash SHA-256 du token en base (table `sessions`) |
|
|
| Stockage client | Token en clair dans le `localStorage` du navigateur |
|
|
| Invalidation | Suppression de l'entree en base via `/auth/logout` |
|
|
|
|
### Securite des tokens
|
|
|
|
- Le token est genere avec un generateur cryptographiquement sur (CSPRNG)
|
|
- Seul le **hash** du token est stocke en base de donnees. Si la base est compromise, les tokens bruts ne sont pas exposes.
|
|
- A chaque requete authentifiee, le token fourni est hashe et compare au hash en base
|
|
- Les tokens expires sont nettoyes periodiquement
|
|
|
|
### Deconnexion
|
|
|
|
L'appel a `POST /auth/logout` invalide la session cote serveur en supprimant l'entree de la table `sessions`. Le client supprime le token de son `localStorage`.
|
|
|
|
## Audit logging
|
|
|
|
### Evenements traces
|
|
|
|
Les actions suivantes sont enregistrees pour auditabilite :
|
|
|
|
| Evenement | Donnees enregistrees |
|
|
| --------- | -------------------- |
|
|
| Authentification reussie | Adresse SS58, timestamp, IP |
|
|
| Authentification echouee | Adresse SS58, raison, timestamp, IP |
|
|
| Vote soumis | Session ID, voter ID, vote, signature, timestamp |
|
|
| Vote modifie | Session ID, voter ID, ancien vote (desactive), nouveau vote |
|
|
| Decision creee/avancee | Decision ID, auteur, action, timestamp |
|
|
| Document archive | Document ID, hash, CID, tx_hash |
|
|
| Session de vote cloturee | Session ID, resultat, seuils |
|
|
| Mandat assigne/revoque | Mandate ID, mandataire, action |
|
|
|
|
### Conservation
|
|
|
|
Les logs d'audit sont conserves de maniere permanente dans la base de donnees. Les votes et leurs signatures sont particulierement importants car ils constituent la preuve cryptographique des decisions collectives.
|
|
|
|
## Signalement de vulnerabilite (Responsible disclosure)
|
|
|
|
### Processus
|
|
|
|
Si vous decouvrez une vulnerabilite de securite dans Glibredecision, merci de suivre cette procedure de divulgation responsable :
|
|
|
|
1. **Ne divulguez pas publiquement** la vulnerabilite avant qu'un correctif soit disponible.
|
|
2. **Contactez l'equipe** via le canal securise indique sur le depot Git Duniter ou via le forum Duniter (message prive aux mainteneurs).
|
|
3. **Decrivez la vulnerabilite** avec autant de details que possible :
|
|
- Type de vulnerabilite (injection, XSS, CSRF, contournement d'authentification, etc.)
|
|
- Etapes pour reproduire
|
|
- Impact potentiel
|
|
- Suggestion de correctif (si applicable)
|
|
4. **Delai de correction** : l'equipe s'engage a accuser reception sous 48 heures et a fournir un correctif sous 14 jours pour les vulnerabilites critiques.
|
|
5. **Credit** : les chercheurs en securite qui signalent des vulnerabilites de maniere responsable seront credites dans le changelog (sauf s'ils preferent l'anonymat).
|
|
|
|
### Perimetre
|
|
|
|
Le perimetre de la politique de securite couvre :
|
|
|
|
| Inclus | Exclus |
|
|
| ------ | ------ |
|
|
| API backend (FastAPI) | Infrastructure d'hebergement tiers |
|
|
| Frontend (Nuxt 4) | Blockchain Duniter V2 elle-meme |
|
|
| Authentification challenge-response | Extension Polkadot.js |
|
|
| Integrite des votes et signatures | Noeud IPFS (kubo) lui-meme |
|
|
| WebSocket | Traefik |
|
|
| Base de donnees (acces, injection) | -- |
|
|
|
|
### Exclusions
|
|
|
|
Les types de signalement suivants sont hors perimetre :
|
|
|
|
- Deni de service par volume (DDoS)
|
|
- Ingenierie sociale
|
|
- Attaques physiques sur l'infrastructure
|
|
- Vulnerabilites dans les dependances tierces deja connues et trackees (utiliser les issues du depot)
|