Qual è la differenza tra localStorage, sessionStorage, session e cookies?


533

Quali sono i pro e i contro tecnici di localStorage, sessionStorage, session e cookies e quando dovrei usarne uno rispetto all'altro?


2
Anche questo è un argomento correlato utile per dare un'occhiata: HTML5 Archiviazione locale vs Archiviazione sessione ( stackoverflow.com/questions/5523140/… )
Sarin JS

2
Si noti inoltre che i cookie di sessione rimangono attivi fino a quando il browser WINDOW è aperto (non la scheda in cui sono stati impostati) MA sessionStorage viene annullato non appena si chiude la scheda ...
yar1

Sì, la sessione è anche un tipo di cookie. La caratteristica è che è transitorio dove il cookie è persistenza
Faris Rayhan,

@ yar1 Una particolare finestra del browser è un elemento dell'interfaccia utente irrilevante.
curiousguy,

Risposte:


719

Questa è una domanda estremamente ampia e molti dei pro / contro saranno contestuali alla situazione.

In tutti i casi, questi meccanismi di archiviazione saranno specifici per un singolo browser su un singolo computer / dispositivo. Qualsiasi requisito per l'archiviazione costante dei dati tra le sessioni dovrà coinvolgere il lato server delle applicazioni, molto probabilmente utilizzando un database, ma probabilmente XML o un file di testo / CSV.

localStorage, sessionStorage e cookie sono tutte soluzioni di archiviazione client. I dati della sessione sono conservati sul server dove rimangono sotto il tuo controllo diretto.

localStorage e sessionStorage

localStorage e sessionStorage sono API relativamente nuove (il che significa che non tutti i browser legacy le supporteranno) e sono quasi identiche (sia nelle API che nelle funzionalità) con la sola eccezione della persistenza. sessionStorage (come suggerisce il nome) è disponibile solo per la durata della sessione del browser (e viene eliminato quando la scheda o la finestra viene chiusa) - tuttavia, sopravvive ai ricarichi di pagine ( guida all'archiviazione DOM di origine - Mozilla Developer Network ).

Chiaramente, se i dati che si stanno archiviando devono essere disponibili su base continuativa, localStorage è preferibile a sessionStorage - anche se si dovrebbe notare che entrambi possono essere cancellati dall'utente, quindi non si deve fare affidamento sulla continua esistenza di dati in entrambi i casi.

localStorage e sessionStorage sono perfetti per il persistere dei dati non sensibili necessari all'interno degli script client tra le pagine (ad esempio: preferenze, punteggi nei giochi). I dati archiviati in localStorage e sessionStorage possono essere facilmente letti o modificati all'interno del client / browser, quindi non è necessario fare affidamento per la memorizzazione di dati sensibili o relativi alla sicurezza all'interno delle applicazioni.

Biscotti

Questo vale anche per i cookie, questi possono essere banalmente manomessi dall'utente e i dati possono anche essere letti da loro in testo semplice - quindi se si desidera archiviare dati sensibili, la sessione è davvero l'unica opzione. Se non si utilizza SSL, le informazioni sui cookie possono anche essere intercettate in transito, soprattutto su un wifi aperto.

Sul lato positivo, ai cookie può essere applicato un certo grado di protezione da rischi per la sicurezza come Cross-Site Scripting (XSS) / iniezione di script impostando un flag solo HTTP che significa che i browser moderni (di supporto) impediranno l'accesso ai cookie e ai valori da JavaScript ( questo impedirà anche al tuo JavaScript legittimo di accedervi). Ciò è particolarmente importante con i cookie di autenticazione, che vengono utilizzati per memorizzare un token contenente i dettagli dell'utente che ha effettuato l'accesso - se si dispone di una copia di quel cookie, a tutti gli effetti si diventa quell'utente per quanto riguarda l'applicazione Web interessato e hanno lo stesso accesso ai dati e alle funzionalità dell'utente.

Poiché i cookie vengono utilizzati a fini di autenticazione e persistenza dei dati dell'utente, tutti i cookie validi per una pagina vengono inviati dal browser al server per ogni richiesta allo stesso dominio. Ciò include la richiesta di pagina originale, eventuali richieste Ajax successive, tutte le immagini, fogli di stile, script e caratteri. Per questo motivo, i cookie non devono essere utilizzati per memorizzare grandi quantità di informazioni. Il browser può anche imporre limiti sulla dimensione delle informazioni che possono essere archiviate nei cookie. In genere i cookie vengono utilizzati per memorizzare token identificativi per il monitoraggio di autenticazione, sessione e pubblicità. I token non sono in genere informazioni leggibili dall'uomo in sé e per sé, ma identificatori crittografati collegati all'applicazione o al database.

localStorage vs. sessionStorage vs. Cookies

In termini di funzionalità, cookie, sessionStorage e localStorage consentono solo di archiviare stringhe: è possibile convertire implicitamente i valori primitivi durante l'impostazione (questi dovranno essere riconvertiti per usarli come tipo dopo la lettura) ma non Oggetti o Array (è possibile serializzarli JSON per memorizzarli utilizzando le API). L'archiviazione della sessione generalmente ti consentirà di archiviare qualsiasi primitiva o oggetto supportato dal tuo linguaggio / framework lato server.

Lato client vs. lato server

Poiché HTTP è un protocollo senza stato - le applicazioni Web non hanno modo di identificare un utente da precedenti visite al ritorno al sito Web - i dati della sessione di solito si basano su un token cookie per identificare l'utente per le visite ripetute (anche se raramente si possono usare parametri URL per lo stesso scopo). I dati di solito hanno un tempo di scadenza scorrevole (rinnovato ogni volta che l'utente visita) e, a seconda del server / quadro, i dati verranno archiviati durante il processo (il che significa che andranno persi se il server Web si arresta in modo anomalo o viene riavviato) o esternamente in un server di stato o un database. Ciò è necessario anche quando si utilizza una web farm (più di un server per un determinato sito Web).

