Beveiliging · Diepgaand
De volledige cryptografische levenscyclus
Een nauwkeurige rondleiding langs wat er bij elke stap met je gegevens gebeurt, met de primitieven, parameters en vertrouwensgrenzen uitgeschreven. Elke stap begint met een samenvatting in gewone taal, zodat je zo diep kunt lezen als je wilt.
Stap 01 · Versleutelen
Je geheim wordt op je apparaat verzegeld
Simpel gezegd: het versleutelen gebeurt in je browser, voordat er iets wordt geüpload. Wat wij ontvangen is al onleesbaar.
Als je op Versleutelen drukt, haalt de browser een nieuwe 256-bit inhoudssleutel uit de WebCrypto CSPRNG en versleutelt je gegevens met AES-256-GCM. GCM is geauthenticeerd: elk blok heeft een 128-bit tag, zodat later knoeien wordt gedetecteerd in plaats van stilletjes te worden ontsleuteld.
Tekst en bestanden worden precies hetzelfde behandeld: beide worden één ondoorzichtige binaire blob, opgevuld tot een standaardgrootte, zodat een kort wachtwoord en een klein bestand er hetzelfde uitzien. De leesbare tekst komt nooit op het netwerk en verlaat nooit het geheugen van het tabblad.
Stap 02 · Sleutelbeheer
De sleutel zit in de link, niet op onze servers
Simpel gezegd: het deel van de link na de # bevat de sleutel, en browsers sturen dat deel nooit naar een server.
Je link bevat een willekeurige 256-bit seed in het fragment van de URL: alles na de #. Volgens de URL-specificatie haalt de browser het fragment eraf en wordt het nooit meegestuurd in het verzoek aan onze servers. Uit die seed leidt de browser met HKDF een opzoekwaarde af om het geheim te vinden, plus de sleutel die de inhoudssleutel ontsluit. De server ziet alleen ooit een hash van de opzoekwaarde.
De browser van de ontvanger leest de seed uit het fragment en doet dezelfde afleiding lokaal. Dat is wat „zero-knowledge” hier betekent: wie alleen de versleutelde data heeft, kan er zonder de link niets mee.
Stap 03 · Optionele wachtwoordzin
Een tweede slot dat alleen mensen hebben
Simpel gezegd: voeg een wachtwoordzin toe en zelfs iemand met de volledige link kan het geheim zonder die niet openen.
Stel je een wachtwoordzin in, dan wordt die opgerekt met Argon2id, een geheugenintensieve functie voor sleutelafleiding, en samen met de seed gecombineerd tot de sleutel die de inhoudssleutel inpakt. De wachtwoordzin zelf wordt nooit verstuurd of opgeslagen; alleen jij en de ontvanger kennen hem. Verkeerde pogingen worden in de browser van de ontvanger opnieuw geprobeerd op gegevens die die al heeft, dus de server kan ze niet tellen.
Door de geheugenintensiviteit wordt grootschalig offline raden duur, zelfs met GPU's en ASIC's. Deel de wachtwoordzin via een ander kanaal dan de link, dan komen de twee factoren nooit op één plek samen. De Argon2-module wordt bij elk aanmaken en tonen opgehaald, zodat het netwerk niet kan zien welke geheimen er een gebruiken.
Stap 04 · Transport & opslag
Alleen versleutelde data verlaat je apparaat
Simpel gezegd: we bewaren een afgesloten doos en een vervaltijd. Verder niets: geen leesbare tekst, geen sleutel, geen gegevens over jou.
De blob wordt geüpload via TLS en opgeslagen als versleutelde data, naast gehashte toegangsgegevens, de levensduur en een afgeronde vervaltijd. We bewaren bewust geen identiteit van de afzender, ontvanger, IP-log, aanmaaktijd of toegangsgeschiedenis. Er is geen accountsysteem om een geheim aan een persoon te koppelen. Snelheidsbeperking werkt met een HMAC van het adres met een salt die regelmatig wisselt, zodat niets van vóór een wissel te koppelen is aan iets daarna.
Omdat het opgeslagen object zonder de seed uit het fragment betekenisloos is, levert een gehackte server of een gerechtelijk bevel alleen ondoorzichtige bytes en hashes op die niet terug te rekenen zijn naar iets bruikbaars.
Stap 05 · Ophalen & vernietigen
Wie het eerst leest, krijgt het, daarna wordt het gewist
Simpel gezegd: een eenmalige link openen levert de doos één keer af en verwijdert de kopie op de server. Of vernietig het zelf, wanneer je maar wilt.
Er wordt niets opgehaald als de pagina om het geheim te tonen laadt: het verzoek gaat pas de deur uit bij een echte klik of toetsaanslag, zodat linkscanners en chatvoorbeelden een geheim niet kunnen opgebruiken. Bij een eenmalig geheim claimt de server de rij in één voorwaardelijke update (precies één verzoek wint) en vanaf dat moment geldt het geheim als gebruikt. Dezelfde browser kan 15 minuten lang opnieuw downloaden als de verbinding wegvalt; niemand anders kan dat, en een onbevestigde claim wordt vernietigd, nooit teruggedraaid. Geheimen met een tijdslimiet zijn gewoon leesbaar tot ze verlopen.
Elk geheim heeft ook een privé-vernietigingslink. Die openen vernietigt op zich nog niets; pas na bevestiging wordt de versleutelde data direct gewist, voordat een ontvanger het kan lezen.
Dreigingsmodel
Wat shhnote wel en niet kan zien
De eerlijke versie. Hier ligt precies de vertrouwensgrens.
Verlaat je apparaat nooit
Wat wij niet kunnen zien
Je leesbare tekst · de inhoudssleutel en de seed · je wachtwoordzin · of er een wachtwoordzin is ingesteld · wie een geheim heeft aangemaakt of geopend · wanneer het is aangemaakt · toegangslogs op IP-niveau. Niets daarvan wordt verstuurd of bewaard.
Opgeslagen op onze servers
Wat we wel hebben
Een versleutelde blob die we niet kunnen openen · de opgevulde grootteklasse · eenrichtingshashes van de waarden die je browser gebruikt om het te vinden of te vernietigen · of het eenmalig is of een tijdslimiet heeft · een afgeronde vervaltijd. Allemaal nutteloos zonder de seed in je link.
Goed om te weten
De eerlijke kanttekeningen
Iedereen met de volledige link (en de wachtwoordzin, als die is ingesteld) kan een eenmalig geheim lezen: behandel de link als het geheim zelf. Nadat een geheim is getoond, overschrijft de pagina wat ze kan als je weggaat, maar JavaScript kan dat niet garanderen; sluit het tabblad als je klaar bent. Een geautomatiseerd aangestuurde browser die echte invoer simuleert, kan een eenmalig geheim openen zoals een mens dat zou doen; de ontvanger vindt dan niets, en dat is de eerlijke uitkomst. Een geheim verwijderen haalt het record weg; versleutelde restanten kunnen achterblijven in journals, vrije schijfruimte of back-ups tot ze worden overschreven, als versleutelde data zonder sleutel. Grote geheimen die buiten de database worden opgeslagen, dragen het tijdstip waarop het bestand is geschreven. En zoals bij elke webapp vertrouw je de code die naar je browser wordt gestuurd: daarom is shhnote open source en zelf te hosten.
Cryptografische specificatie
Elke primitief op één plek
Versleuteling
Geauthenticeerde symmetrische versleuteling in blokken van 64 KiB. Een 256-bit sleutel plus een integriteitstag per blok, zodat knoeien of inkorten wordt gedetecteerd en niet alleen onleesbaar blijft.
Sleutelafleiding
Sleutels en identificatoren worden met HKDF-SHA-256 uit de seed afgeleid. Optionele wachtwoordzinnen worden opgerekt met een geheugenintensieve KDF, waardoor offline brute-force traag en duur wordt, zelfs op speciale hardware.
Willekeur
Seeds, sleutels, salts en nonces komen uit de cryptografisch veilige generator van de browser. Nooit Math.random(), nooit voorspelbaar.
Sleutelbeheer
De seed staat na de # in je link. Browsers versturen het fragment nooit, dus het bereikt onze servers nooit.
Onderweg
Alleen versleutelde data verlaat je apparaat, via moderne TLS. Wat wij ontvangen is een ondoorzichtige blob die we niet kunnen openen.
Opgeslagen
We bewaren een versleutelde blob, eenrichtingshashes van de toegangsgegevens en een afgeronde vervaltijd: geen leesbare tekst, geen sleutels, geen metadata over afzender of ontvanger.
Technische vragen
Voor de beveiligingsbewuste lezer
Hoe wordt de eenmaligheid afgedwongen bij gelijktijdige verzoeken?
Het tonen voert één voorwaardelijke update uit die de rij van „actief” naar „geclaimd” zet. Twee gelijktijdige verzoeken kunnen niet allebei slagen; het tweede ziet een record dat al geclaimd is en krijgt hetzelfde antwoord „geen geheim gevonden” als iedereen. De claim wordt nooit teruggedraaid: gaat de download verloren, dan downloadt de houder van het claimtoken opnieuw binnen het tijdvenster, en anders wordt het geheim ongelezen vernietigd.
Waarom het URL-fragment en niet een queryparameter?
Queryparameters worden naar de server gestuurd en komen terecht in toegangslogs, proxy's en analysetools. Het fragment (na de #) blijft volgens de URL-specificatie aan de kant van de client: de browser zet het nooit in het HTTP-verzoek. Op die eigenschap rust het hele zero-knowledge-ontwerp. Na het tonen haalt de pagina het fragment bovendien uit de adresbalk en de geschiedenis.
Wat houdt linkscanners tegen om eenmalige geheimen voortijdig op te branden?
De weergavepagina is voor elk geheim hetzelfde statische document en haalt bij het laden niets op. Het verzoek wordt pas verstuurd bij een echte klik of toetsaanslag; een gescripte click() of formulierverzending telt niet. Geautomatiseerde scanners (e-mailbeveiliging, linkvoorbeelden in chats, virusscanners) laden alleen die statische schil.
Kan ik dit controleren of zelf hosten?
Ja. De volledige broncode is openbaar onder AGPLv3, het protocol is vastgelegd in een specificatie met conformiteitsvectoren waar elke implementatie zichzelf tegen kan toetsen, en er is een handleiding om zelf te hosten. Je kunt de cryptografie aan de clientkant controleren en je eigen instantie draaien, zodat de vertrouwensgrens helemaal bij jou ligt.
Wordt er iets gelogd om misbruik te voorkomen?
We houden geen toegangslogs per geheim en geen IP-geschiedenis bij. Snelheidsbeperking gebruikt een gesleutelde hash van het adres met een salt die regelmatig wisselt en daarbij alle tellers wist. Misbruikmeldingen worden per dag geteld; er wordt niet bijgehouden welk geheim is gemeld.
Tips om veilig te delen
- Stuur de wachtwoordzin op een andere manier dan de link, bijvoorbeeld de link per e-mail en de wachtwoordzin via de telefoon.
- Kies de kortste vervaltijd die voor jou werkt.
- Gebruik een eenmalig geheim voor wachtwoorden en andere informatie die precies één keer gelezen moet worden.
Een kwetsbaarheid melden
Denk je een beveiligingsprobleem met deze site te hebben gevonden? Mail dan naar [email protected] en laat het ons vertrouwelijk weten voordat je het aan iemand anders vertelt. Beschrijf wat je hebt gevonden en hoe het te reproduceren is.
Wij zullen:
- binnen 5 werkdagen reageren;
- je op de hoogte houden terwijl we het oplossen;
- je vermelden wanneer we de oplossing publiceren, als je dat wilt.
Ga tijdens het testen niet aan de gegevens van anderen, verstoor de dienst niet en voer geen geautomatiseerde scans uit die hem zwaar belasten.
Kwetsbaarheden in de shhnote-software zelf: zie het beveiligingsbeleid van het project.
Probeer het: het kost tien seconden
Schrijf iets geheims, krijg je eenmalige link en zie het verdwijnen.