Sicurezza¶
Questa pagina raccoglie autenticazione, autorizzazione e protezioni trasversali. I permessi di team sono descritti in Team e ruoli.
Account¶
Stati¶
User ha tre stati: STATUS_ACTIVE (10), STATUS_INACTIVE (9, non attivato)
e STATUS_DELETED (0, disattivato). Login, ricerca per nome utente e token API
considerano solo gli account attivi: disattivare un utente chiude subito anche
il suo accesso all'API.
Registrazione¶
SignupForm::signup() crea l'utente già attivo con UserRegistration, che
gli assegna il ruolo RBAC user, crea lo spazio personale e avvisa gli
amministratori; poi invia l'email di benvenuto ed entra. Lo stesso servizio crea
gli account dal pannello, con il ruolo scelto e una password temporanea. Il flusso di verifica dell'email esiste ancora nel
codice ma non si usa, perché richiede account non attivati.
Login¶
- "Ricordami" dura 30 giorni.
LoginThrottleblocca per 15 minuti dopo 5 tentativi falliti per coppia nome utente + IP, o dopo 20 per IP. Durante il blocco la password non viene nemmeno verificata.- I tentativi falliti finiscono nel registro operazioni con il motivo interno.
- Il login del pannello accetta solo
adminesuperAdmin.
Verifica in due passaggi¶
Facoltativa, con codici TOTP (RFC 6238: SHA-1, 6 cifre, 30 secondi), attivabile
dal profilo (TwoFactorService, Totp):
- il segreto si salva cifrato con
params['totpEncryptionKey'](in DockerTOTP_ENC_KEY). Senza chiave la funzione resta spenta; cambiare la chiave rende illeggibili i segreti salvati, quindi chi l'ha attivata dovrà essere azzerato; - con la verifica attiva
LoginFormnon apre la sessione: salva in sessione chi ha superato la password (5 minuti) e rimanda asite/two-factor, sia nel frontend sia nel pannello. LìTwoFactorFormaccetta il codice dell'app (uno step di tolleranza, mai lo stesso step due volte grazie atotp_last_step) o uno dei 10 codici di recupero monouso, salvati come hash sha256; - i codici sbagliati hanno un contatore
LoginThrottleseparato (totp:<nome>), che il login con password non azzera; - attivare, disattivare o azzerare rigenera
auth_key, così i cookie "ricordami" emessi prima non valgono più. Ogni passaggio finisce nel registro operazioni come "Verifica in due passaggi"; - il super amministratore la azzera dal pannello (non sugli account protetti
altrui);
php yii account/reset-two-factor <email>è l'uscita di emergenza; - i token API restano invariati: si generano solo da una sessione web già autenticata.
Password¶
- Hash bcrypt di Yii; unica regola la lunghezza minima,
params['user.passwordMinLength'](8). - Il recupero password non rivela se l'account esiste; il token dura un'ora.
- Reimpostare la password (dal recupero o dal pannello) rigenera
auth_key, che chiude le sessioni "ricordami".
Account protetti¶
Gli account in params['protectedAccounts'] (per email, perché l'id cambia fra
installazioni) sono garantiti da AccountProtection:
- restano sempre super amministratori:
ProtectedDbManagerignora la revoca; - restano sempre attivi:
User::beforeSavelo impedisce; - l'email non si cambia, se non modificando prima la configurazione;
- nessuno può generare loro una password temporanea dal pannello.
AccountProtectionBootstrap riallinea l'account prima di ogni azione web;
php yii account/protect fa lo stesso da console.
RBAC¶
authManager è common\rbac\ProtectedDbManager, un DbManager che in più
protegge gli account protetti e registra ogni assegnazione e revoca.
Ruoli e permessi¶
Lo stato attuale nasce dalla migrazione m260704_121829_rebuild_rbac_roles.
- Ruoli globali predefiniti:
superAdmin,admin,user, alternativi fra loro (GlobalRoleManager::BUILTIN). - Permessi globali (regola
globalAdmin, sempre vera):admin.panel,tag.global,user.manage,user.deactivate,team.create(oggi solo del super amministratore). - Ruoli di team RBAC:
teamViewer⊂teamMember⊂teamEditor⊂teamAdmin, con i permessi di team (regolateamMember). - Ruoli globali personalizzati: ruoli RBAC
custom:<slug>che si sommano al ruolo predefinito e possono concedereadmin.panel,tag.global,user.manage,user.deactivate. Si gestiscono dal pannello, alla voce "Ruoli globali"; l'etichetta sta indata['label'], il nome tecnico non cambia.
Come si decide un permesso di team¶
Yii::$app->user->can('task.update', ['team_id' => $teamId]);
Di solito attraverso TaskAccess::ensureCan(). La regola TeamMemberRule:
- senza
team_idnega; - se l'utente non è membro, concede solo
board.viewe solo agli overseer; - se il team è nel cestino nega;
- altrimenti chiede a
TeamPermissionResolverper il ruolo del membro.
Per questo admin e superAdmin, pur ereditando teamAdmin, non hanno
accesso ai team di cui non sono membri.
Altre politiche¶
| Classe | Regola |
|---|---|
TaskAccess |
permessi sulle task; ensureActionable() blocca le task bloccate; ensureCanDelete() |
TagPolicy |
etichette globali con tag.global, di bacheca con tag.manage |
TrashPolicy |
chi elimina task, chi vede e svuota il cestino (vedi Cestino) |
TeamVisibility |
quali team vede un utente (vedi Team e ruoli) |
API¶
- I token personali sono 48 caratteri casuali; nel database c'è solo lo SHA-256. Lo stesso vale per i token del feed iCal e per i token master.
- I token master (prefisso
gtm_) operano per conto dell'utente indicato inX-Act-As, solo sulle rotte abilitate e mai oltre i permessi di quell'utente. Vedi API HTTP. - I token OAuth del connettore MCP (
gto_di accesso, un'ora;gtr_di rinnovo, 30 giorni) valgono quanto il token personale di chi ha dato il consenso. Si salvano solo gli SHA-256; codice di autorizzazione e refresh sono monouso e il rinnovo è atomico, così due richieste con lo stesso segreto non ottengono entrambe un token. Un utente disattivato li perde subito; dal profilo l'utente vede le app collegate e le revoca. - Il consenso OAuth richiede PKCE S256 e un indirizzo di ritorno registrato e
uguale; i client si registrano solo con indirizzi
https:ohttpverso localhost, al massimo 20 l'ora per IP. Un client sconosciuto o un indirizzo sbagliato non provocano un redirect: l'errore si mostra sulla pagina. - Nel consenso l'utente sceglie cosa concedere all'app (sola lettura, lavoro quotidiano, tutto) e il token è legato al connettore per cui è stato chiesto: non vale sull'altro connettore né, da solo, sull'API. Un refresh già sostituito che si ripresenta revoca l'intera autorizzazione. Dettagli in Connettore MCP.
- Le operazioni pesanti chieste da Claude (scritture dell'amministrazione, cestino, stati, membri, ruoli e permessi dei team, webhook) partono solo dopo che l'utente le approva in GesTask, una volta e per dieci minuti (Operazioni da approvare).
- I client OAuth che si presentano con un Client ID Metadata Document vengono letti con la stessa protezione SSRF dei webhook.
- Dall'API e dal connettore non si toccano credenziali: niente password, verifica in due passaggi, token personali o master, propri o altrui.
- Limite di 120 richieste al minuto per utente, con gli header
X-Rate-Limit-*. Per il connettore MCP conta ogni tool eseguito. - L'API non ha sessione, CSRF né cookie; accetta solo le rotte dichiarate.
Header HTTP¶
SecurityHeadersBootstrap imposta, senza sovrascrivere quelli già presenti:
X-Frame-Options: SAMEORIGIN,X-Content-Type-Options: nosniff;Referrer-Policy: strict-origin-when-cross-origin;Permissions-Policy: camera=(), microphone=(), geolocation=();Content-Security-Policy: frame-ancestors 'self'; object-src 'none'; base-uri 'self'; form-action 'self';Strict-Transport-Securitysulle richieste HTTPS.
Note
La CSP non limita ancora gli script, perché le viste usano JavaScript in linea. Restringerla richiede prima di spostare quel codice in file.
Sessioni e CSRF¶
- Frontend e pannello hanno sessioni, cookie d'identità (httpOnly) e parametri CSRF separati: un login nel frontend non apre il pannello.
- Le azioni che modificano dati accettano solo
POST(VerbFilter). cookieValidationKeysta neimain-local.php, generati daphp init.
Allegati¶
AttachmentStorage salva i file in
storage/attachments/tasks/<uuid-task>/<nome-casuale>.<estensione>, fuori dalle
cartelle web: nessun file caricato è raggiungibile o eseguibile direttamente.
- Sono ammesse solo le estensioni in elenco (niente HTML, SVG o JavaScript) e il tipo MIME inviato al browser dipende dall'estensione.
- Dimensione massima
params['attachments']['maxSize'](20 MB). Va tenuta sottoupload_max_filesizeepost_max_sizedi PHP. - Il download richiede
board.viewsul team e passa daAttachmentDownload:nosniff, e per tutto ciò che non è PDF una CSPdefault-src 'none'; sandbox. Solo PDF e immagini si aprono nel browser; ogni download si registra. - Gli avatar sono un'eccezione: stanno in
frontend/web/uploads/avatars, pubblici, ricodificati in WebP.
Altre protezioni¶
- SSRF: ogni richiesta verso indirizzi scelti dagli utenti (webhook, Slack,
Web Push) passa da
OutboundUrlGuard(vedi Webhook). - CSV:
CsvSafetyantepone'alle celle che iniziano con=,+,-,@, tabulazione o ritorno a capo, contro l'esecuzione di formule nei fogli di calcolo. Vale per tutte le esportazioni. - Identificatori: URL e API usano solo uuid; gli id numerici non escono.
- Registro operazioni: vedi Registro operazioni.