Poiché i dati della sessione sono completamente controllati dall'applicazione (lato server), è il posto migliore per qualsiasi cosa sensibile o sicura in natura.

L'ovvio svantaggio dei dati sul lato server è la scalabilità: le risorse del server sono necessarie per ciascun utente per la durata della sessione e tutti i dati necessari sul lato client devono essere inviati con ogni richiesta. Poiché il server non ha modo di sapere se un utente accede a un altro sito o chiude il proprio browser, i dati della sessione devono scadere dopo un determinato tempo per evitare che tutte le risorse del server vengano assorbite da sessioni abbandonate. Quando si utilizzano i dati di sessione, pertanto, è necessario essere consapevoli della possibilità che i dati siano scaduti e persi, soprattutto nelle pagine con moduli lunghi. Si perderà anche se l'utente elimina i propri cookie o cambia browser / dispositivo.

Alcuni framework / sviluppatori Web utilizzano input HTML nascosti per conservare i dati da una pagina di un modulo a un'altra per evitare la scadenza della sessione.

localStorage, sessionStorage e cookie sono tutti soggetti alle regole della "stessa origine", il che significa che i browser dovrebbero impedire l'accesso ai dati tranne il dominio che imposta le informazioni per iniziare.

Per ulteriori informazioni sulle tecnologie di archiviazione client, consultare Dive Into Html 5 .


34
Attenzione: sessionStorage, localStorage non sono appropriati per le informazioni di autenticazione. Non vengono inviati automaticamente al server. Ciò significa che se un utente modifica manualmente l'URL o fa clic su collegamenti HTML, non otterrai informazioni di autenticazione. Anche se si riscrivono i collegamenti HTML, si è costretti a passare le informazioni di autenticazione sull'URL, che è un no-no di sicurezza. Alla fine della giornata, sarai costretto a utilizzare i cookie. Vedere stackoverflow.com/q/26556749/14731 per un argomento correlato.
Gili,

23
Verrà sessionStorageeliminato alla chiusura della finestra o alla scheda?
trysis

34
SessionStorage verrà eliminato alla chiusura della scheda.
rcarrillopadron,

10
@Gili perché passare le informazioni di autenticazione sull'URL è l'unica opzione se non si utilizzano i cookie? Perché non passarlo in un'intestazione HTTP?
yby,

