De toegangscode wordt nooit verstuurd via e-mail.
Stuur deze separaat aan de ontvanger (bijv. via telefoon of chat).
⚠️ Kanaalscheiding — zo werkt het veilig
1. See It Once verstuurt het ID automatisch per e-mail naar de ontvanger
✓ Automatisch via See It Once
2. Stuur de sleutel via een ander kanaal: telefoon, SMS of chat
✓ Bel, app of SMS
3. Stuur de toegangscode eveneens via een apart kanaal naar de ontvanger
✓ Nooit samen met ID of sleutel
✗ Stuur ID, sleutel én toegangscode nooit samen in één bericht
✗ Maakt beveiliging ongedaan
Secret ophalen
Vul het ID in.
Vul de sleutel in.
Vul de toegangscode in.
Het ID staat in de e-mail van de ontvanger. De sleutel en toegangscode ontvangt u separaat van de verzender.
Contact
Heeft u vragen, verbeteringen en/of opmerkingen over See It Once?
Vul dan onderstaand contactformulier in. Wij nemen zo spoedig mogelijk contact met u op.
Vul uw naam in.
Vul een geldig e-mailadres in.
0/1000
Vul een bericht in (minimaal 10 tekens).
Technische werking
Architectuur
See It Once bestaat uit drie Docker-containers die via een intern netwerk communiceren:
een Nginx-frontend die de interface serveert, een
Node.js/Express-backend die de REST API aanbiedt, en
Redis als in-memory datastore met ingebouwde TTL-ondersteuning.
Alle API-verzoeken van de browser lopen via de Nginx-proxy naar de backend —
de browser communiceert nooit rechtstreeks met Node.js.
Twee-staps verificatieflow
Het aanmaken van een secret verloopt via een bevestigde twee-staps flow:
Stap 1 — Aanmaken
De verzender vult naam, e-mailadres, naam en e-mailadres van de ontvanger,
de inhoud en een verplichte toegangscode in. De browser versleutelt het secret
lokaal met PBKDF2 + AES-256-GCM op basis van de toegangscode,
vóórdat er iets naar de server wordt gestuurd. De backend slaat het versleutelde
secret tijdelijk op als pending in Redis (TTL: 1 uur) en stuurt een
bevestigingsmail naar de verzender.
Stap 2 — Bevestiging
De verzender klikt op de bevestigingsknop in de e-mail. De backend verifieert
het eenmalige token, activeert het secret definitief in Redis en stuurt een
notificatiemail naar de ontvanger met uitsluitend het
id — zonder sleutel of toegangscode.
Stap 3 — Ophalen
De ontvanger klikt op de link in de notificatiemail, voert de toegangscode in
die de verzender separaat heeft gecommuniceerd (telefoon, chat) en de
browser ontsleutelt het secret volledig lokaal. Daarna wordt het secret permanent
van de server verwijderd.
Verplichte toegangscode (versleutelingslaag)
Een toegangscode is altijd verplicht. De browser versleutelt het secret vóór
verzending via PBKDF2 (200.000 iteraties, SHA-256) gevolgd door
AES-256-GCM. De toegangscode verlaat de browser nooit
en wordt nergens opgeslagen — ook niet door de server. De ontvanger kan het secret
alleen ontsleutelen met de toegangscode die de verzender separaat communiceert.
Zelfs de server-operator kan de inhoud niet inzien.
Verzendersverificatie via e-mail
Voordat de ontvanger een notificatie krijgt, moet de verzender de actie bevestigen
via een bevestigingslink die naar zijn eigen e-mailadres wordt gestuurd.
Dit voorkomt misbruik: een bot of onbekende bezoeker die het formulier invult kan
de flow niet voltooien zonder toegang tot het opgegeven e-mailaccount.
De bevestigingslink bevat een willekeurig token van 32 bytes en is
1 uur geldig en eenmalig bruikbaar.
Bescherming tegen e-mailscanners (Safe Links)
Beveiligingssoftware zoals Microsoft Defender Safe Links volgt alle links in
inkomende e-mails automatisch — ook vóórdat de gebruiker klikt. Als de bevestiging
direct via een GET-request zou worden uitgevoerd, zou de scanner de token verbruiken
en de gebruiker zou een "verlopen link" zien zonder ooit zelf te hebben geklikt.
Om dit te voorkomen werkt de bevestigingsflow in twee stappen: de link in de e-mail
opent uitsluitend een landingspagina (GET zonder bijwerking).
De daadwerkelijke activatie — opslaan van het secret en versturen van de
notificatiemail — vindt pas plaats na een expliciete klik op de bevestigingsknop
op die pagina, via een POST-request. Scanners volgen geen POSTs,
waardoor de token intact blijft tot de verzender zelf bevestigt.
Notificatiemail naar ontvanger
De notificatiemail aan de ontvanger bevat uitsluitend het id van het
secret — geen sleutel, geen toegangscode. Onderschepping van de
e-mail geeft een aanvaller niets bruikbaars: zonder de toegangscode (die via een
apart kanaal wordt gecommuniceerd) is het secret niet te ontsleutelen.
Atomaire ophaal & verwijdering (GETDEL)
Het ophalen van een secret gebeurt via een atomaire Redis GETDEL-operatie:
ophalen en verwijderen zijn één ondeelbare stap. Dit voorkomt een race condition
waarbij twee gelijktijdige verzoeken hetzelfde secret zouden kunnen ontvangen.
Zodra het secret is opgehaald — ook bij een onjuiste sleutel of toegangscode —
is het permanent verwijderd van de server.
Cleane URL — sleutel nooit in adresbalk
De notificatiemail bevat een link in de vorm /s/{id}. De server-sleutel
en toegangscode staan nooit in de URL: de sleutel wordt via een
versleuteld POST-verzoek (request body) verstuurd, niet als querystring. Na het
ophalen van het secret vervangt de browser de URL automatisch terug naar
/ via history.replaceState, zodat het ID niet
achterblijft in de browserhistorie.
Automatisch verlopen & datapersistentie
Niet-geopende secrets worden automatisch verwijderd door Redis na de ingestelde
vervaltijd (15 minuten tot 7 dagen). De backend luistert via een aparte
subscriber-verbinding op Redis keyspace-events; zodra een secret verloopt,
worden de statistieken direct bijgewerkt.
Redis is geconfigureerd met AOF-persistentie
(appendfsync everysec) en aanvullende RDB-snapshots. Bij een
herstart of update blijven alle nog-niet-verlopen secrets en statistieken
bewaard — maximaal 1 seconde dataverlies bij een onverwachte crash.
Invoervalidatie
Elk API-endpoint valideert inkomende data via Zod-schema's
vóórdat enige businesslogica wordt uitgevoerd. Ontbrekende velden, verkeerde
typen of waarden buiten de toegestane grenzen worden direct afgewezen met een
duidelijke foutmelding. Het id in de URL en de key
in de request body worden gefilterd op toegestane tekens ([a-f0-9])
voordat ze Redis ingaan.
Beveiliging & rate limiting
De backend zet via Helmet automatisch HTTP-beveiligingsheaders
(X-Frame-Options, X-Content-Type-Options,
Referrer-Policy e.a.). Verzoeken worden op drie niveaus
beperkt via express-rate-limit:
· Globaal: max 100 req/min per IP op alle API-routes
· Secret aanmaken: max 20 req/min per IP
· Contactformulier: max 3 req/10 min per IP
De server retourneert bij onbekende routes altijd een JSON-404 en bij
onverwachte fouten een JSON-500 — stack traces worden nooit naar de
client gestuurd.
Wat de server nooit ziet
De server ontvangt nooit de decryptiesleutel, de toegangscode
of de leesbare tekst van het secret. De enige manier om een secret te lezen is
met het juiste ID, de juiste sleutel én de juiste toegangscode — na één gebruik
is het secret permanent verwijderd.
Dataflow samengevat
1 · Aanmaken
Browser versleutelt secret lokaal (PBKDF2 + AES-256-GCM) →
Zod valideert invoer → backend slaat pending op in Redis (1 uur TTL) →
bevestigingsmail naar verzender
2 · Bevestigen
Verzender klikt link → landingspagina getoond (GET, geen activatie) →
verzender klikt bevestigingsknop → POST stuurt token →
secret geactiveerd in Redis met gekozen TTL →
notificatiemail naar ontvanger (alleen id, geen sleutel)
3 · Ophalen
Ontvanger opent site → vult ID, sleutel en toegangscode in →
browser ontsleutelt lokaal → secret getoond →
backend verwijdert secret én persoonsgegevens permanent uit Redis →
ontvangstbevestiging verstuurd naar verzender
Ontvangstbevestiging
Zodra een secret succesvol is opgehaald ontvangt de verzender
automatisch een bevestigingsmail met tijdstip van ophalen en de referentie (indien opgegeven).
Deze melding is niet-fataal: als de bevestigingsmail niet afgeleverd kan worden,
wordt het secret toch correct teruggegeven aan de ontvanger.
Geanonimiseerde audit-log
Bij elk ophaalmoment schrijft de backend een gestructureerde logmelding met:
een afgekorte variant van het secret-ID, de referentie (indien aanwezig),
het tijdstip (ISO 8601), en een gehashte versie van het IP-adres
(SHA-256 met een server-side salt). Het werkelijke IP-adres wordt nooit opgeslagen.
De hash is alleen bruikbaar voor correlatie bij een incident — niet voor identificatie.
Content Security Policy
De backend stuurt via Helmet een strikte Content-Security-Policy
header mee op alle responses. Dit voorkomt dat een eventuele XSS-aanval de
client-side encryptie kan omzeilen door externe scripts te laden of data
naar externe domeinen te sturen. De policy staat uitsluitend eigen scripts,
stijlen en het Google Fonts CDN toe.
Redis TLS
De verbinding tussen de backend en Redis kan worden beveiligd via TLS door
REDIS_TLS=true in te stellen en een rediss:// URL
te gebruiken. Dit is aanbevolen wanneer Redis op een externe host of apart
netwerksegment draait. Binnen een geïsoleerd Docker-netwerk (internal bridge)
is het risico beperkt, maar de optie is beschikbaar voor productie-omgevingen
met hogere eisen.
Wachtwoordgenerator
Genereer een sterk willekeurig wachtwoord om te gebruiken als toegangscode.
—
Wachtwoorden worden lokaal gegenereerd in je browser — er wordt niets naar de server gestuurd.
Release notes
202604.07April 2026
📬 Ontvangstbevestiging: verzender krijgt e-mail zodra secret is opgehaald
📧 Notificatiemail onderwerp bevat nu naam van verzender
📧 Bevestigingsmail: melding inbox/spam toegevoegd
🔒 Wachtwoord verplicht gesteld, pw=1 uit URL verwijderd
202604.02April 2026
📊 Redis-backed statistieken met keyspace expiry listeners
📬 Nodemailer SMTP integratie (Office365)
🛡️ Contactformulier met honeypot en rate limiting
🎨 Hamburger navigatie en card layout frontend
📄 Technische documentatiepagina
Wat vindt AI ervan?
We hebben drie toonaangevende AI-modellen gevraagd om See It Once te beoordelen vanuit
het perspectief van een security officer:
zou je de tool gebruiken voor het veilig delen van vertrouwelijke informatie?
Hieronder de onafhankelijke analyses van Claude, ChatGPT en Gemini.
🟠
Claude (Anthropic)
Sonnet — security review
✓ Ja, met kanttekeningen
Oordeel
See It Once is aantoonbaar beter dan het alternatief: wachtwoorden en credentials via
e-mail of chat sturen. De combinatie van client-side AES-256-GCM encryptie, verplichte
twee-kanaals communicatie (ID via mail, sleutel via apart kanaal) en eenmalig lezen
via atomaire GETDEL maakt dit een solide keuze voor intern gebruik binnen een
organisatie die de server zelf beheert.
Sterk
Client-side encryptie — server ziet nooit de leesbare inhoud
Safe Links-proof bevestigingsflow (GET → landingspagina, POST → activatie)
Sleutel nooit in URL of browserhistorie
Self-hosted: geen afhankelijkheid van externe partijen
Aandachtspunten
Vereist gebruikersdiscipline: ID en sleutel mogen nooit via hetzelfde kanaal
Geen audit trail per secret (wie las het, wanneer, welk IP)
Server-operator heeft toegang tot de versleutelde blob + serversleutel
Geen identiteitsverificatie van de ontvanger
Redis moet goed beveiligd zijn (geen publieke toegang, encryptie at rest)
Conclusie
Geschikt voor: intern delen van tijdelijke credentials, wachtwoorden en API-sleutels
tussen collega's en bekende externe partijen. Niet geschikt voor
situaties waarbij de server-operator zelf niet vertrouwd wordt, of waarbij harde
audit-vereisten gelden (AVG-logging, NEN 7510).
🟢
ChatGPT (OpenAI)
GPT-4o — security review
~ Voorwaardelijk ja
Oordeel
See It Once hanteert een verdedigbare security-architectuur. De keuze voor
client-side encryptie via de Web Crypto API (PBKDF2 + AES-256-GCM) is in lijn
met moderne best practices. Het ontwerp minimaliseert server-side risico's effectief:
zelfs bij een volledig gecompromitteerde Redis-instantie beschikt een aanvaller
alleen over versleutelde data zonder het wachtwoord.
Sterk
Zero-knowledge architectuur voor de inhoud van het secret
Rate limiting op alle endpoints beperkt brute-force risico
Twee-staps verificatie via e-mail voorkomt ongeautoriseerd aanmaken
Risico's
Geen MFA of sessiebeheer voor de gebruiker
Beveiliging staat of valt bij correcte scheiding van kanalen door gebruikers
Geen logging of alerting bij verdachte toegangspatronen
SMTP-transport als potentieel zwakke schakel voor notificaties
Ontbreekt: Content Security Policy headers voor de frontend
Conclusie
Voor een zelfbeheerde, interne implementatie binnen een vertrouwde infrastructuur
is See It Once een acceptabele keuze als vervanging voor onveilige kanalen.
Aanbevolen aanvullingen: CSP-headers instellen, Redis-verbinding versleutelen met TLS,
en een minimale audit-log toevoegen voor compliance-doeleinden.
🔵
Gemini (Google DeepMind)
Gemini 1.5 Pro — security review
~ Voorwaardelijk ja
Oordeel
See It Once pakt een reëel en veelvoorkomend probleem aan: organisaties sturen
dagelijks gevoelige informatie via inherent onveilige kanalen zoals e-mail en
messaging-apps. De tool biedt een structurele verbetering, mits correct ingezet.
De self-hosted opzet is vanuit privacyperspectief een pre — er is geen afhankelijkheid
van derde partijen voor de opslag van gevoelige data.
Sterk
Versleutelde opslag: server slaat nooit plaintext op
Self-hosted: volledige controle over data en infrastructuur
Open codebase maakt onafhankelijke audit mogelijk
Bevestigingsflow beschermt tegen geautomatiseerd misbruik
Risico's
Vertrouwensmodel: server-operator is impliciet vertrouwd
Geen bevestiging aan verzender dat het secret is opgehaald
Gebruikersfout (kanalen mengen) is de grootste kwetsbaarheid
Geen ondersteuning voor bestanden of binaire data
Schaalbaarheidsvraag: single Redis instance als single point of failure
Conclusie
Een pragmatische, goed doordachte tool voor het beoogde doel. De beveiligingsaanpak
is consistent met het dreigingsmodel. Gemini beveelt aan om de tool te begeleiden
met gebruikerstraining over kanaalscheiding, en overweeg een
optionele ontvangstbevestiging toe te voegen voor hoog-risico use cases.
ⓘ De reviews van ChatGPT en Gemini zijn samengesteld op basis van hun publiek gedocumenteerde
security-analyseprincipes en redeneerpatronen, toegepast op de architectuur van See It Once.
De review van Claude is een directe evaluatie. Alle drie de modellen zijn gevraagd dezelfde
vraag te beantwoorden: "Zou je See It Once als security officer gebruiken, en waarom?"
Over See It Once
Het begon met nieuwsgierigheid. Gewoon eens kijken wat er mogelijk is met Docker,
een Redis-database en een zelfgebouwde backend en frontend. Geen groot plan, geen
strak businessmodel — gewoon bouwen, proberen, stukmaken en opnieuw beginnen.
In het begin was het vooral technisch spelen: containers opzetten, services met
elkaar laten communiceren, caching regelen via Redis. Maar al snel ontstond er een
praktisch idee — hoe handig zou het zijn om tijdelijk gevoelige informatie te kunnen
delen? Denk aan tokens of een korte boodschap: iets wat je één keer wilt bekijken
en daarna nooit meer.
En daar zit precies de uitdaging. Want eerlijk is eerlijk: er zijn al genoeg van dit
soort tools. "One-time secret", "burn after reading" — noem het maar op. Maar welke
kun je vertrouwen? Waar blijft je data echt privé? Wat gebeurt er op de achtergrond?
In plaats van te vertrouwen op iets wat ik niet volledig begrijp of kan controleren,
besloot ik het zelf te bouwen. Niet omdat ik dacht het beter te kunnen dan de rest,
maar omdat ik wilde weten hóe het werkt. Volledige
controle over de logica, de opslag, de beveiliging. Geen black box — maar iets waarvan
ik elke regel code ken.
Langzaam groeide het project. AI-ondersteuning kwam erbij — niet als gimmick, maar als
hulpmiddel. Voor het versnellen van ontwikkeling, het verbeteren van logica en het
itereren op features. Het werd een combinatie van experiment en praktijktool.
En ja, het voelt soms nog steeds als "één uit een dozijn". Maar tegelijk ook niet.
Want dit is mijn dozijn. Gebouwd vanuit
nieuwsgierigheid, met aandacht voor veiligheid en transparantie. Geen grote
marketingverhalen — gewoon iets dat werkt.
Het is geen afgerond product, maar een doorlopend experiment. Iets dat blijft groeien,
verbeteren en soms ook weer opnieuw begint. En stiekem hoop ik dat meer mensen het gaan
gebruiken — niet alleen omdat het handig is, maar omdat het laat zien dat je zelf ook
controle kunt nemen over de tools die je dagelijks gebruikt.