Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
codice o viceversa)) per gli esercizi si puó usare il materiale ma non per il quiz.
L'esame è diviso in 3 ore ed il tempo che salvi puoi usarlo nella parte successiva.
Lezione 1
Why design matters for security?
Meglio puntare alla stabilità che all'efficienza.
Quando faccio un get dobbiamo sapere se quel parametro su cui facciamo get é mutable
oppure no e questo dipende dal fatto che per array o matrici viene passato il riferimento e
quindi possono mutare. Stessa cosa per le stringhe in c++.
Nella realizzazione di un progetto dobbiamo parlare con i clienti per le principali funzionalità,
implementare la logica di business del software e poi la sicurezza che è un livello
trasparente. Quando il software è pronto per la release dobbiamo fare dei test di
penetrazione e di sicurezza per controllare il funzionamento.
Lezione 2
CIA-T:
● Confidentiality: mantenere segreto ciò che non dev'essere pubblico;
● Integrity: le informazioni possono cambiare solo nel modo autorizzato;
● Availability: assicurare l'accesso alle informazioni quando necessario;
● Traceability: tenere traccia di chi accede o modifica i dati.
final in java significa che puoi assegnare quella variabile che poi non cambierà diventando
immutabile.
validateForXSS fa un check della validità. Se la single quote ' non è possibile non c'è SQL
injection.
Trim è un sanificatore che elimina ció che impostiamo.
Qui è inutile mettere private sulla string value perchè abbiamo messo final, il che prevede
un'unica modifica e poi le stringhe in java sono immutable, quindi non potremmo modificarla
nuovamente. Abbiamo messo una specie di get con il return di value, quindi non cambia se
sia public.
Deep Modeling
Mai usare float o double per valori precisi perché per definizione float e double sono valori
non precisi.
Anche per le stringhe non dobbiamo concedere qualsiasi tipo di stringa per sicurezza. Se
abbiamo due stringhe e le scambiamo il compilatore non può darci nessun aiuto su questo.
Occorre rappresentare tutti i concetti del dominio come una classe. Ogni classe è
responsabile della validazione dei suoi dati. Se non usiamo una classe dobbiamo ripetere la
validazione piú volte nel codice.
Casa
La tipizzazione statica è un tipo particolare di controllo statico , che significa verificare la
presenza di bug in fase di compilazione.
Il codice duplicato è un rischio per la sicurezza. Se hai codice identico o molto simile in due
posti, il rischio fondamentale è che ci sia un bug in entrambe le copie e che qualche
manutentore risolva il bug in un posto ma non nell'altro.
I bravi sviluppatori di software scrivono commenti nel loro codice e lo fanno con giudizio. I
buoni commenti dovrebbero rendere il codice più facile da comprendere, più sicuro dai bug
(perché sono stati documentati presupposti importanti) e pronto per il cambiamento.
In Java viene dichiarata una variabile globale public static. Il public modificatore lo
rende accessibile ovunque e static significa che esiste una singola istanza della
variabile.
Tuttavia, con un modificatore aggiuntivo, public static final il tipo della variabile è
immutabile, il nome diventa costante globale. Una costante globale può essere letta
ovunque nel programma ma mai riassegnata o modificata, quindi i rischi scompaiono.
Quando disegniamo diagrammi istantanei, è importante distinguere tra diversi tipi di variabili:
Lezione 3
Domain-Driven Design
E’ una metodologia per lo sviluppo di software complessi. Si concentra sul dominio. Se
dobbiamo incrementare qualcosa dobbiamo capire cosa dobbiamo incrementare parlando
con il cliente.
Il modello del sistema non può essere ambiguo e dev’essere rigorosamente definito e il
sistema deve fare soltanto quel lavoro per cui è stato progettato. Per ogni contesto del
sistema dobbiamo creare un modulo, i quali possono comunicare tra loro e scambiare dati.
Questa suddivisione in moduli facilità lo sviluppo del software. Un modello è un’astrazione
della realtà che in questo caso facilità la progettazione ed il funzionamento del software. Ci
sono diverse rappresentazioni dello stesso modello. Il modello che scegliamo dipende dal
contesto. Il principio da utilizzare è KISS (keep it simple and stupid).
Vogliamo semplificare il modello per essere precisi e formali, eliminando forme di ambiguità.
Semplificare troppo non è un bene, e se i tipi diventano troppo generici significa che
potrebbe esserci un problema di sicurezza. Per risolvere il problema di oversimplify significa
che non abbiamo capito il dominio. Dobbiamo continuare a chiedere all’esperto del dominio
finché non è definito formalmente il modello.
Quando parliamo con un cliente ci dev’essere un accordo.
Entità
Un'entità rappresenta qualcosa che può cambiare nel nostro programma. Siccome un’entità
può cambiare abbiamo bisogno di qualcosa per distinguerla che prende il nome di identità.
L’identità dell’entità è un identificatore che può essere un numero progressivo oppure una
stringa o altro che fa distinguerlo tra le varie entità. L’identità dell’entità è associata all’intera
vita dell’entità. L’entità può contenere altri oggetti, entità oppure value. Essa è responsabile
della coordinazione delle operazioni sugli oggetti di cui è proprietario.
Lezione 4
Value Object
Il value object è immutabile e non ha un identificatore perché è definito dal suo valore che
non può cambiare (per esempio una banconota da 5€ è uguale a tutte le altre banconote da
5€). Può essere utilizzato come un attributo di un'entità e può fare riferimento ad un’entità.
E’ importante impostare dei constraints perché se dobbiamo fare una statistica possiamo
creare un’array statico che possiamo scorrere in tempo lineare, mentre se trattiamo le cose
con oggetti primitivi andiamo ad occupare tutta la memoria.
Aggregate
Gli aggregati sono delle porzioni di modello che non sono scelti a caso ma secondo ragioni
tecniche. Esse rappresentano un’unità. Un aggregate deve avere una radice, ovvero
un’entità all’interno dell’aggregato ed un confine. Solo la radice dell’aggregato può essere
referenziato dall’esterno. La radice può fare riferimento a tutte le entità nel confine
dell’aggregato.
I termini possono avere significati diversi in contesti diversi. Pertanto, è essenziale definire i
"bounded contexts" per stabilire confini chiari tra le diverse parti del sistema e gestire la
comunicazione tra di loro.
Il migliore approccio è capire la comunicazione fra i differenti dipartimenti.
Domande
Casa
Diagrammi snapshot
I diagrammi snapshot rappresentano lo stato interno di un programma in fase di esecuzione.
Ecco perché usiamo diagrammi istantanei:
● Per illustrare concetti come tipi primitivi vs. tipi di oggetto, valori immutabili vs.
riferimenti non riassegnabili, aliasing del puntatore, stack vs. heap, astrazioni vs.
rappresentazioni concrete;
● Per spiegare la progettazione per il progetto team.
Valori primitivi
I valori primitivi sono rappresentati da costanti nude. La freccia in arrivo è un riferimento al
valore di una variabile o di un campo oggetto.
Valori immutabili
Gli oggetti immutabili sono indicati in un diagramma istantaneo da un doppio bordo, al contrario
quelli mutabili.
Casa
Oggetti immutabili in Java
Un oggetto immutabile è un oggetto il cui stato interno rimane costante dopo che è stato
interamente creato. In Java, le variabili sono modificabili per impostazione predefinita, il che
significa che possiamo cambiare il valore che contengono. Utilizzando la parola chiave
finale quando si dichiara una variabile, il compilatore Java non ci consente di modificare il
valore di tale variabile. Si noti che final ci vieta solo di modificare il riferimento contenuto
nella variabile, non ci protegge dal modificare lo stato interno dell'oggetto a cui fa
riferimento.
Poiché lo stato interno di un oggetto immutabile rimane costante nel tempo, possiamo
condividerlo in modo sicuro tra più thread. Gli oggetti immutabili non cambiano il loro stato
interno nel tempo, sono thread-safe e privi di effetti collaterali. A causa di queste proprietà,
gli oggetti immutabili sono particolarmente utili anche quando si ha a che fare con ambienti
multi-thread
Mutabilità e immutabilità
I tipi immutabili sono più sicuri dai bug, più facili da capire e più pronti al cambiamento. La
mutabilità rende più difficile capire cosa sta facendo il tuo programma e molto più difficile far
rispettare i contratti. Questo è un problema fondamentale con le strutture dati mutabili.
Riferimenti multipli allo stesso oggetto mutabile (chiamati anche alias per l'oggetto) possono
significare che più punti nel tuo programma, possibilmente ampiamente separati, fanno
affidamento su quell'oggetto per rimanere coerenti. Per dirla in termini di specifiche, i
contratti non possono più essere applicati in un solo posto, ad esempio tra il client di una
classe e l'implementazione di una classe. I contratti che coinvolgono oggetti mutabili ora
dipendono dal buon comportamento di tutti coloro che hanno un riferimento all'oggetto
mutabile.
arraylist.add("goodbye");
System.out.println(unmodlist);
Immutability
Un oggetto è mutabile se permette cambiamenti, immutabile altrimenti. è preferibile che
rendiamo i nostri oggetti immutabili al fine di prevenire attacchi DOS.
Fail fast
I dati non validi vengono bloccati prima di creare oggetti invalidi. Ogni classe deve definire
una sorta di contratto tra prerequisiti che i dati devono soddisfare per ottenere la
postcondizione, ovvero la costruzione dell’istanza dell’oggetto.
Contracts
Un contratto è un formalità da rispettare al fine di evitare oggetti invalidi con l’obiettivo di
creare il codice più sicuro.
Dobbiamo definire i contracts quando:
● Abbiamo metodi pubblici;
● Per i metodi con visibilità di package se il package è grande ed il metodo è molto
utilizzato;
● Per i metodi privati non abbiamo bisogno dei contratti;
● Per i metodi protetti nemmeno.
Dobbiamo partire da un buon costruttore al fine di evitare di creare invalide istanze.
Validazione
La validazione è una fase dove rendiamo il nostro input valido. Tuttavia l’input validation
dipende dal contesto del modello in cui ci troviamo. Inoltre dobbiamo validare tutti i dati, da
quelli più semplici a quelli più complessi.
Tipi di validazione
Queste validazioni vanno fatte in questo ordine, dal più alto al più basso, perché se facciamo
un controllo semantico su un dato in input molto grande, esso richiede molta computazione,
il che si traduce che il nostro server ha un costo di elaborazione maggiore. Per questo
dobbiamo prima occuparci della size.
● Origine: i dati provengono dal legittimo mittente? Per un attaccante non è così
costoso inviare dati dannosi. Occorre prevenire attacchi DoS e DDos e lo facciamo
controllando gli indirizzi IP. Dobbiamo fare attenzione allo spoofing richiedendo una
chiave di accesso alle nostre API, token o chiavi univoche;
● Size: la size è ragionevole? Dobbiamo capire qual è la nostra ragionevole size ed
essa dipende dal contesto;
● Contesto lessicografico: l’input contiene solo caratteri ammissibili? verificare che i
dati ricevuti siano quelli consentiti
● Sintattico: Il formato dei dati è corretto? controllare se i caratteri sono nel posto
giusto, tramite delle regex;
● Semantico: i dati hanno un senso nel contesto in cui siamo? questo generalmente
richiede molto tempo. Determinare se i dati sono consistenti rispetto allo stato del
sistema.
Lezione 5
Generics Java
Una classe generica viene implementata esattamente come una classe non generica.
L'unica differenza è che contiene una sezione di parametri di tipo. Possono esistere più tipi
di parametro, separati da una virgola. Le classi, che accettano uno o più parametri, sono
note come classi con parametri o tipi con parametri.
Lezione 6
Domain primitives
I "Domain Primitives" sono i value object fondamentali nel progettare un sistema software,
rappresentando concetti chiave all'interno di un dominio specifico.
Nella creazione dei primitivi di dominio, di solito vengono applicate invarianti (regole o
vincoli) per garantire la loro validità e coerenza. Un oggetto dovrebbe esistere soltanto se
esso è valido.
I primitivi di dominio non dovrebbero utilizzare tipi generici o consentire valori nulli. Ciò
assicura che rappresentino accuratamente i valori specifici del dominio ed evita ambiguità.
Ogni concetto all'interno del dominio dovrebbe essere rappresentato in modo adeguato dai
primitivi di dominio in modo da avere un codice chiaro ed organizzato.
Il significato di un primitivo dominio è dipendente dal contesto e potrebbe essere necessario
definirli per allinearlo meglio con gli obiettivi specifici del progetto. Limitare o estendere la
definizione secondo necessità è accettabile.
È consigliabile evitare di rendere le definizioni più permissive, poiché ciò può generare
confusione. È preferibile invece introdurre nuovi termini e utilizzare l'ereditarietà quando
necessario.
Creare una libreria di primitivi di dominio fin dall'inizio, coprendo tutti i termini rilevanti nel
dominio per garantire coerenza e chiarezza.
Nel contesto della definizione di un'API REST, è consigliabile evitare di esporre il modello di
dominio interno. Ciò impedisce che i futuri cambiamenti diventino costosi, poiché tutti gli
utenti dell'API dovrebbero adattarsi a tali cambiamenti.
Gli oggetti da leggere una volta sono progettati per essere letti solo una volta e possono
aiutare a identificare utilizzi inattesi o non autorizzati. Spesso sono costituiti da primitivi di
dominio e impediscono la serializzazione di dati sensibili, l'ereditarietà e le estensioni
Utilizzare i primitivi di dominio per argomenti dei metodi, argomenti dei costruttori, tipi di
ritorno e attributi delle entità offre diversi vantaggi:
● L'input è sempre valido.
● La convalida è coerente.
● Il codice delle entità è più conciso e leggibile.
● Il sovraccarico è minimo rispetto ad altre operazioni del sistema.
È importante creare all’inizio consistenti entità altrimenti maneggiamo entità non consistenti
con la business logic. È buona norma non avere un costruttore vuoto se un’entità ha campi
obbligatori, altrimenti l’entità ha uno stato inconsistente quando andiamo a creare un’istanza
di quell’oggetto.
è meglio annotare i campi invece di fornire getter e setter.
Come gestire entità con molti campi obbligatori? Possiamo raggruppare alcuni campi in
entità. Possiamo utilizzare il pattern fluent interface che consente di restituire la stessa
classe che possiamo richiamare per fare altre operazioni come nell’esempio precedente.
Advanced constraints
Ci sono alcuni campi che possono essere obbligatori se uno manca. Ad esempio un conto
bancario deve avere uno scoperto oppure un account di riserva.
Ora sappiamo creare entità valide, ma come assicurarci che le entità che sono oggetti
mutabili nel Drive-Domain Development, siano validi dopo i cambiamenti?
WRONG WAY
CORRECT WAY
Non dobbiamo condividere gli oggetti mutabili, altrimenti perdiamo il controllo sulla loro
validità. Il modo più sicuro è condividere i domini primitivi.
Se devi ritornare un oggetto mutabile, restituisci una sua copia.
Per quanto riguarda le collezioni, come le liste, non possiamo restituire l’entità perché è
mutabile, potremmo fare una copia ma è costoso ma è meglio codificare la logica aziendale,
come metodi per aggiungere o riordinare. Se vogliamo proprio restituire la lista potremmo
utilizzare un proxy, ma attenzione agli oggetti dentro alla lista, dobbiamo essere sicuri che
siano immutable? è sempre meglio fare una copia approfondita anche se è molto costosa.
Esercizi
Lezione 8
Reducing complexity of state
Controllare gli stati mutabili delle entità è difficile. Abbiamo bisogno di un pattern sicuro per
gestire le transazioni di stati delle entità.
Fino ad ora abbiamo visto metodi che funzionano per il single-thread, questi metodi hanno
problemi detti TOCTOU (Time-to-check to time-to-usage) in quanto possono essere
eseguiti in parallelo da un altro thread dove il check non sarebbe rispettato se la funzione
non soffrisse del problema.
Lezione 10
Handling failures securely
Quando progettiamo un software dobbiamo pensare alla possibilità che gli utenti falliscono.
Le eccezioni devono restituire un messaggio perché e dove c’è stato il problema.
Esistono due tipi di eccezioni che vanno gestite separatamente, le eccezioni della business
logic e le eccezioni tecniche. Le eccezioni vanno gestite separatamente per capire
immediatamente in che tipo di eccezione ci troviamo senza riconoscere il messaggio
stampato.
Se abbiamo un’eccezione business logic sconosciuta, che non dovrebbe esistere, runniamo
un’eccezione tecnica. Le eccezioni non dovrebbero includere dati sensibili.
I fallimenti sono un risultato naturale e atteso di tutto ciò che facciamo, è giusto trattarli come
eccezioni? Dipende se il risultato è eccezionale o meno.
Progettare per l’availability
Non vogliamo che la nostra applicazione o il nostro servizio non siano disponibili, tuttavia
può capitare che la nostra applicazione non sia disponibile oppure non siamo in grado di
soddisfare tutte le richieste. è sempre meglio cercare di informare l’utente che il server è
occupato piuttosto che lasciarlo in attesa.
Questo è lo schema che dovremmo utilizzare per le richieste. Iniziamo con il sistema chiuso
e contiamo le richieste che falliscono. Se ne falliscono troppe mettiamo il sistema aperto.
Dopo un po’ di tempo elaboriamo alcune richieste ed in base al risultato decidiamo se aprire
o chiudere il ciclo.
Lezione 12
TODO
Bla
Lezione 13
TODO
Bla
Lezione 14
TODO
Bla
Lezione 15
TODO
Bla
Lezione 16
TODO
Bla
Lezione 17
TODO
Bla
Lezione 18
TODO
Bla
Lezione 19
TODO
Bla
Lezione 20
TODO
Bla
Lezione 21
TODO
Bla
Lezione 22
TODO
Bla
Lezione 23
TODO
Bla
Lezione 24
TODO
Bla