21
@Gili Hai ragione di dire che non si invia automaticamente, ma non è corretto dire che non è appropriato. Uso localStorage e sessionStorage in molte diverse applicazioni di produzione che ho scoperto per i miei clienti e non ho avuto una vulnerabilità dovuta all'affidamento a localStorage / sessionStorage associato all'invio dell'ID e di un token nelle intestazioni. Meno carico sul server anche. Inoltre associo un evento al ricaricamento della pagina e agli hook di caricamento dell'applicazione per chiedere al mio backend se questo utente è ancora autenticato. Funziona alla grande. Buona autenticazione! EDIT: un token CSRF con tutto ciò che aggiunge ancora più sicurezza.
NodeDad,

74
  1. Memoria locale

    Pro :

    1. L'archiviazione Web può essere vista in modo semplice come un miglioramento dei cookie, fornendo una capacità di archiviazione molto maggiore. Se osservi il codice sorgente di Mozilla, possiamo vedere che 5120 KB ( 5 MB che equivalgono a 2,5 milioni di caratteri su Chrome) è la dimensione di archiviazione predefinita per un intero dominio. Questo ti dà molto più spazio su cui lavorare rispetto a un tipico cookie 4KB.
    2. I dati non vengono rinviati al server per ogni richiesta HTTP (HTML, immagini, JavaScript, CSS, ecc.), Riducendo la quantità di traffico tra client e server.
    3. I dati memorizzati in localStorage persistono fino a quando non vengono esplicitamente eliminati. Le modifiche apportate vengono salvate e disponibili per tutte le visite attuali e future al sito.

    Contro :

    1. Funziona sulla stessa politica di origine . Pertanto, i dati memorizzati saranno disponibili solo sulla stessa origine.
  2. Biscotti

    Professionisti:

    1. Rispetto ad altri, non c'è nulla di AFAIK.

    Contro:

    1. Il limite 4K è per l'intero cookie, inclusi nome, valore, data di scadenza, ecc. Per supportare la maggior parte dei browser, mantenere il nome sotto i 4000 byte e la dimensione complessiva del cookie sotto i 4093 byte.
    2. I dati vengono rinviati al server per ogni richiesta HTTP (HTML, immagini, JavaScript, CSS, ecc.), Aumentando la quantità di traffico tra client e server.

      In genere, sono consentiti:

      • 300 biscotti in totale
      • 4096 byte per cookie
      • 20 cookie per dominio
      • 81920 byte per dominio (dati 20 cookie di dimensione massima 4096 = 81920 byte.)
  3. sessionStorage

    Professionisti:

    1. È simile a localStorage.
    2. I dati non sono persistenti, ovvero i dati sono disponibili solo per finestra (o scheda nei browser come Chrome e Firefox). I dati sono disponibili solo durante la sessione di pagina. Le modifiche apportate vengono salvate e disponibili per la pagina corrente, nonché future visite al sito nella stessa finestra. Una volta chiusa la finestra, la memoria viene eliminata.

    Contro:

    1. I dati sono disponibili solo all'interno della finestra / scheda in cui sono stati impostati.
    2. Ad esempio localStorage, funziona sulla stessa politica di origine . Pertanto, i dati memorizzati saranno disponibili solo sulla stessa origine.

Verifica tra le schede : come facilitare la comunicazione tra le schede del browser di origine incrociata.


13
Cookie : " I dati vengono rinviati al server per ogni requisito HTTP ". In alcuni casi d'uso (come nel processo di autenticazione) anche questo può essere considerato un vantaggio. sessionStorage : " Le modifiche sono disponibili solo per finestra (o scheda nei browser come Chrome e Firefox) ". Penso che sia meglio formularlo " Le modifiche sono disponibili solo durante la sessione di pagina ". Una sessione di pagina dura fino a quando il browser è aperto e sopravvive a ricariche e ripristini di pagine (da MDN: developer.mozilla.org/en/docs/Web/API/Window/sessionStorage )
Deniz,

Aggiornato! Grazie @DenizToprak
softvar

1
@softvar: sessionStorage - Con 2 .: "I dati non sono persistenti, cioè andranno persi una volta chiusa la finestra / scheda." - Questo non è sicuramente un difetto. Direi che è un vantaggio. Dopotutto è lo spazio di "sessione". È progettato per funzionare in questo modo.
Devstructor il

@devstructor Sì, hai ragione. L'ho pensato in termini di archiviazione locale di alcuni dati. Ho aggiornato la risposta. Grazie per la segnalazione.
softvar,

57

OK, LocalStorage come viene chiamato memoria locale per i tuoi browser, può risparmiare fino a 10 MB , SessionStorage fa lo stesso, ma come dice il nome, è basato sulla sessione e verrà eliminato dopo aver chiuso il browser, inoltre può risparmiare meno di LocalStorage, come fino a 5 MB , ma i cookie sono una minuscola memorizzazione dei dati nel tuo browser, che possono salvare fino a 4KB e sono accessibili tramite server o browser sia ...

Ho anche creato l'immagine qui sotto per mostrare le differenze a colpo d'occhio:

LocalStorage, SessionStorage e Cookies


25

Queste sono proprietà dell'oggetto 'finestra' in JavaScript, proprio come il documento fa parte di una proprietà dell'oggetto finestra che contiene oggetti DOM.

La proprietà Archiviazione sessione mantiene un'area di archiviazione separata per ciascuna origine disponibile per la durata della sessione della pagina, ad esempio fino a quando il browser è aperto, inclusi ricariche e ripristini di pagine.

L'archiviazione locale fa la stessa cosa, ma persiste anche quando il browser viene chiuso e riaperto.

È possibile impostare e recuperare i dati memorizzati come segue:

sessionStorage.setItem('key', 'value');

var data = sessionStorage.getItem('key');

Allo stesso modo per localStorage.


10
Solo per aggiungere - sessionStorageanche per una nuova scheda è una nuova finestra. Pertanto, qualsiasi cosa memorizzata per un dominio specifico in una scheda non sarà disponibile per lo stesso dominio nella scheda successiva.
RBT

5

Archiviazione locale: mantiene i dati delle informazioni dell'utente conservati senza data di scadenza che non verranno eliminati quando l'utente chiuderà le finestre del browser e sarà disponibile per giorno, settimana, mese e anno.

Nella memoria locale è possibile archiviare dati offline da 5-10 MB.

//Set the value in a local storage object
localStorage.setItem('name', myName);

//Get the value from storage object
localStorage.getItem('name');

//Delete the value from local storage object
localStorage.removeItem(name);//Delete specifice obeject from local storege
localStorage.clear();//Delete all from local storege

Archiviazione sessione: è uguale alla data di archiviazione locale tranne per l'eliminazione di tutte le finestre quando le finestre del browser vengono chiuse da un utente Web.

Nella sessione di archiviazione è possibile memorizzare fino a 5 mb di dati

//set the value to a object in session storege
sessionStorage.myNameInSession = "Krishna";

Sessione : una sessione è una variabile globale memorizzata sul server. A ogni sessione viene assegnato un ID univoco che viene utilizzato per recuperare i valori memorizzati.

Cookie : i cookie sono dati, memorizzati in piccoli file di testo come coppie nome-valore, sul tuo computer. Una volta impostato un cookie, tutte le richieste di pagina che seguono restituiscono il nome e il valore del cookie.


2

L'API di archiviazione Web fornisce meccanismi tramite i quali i browser possono archiviare in modo sicuro coppie chiave / valore, in modo molto più intuitivo rispetto all'utilizzo dei cookie. L' API di archiviazione Web estende l' Windowoggetto con due nuove proprietà - Window.sessionStoragee Window.localStorage. - invocando uno di questi verrà creata un'istanza dell'oggetto Storage, attraverso la quale è possibile impostare, recuperare e rimuovere gli elementi di dati. Un oggetto di archiviazione diverso viene utilizzato per sessionStoragee localStorageper ogni origine (dominio).

Gli oggetti di archiviazione sono semplici archivi di valori-chiave , simili agli oggetti, ma rimangono intatti durante il caricamento della pagina .

localStorage.colorSetting = '#a4509b';
localStorage['colorSetting'] = '#a4509b';
localStorage.setItem('colorSetting', '#a4509b');

Le chiavi e i valori sono sempre stringhe . Per memorizzare qualsiasi tipoconvert it to Stringe quindi memorizzarlo. Si consiglia sempre di utilizzareStorage interfacemetodi.

var testObject = { 'one': 1, 'two': 2, 'three': 3 };
// Put the object into storage
localStorage.setItem('testObject', JSON.stringify(testObject));
// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');
console.log('Converting String to Object: ', JSON.parse(retrievedObject));

I due meccanismi all'interno di Web Storage sono i seguenti:

  • sessionStorage mantiene un'area di archiviazione separata per ogni origine. Politica della stessa origine che è disponibile per la durata della sessione della pagina (a condizione che il browser sia aperto, inclusi ricariche e ripristini di pagine).
  • localStorage fa la stessa cosa, ma persiste anche quando il browser viene chiuso e riaperto.

Archiviazione « L'archiviazione locale scrive i dati sul disco, mentre l'archiviazione della sessione scrive i dati solo nella memoria. Tutti i dati scritti nella memoria della sessione vengono eliminati quando l'app viene chiusa.

Lo spazio di archiviazione massimo disponibile è diverso per browser , ma la maggior parte dei browser ha implementato almeno il limite di archiviazione massimo consigliato di w3c di 5 MB .

+----------------+--------+---------+-----------+--------+
|                | Chrome | Firefox | Safari    |  IE    |
+----------------+--------+---------+-----------+--------+
| LocalStorage   | 10MB   | 10MB    | 5MB       | 10MB   |
+----------------+--------+---------+-----------+--------+
| SessionStorage | 10MB   | 10MB    | Unlimited | 10MB   |
+----------------+--------+---------+-----------+--------+

Rileva sempre la sicurezza di LocalStorage e gli errori di quota superati

StorageEvent «L'evento di archiviazione viene generato sull'oggetto Window di un documento quando viene modificata un'area di archiviazione. Quando un agente utente deve inviare una notifica di archiviazione per un documento, l'agente utente deve mettere in coda un'attività per generare un evento denominato archiviazione nell'oggetto Window dell'oggetto Document, utilizzando StorageEvent.

Nota: per un esempio reale, vedere Demo di archiviazione Web . controlla il codice sorgente

Ascolta l'evento di archiviazione su dom / Window per rilevare le modifiche nella memoria. violino .


Cookie (cookie web, cookie del browser) I cookie sono dati, memorizzati in piccoli file di testo come coppie nome-valore, sul tuo computer.

Accesso JavaScript tramite Document.cookie

È inoltre possibile creare nuovi cookie tramite JavaScript utilizzando la proprietà Document.cookie e, se il flag HttpOnly non è impostato, è possibile accedere ai cookie esistenti anche da JavaScript.

document.cookie = "yummy_cookie=choco"; 
document.cookie = "tasty_cookie=strawberry"; 
console.log(document.cookie); 
// logs "yummy_cookie=choco; tasty_cookie=strawberry"

Meccanismo di gestione dello stato HTTP dei cookie sicuri e di HttpOnly

I cookie vengono spesso utilizzati nell'applicazione Web per identificare un utente e la sua sessione autenticata

Quando riceve una richiesta HTTP, un server può inviare un'intestazione Set-Cookie con la risposta. Il cookie viene generalmente memorizzato dal browser, quindi viene inviato con le richieste fatte allo stesso server all'interno di un'intestazione HTTP del cookie.

Set-Cookie: <cookie-name>=<cookie-value> 
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>

I cookie di sessione verranno rimossi all'arresto del client. Non specificano le direttive Scadenza o Max-Age.

Set-Cookie: sessionid=38afes7a8; HttpOnly; Path=/

I cookie permanenti scadono a una data specifica (Scade) o dopo un determinato periodo di tempo (Max-Age).

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

L'intestazione della richiesta HTTP Cookie contiene i cookie HTTP memorizzati precedentemente inviati dal server con l'intestazione Set-Cookie. I cookie solo HTTP non sono accessibili tramite JavaScript tramite la proprietà Document.cookie, le API XMLHttpRequest e Request per mitigare gli attacchi contro gli script tra siti (XSS).

I cookie vengono utilizzati principalmente per tre scopi:

  • Gestione della sessione «Login, carrelli della spesa, punteggi di gioco o qualsiasi altra cosa che il server dovrebbe ricordare
  • Personalizzazione «Preferenze dell'utente, temi e altre impostazioni
  • Tracciamento (registrazione e analisi del comportamento dell'utente) «Ai cookie è associato un dominio. Se questo dominio è uguale al dominio della pagina in cui ti trovi, si dice che i cookie sono cookie proprietari. Se il dominio è diverso, si dice che sia un cookie di terze parti. Mentre i cookie proprietari vengono inviati solo al server che li imposta, una pagina Web può contenere immagini o altri componenti memorizzati su server in altri domini (come banner pubblicitari). I cookie inviati tramite questi componenti di terze parti sono chiamati cookie di terze parti e vengono utilizzati principalmente per la pubblicità e il tracciamento sul Web.

I cookie sono stati inventati per risolvere il problema "come ricordare le informazioni sull'utente":

  • Quando un utente visita una pagina Web, il suo nome può essere memorizzato in un cookie.
  • La prossima volta che l'utente visita la pagina, i cookie appartenenti alla pagina vengono aggiunti alla richiesta. In questo modo il server ottiene i dati necessari per "ricordare" le informazioni sugli utenti.

Esempio GitHubGist


Come sintesi,

  • localStorage persiste su diverse schede o finestre, e anche se chiudiamo il browser, di conseguenza con la politica di sicurezza del dominio e le scelte dell'utente sul limite di quota.
  • L'oggetto sessionStorage non persiste se chiudiamo la scheda (contesto di navigazione di livello superiore) in quanto non esiste se navighiamo attraverso un'altra scheda o finestra.
  • L'archiviazione Web (sessione, locale) ci consente di salvare una grande quantità di coppie chiave / valore e un sacco di testo, cosa impossibile da fare tramite i cookie.
  • I cookie utilizzati per azioni sensibili dovrebbero avere solo una durata breve.
  • Cookie utilizzati principalmente per la pubblicità e il tracciamento sul Web. Vedi ad esempio i tipi di cookie utilizzati da Google .
  • I cookie vengono inviati con ogni richiesta, quindi possono peggiorare le prestazioni (soprattutto per le connessioni dati mobili). Le API moderne per l'archiviazione client sono l'API di archiviazione Web (localStorage e sessionStorage) e IndexedDB.

2

LocalStorage :

  • L'archiviazione Web può essere vista in modo semplicistico come un miglioramento dei cookie, fornendo una capacità di archiviazione molto maggiore. La dimensione disponibile è di 5 MB, che offre uno spazio notevolmente maggiore rispetto a un tipico cookie 4KB.

  • I dati non vengono rinviati al server per ogni richiesta HTTP (HTML, immagini, JavaScript, CSS, ecc.), Riducendo la quantità di traffico tra client e server.

  • I dati memorizzati in localStorage persistono fino a quando non vengono esplicitamente eliminati. Le modifiche apportate vengono salvate e disponibili per tutte le visite attuali e future al sito.

  • Funziona sulla stessa politica di origine. Pertanto, i dati memorizzati saranno disponibili solo sulla stessa origine.

Biscotti:

  • Possiamo impostare il tempo di scadenza per ciascun cookie

  • Il limite 4K è per l'intero cookie, inclusi nome, valore, data di scadenza, ecc. Per supportare la maggior parte dei browser, mantenere il nome sotto i 4000 byte e la dimensione complessiva del cookie sotto i 4093 byte.

  • I dati vengono rinviati al server per ogni richiesta HTTP (HTML, immagini, JavaScript, CSS, ecc.), Aumentando la quantità di traffico tra client e server.

sessionStorage:

  • È simile a localStorage.
  • Le modifiche sono disponibili solo per finestra (o scheda nei browser come Chrome e Firefox). Le modifiche apportate vengono salvate e disponibili per la pagina corrente, nonché future visite al sito nella stessa finestra. Una volta chiusa la finestra, la memoria viene eliminata. I dati sono disponibili solo all'interno della finestra / scheda in cui è stata impostata.

  • I dati non sono persistenti, cioè andranno persi una volta chiusa la finestra / scheda. Come localStorage, funziona sulla stessa politica di origine. Pertanto, i dati memorizzati saranno disponibili solo sulla stessa origine.


0

ecco una recensione veloce e con una comprensione semplice e veloce

inserisci qui la descrizione dell'immagine

dall'insegnante Beau Carnes di Freecodecamp

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.