We staan vandaag op Product Hunt! Reactie plaatsen

Authenticator 1.16.0 is er. Bekijk wat er nieuw is

Hoe het werkt

Een technisch overzicht voor security-reviewers. Alles hier is te controleren aan de broncode — ook de minder flatteuze punten, die we benoemen in plaats van weglaten.

Architectuur

De extensie is een Manifest V3-Chrome-extensie die TOTP-codes lokaal genereert met OTPAuth. Voor het genereren van codes en het opslaan en versleutelen van accounts is geen backend nodig, en er zijn geen gebruikersaccounts. We draaien één server, api.authenticator.sh, voor het gratis plan en Pro — hieronder beschreven — en die ontvangt nooit een geheim, een accountnaam, een uitgever of een code.

Die server bewaart voor niemands accounts sleutelmateriaal: er is geen key escrow en geen herstelpad dat wij beheren. Vergeet een gebruiker het wachtwoord en raakt de herstelcode kwijt, dan kunnen we niets doen — dat is opzet.

Cryptografie

Wachtwoordbeveiliging is optioneel en staat standaard uit. Met de beveiliging aan:

Versleuteling
AES-256-GCM, willekeurige 96-bits IV per record en per herschrijving
Sleutelafleiding
PBKDF2-HMAC-SHA256, 600.000 iteraties, willekeurige 128-bits salt (OWASP-richtlijn)
Hoofdsleutel
256 bits, uit crypto.getRandomValues, eenmalig gegenereerd; het wachtwoord verpakt hem alleen
Vingerafdrukken
HMAC-SHA256 onder een via HKDF afgeleide subsleutel, om records tussen apparaten samen te voegen
Opslag van de ontgrendelde sleutel
chrome.storage.session — alleen in geheugen, nooit naar schijf geschreven, gewist zodra de browser sluit, onbereikbaar voor content scripts
Automatisch vergrendelen
Bij elke opening / na 5, 15 of 60 minuten inactiviteit / tot de browser sluit
Implementatie
Uitsluitend WebCrypto. Geen enkele primitieve is zelfgebouwd en er wordt geen crypto-bibliotheek meegeleverd.

Waarom twee sleutelniveaus

Gegevens worden nooit rechtstreeks versleuteld met een van het wachtwoord afgeleide sleutel. Een willekeurige hoofdsleutel versleutelt de records; de PBKDF2-uitvoer verpakt alleen die hoofdsleutel. Daar volgen twee dingen uit, en beide doen ertoe:

  • Het wachtwoord wijzigen herschrijft 32 bytes in plaats van elk record, elke back-up en elke export opnieuw te versleutelen. Juist bij zulke massale herschrijvingen verliezen opslagsystemen data.
  • Een 160-bits herstelcode kan dezelfde hoofdsleutel onafhankelijk verpakken, dus een vergeten wachtwoord is te herstellen. De code wordt één keer getoond, moet ter bevestiging worden ingetypt en wordt na elk gebruik vervangen. Er wordt niets naar schijf geschreven voordat de gebruiker hem heeft bevestigd.

Wat wel en wat niet versleuteld is

Met wachtwoordbeveiliging aan wordt het hele accountrecord versleuteld, op de identificator en de vingerafdruk na — inclusief de servicenaam, zodat een gestolen profiel niet verraadt bij welke diensten iemand accounts heeft. Bij het inschakelen versleutelt de extensie de records, controleert ze door te ontsleutelen en te vergelijken en verwijdert pas daarna elke kopie in leesbare vorm: lokale browseropslag, sync en alle zeven roulerende back-upmomentopnamen.

Ronduit gezegd, omdat het meeweegt in uw beoordeling:

  • Met wachtwoordbeveiliging uit worden accounts onversleuteld opgeslagen en, als Chrome Sync aanstaat, gerepliceerd via het eigen Google-account van de gebruiker. Ze bereiken ons nooit, maar Google heeft ze wel. Sync kan worden uitgezet, wat ook verwijdert wat er al staat.
  • De gebruiksgeschiedenis per site — welk account op welk domein wordt gebruikt — staat lokaal opgeslagen en valt tijdens het verzamelen niet onder de kluis. Wachtwoordbeveiliging inschakelen wist die geschiedenis; ze kan ook los worden uitgezet.

Dreigingsmodel

Beschermt tegen

  • Een gestolen of gekopieerde browserprofielmap
  • Infostealer-malware die browsergegevens en masse wegsluist
  • Iemand met fysieke toegang tot een onbeheerde machine
  • Accountrecords die via Chrome Sync bij Google terechtkomen
  • Een verloren of gestolen back-upbestand (met wachtwoord beveiligde export)

Beschermt niet tegen

  • Malware die als de gebruiker draait terwijl de kluis ontgrendeld is
  • Een keylogger die het wachtwoord tijdens het typen opvangt
  • Een gecompromitteerde browser of een gecompromitteerd besturingssysteem
  • Compromittering van de externe diensten waarvoor de codes bedoeld zijn
  • Een onversleutelde export die je zelf hebt gemaakt — platte JSON, otpauth://-links of CXF zijn leesbaar voor iedereen die het bestand opent

Kortom: dit beschermt gegevens in rust. Het verdedigt geen apparaat dat tijdens gebruik al in handen van een aanvaller is — dat kan het ook niet.

Permissies

Het manifest declareert vijf permissies en geen host-permissies:

storage
Accounts en instellingen in lokale opslag, de ontgrendelde sleutel in sessieopslag en de optionele synckopie
activeTab
Leest de hostnaam van het actieve tabblad om het bijbehorende account te markeren, en legt het tabblad vast wanneer de gebruiker een QR-code van het scherm scant
contextMenus
Voegt het item “2FA-code invoegen” toe aan het rechtsklikmenu, en alleen op bewerkbare velden op http(s)-pagina's
scripting
Injecteert de ene functie die de code in het veld zet — voor één tabblad en één aanroep, pas nadat dat menu-item is gekozen
sidePanel
Opent de app in Chromes zijpaneel wanneer dat de gekozen weergave is; het geeft geen toegang tot enige pagina

Er zijn Er zijn geen content scripts, geen tabs-, cookies- of webRequest-permissies, en geen web-accessible resources. Niets van ons bestaat in een pagina totdat je daar om een code vraagt; wat dan draait wordt geïnjecteerd voor dat ene tabblad en die ene aanroep, onder de toestemming die je eigen klik geeft, en laat niets achter.

Camera

QR-scannen met de camera draait op een extensiepagina in een tabblad, niet in de popup — een popup wordt vernietigd zodra hij focus verliest, en dat is precies wat een permissieverzoek doet. Hiervoor is geen permissie in het manifest nodig: het gebruikt het standaard cameraverzoek van de browser, verleend per extensie-origin en intrekbaar in de site-instellingen, en het verschijnt pas als de gebruiker de scanner opent. Video wordt lokaal gedecodeerd en nooit verzonden of opgeslagen.

Gratis plan, Pro en de dagelijkse check-in

Sinds 1.14.0 maakt de extensie één keer per dag contact met api.authenticator.sh — bij het starten van de browser of nadat de pop-up is weergegeven, zodat het tonen van een code er nooit op wacht. Het verzoek is een gesloten lijst velden, opgebouwd in één functie in src/billing/sync.ts en door een test aan die lijst gehouden:

iid
Een willekeurige id die deze installatie zelf aanmaakt. Niet gekoppeld aan een Google-account, een e-mailadres of de browser — tenzij de gebruiker Pro koopt: dan koppelt de aankoopregistratie de installatie waarvandaan gekocht is, en elke installatie waarop de licentiesleutel actief is, aan het e-mailadres waarmee betaald is
ver, ui_lang, browser_lang
Versie van de extensie, taal van de interface, taal van de browser
installed_at, legacy, legacy_src
Wanneer de installatie is ontstaan, en of ze dateert van vóór de invoering van het betaalde plan
accounts, groups
Hoeveel accounts ze bevat en in hoeveel groepen — aantallen, nooit welke accounts of hoe de groepen heten
vault, sync, open_mode, view_mode, style, dark, os
Of wachtwoordbeveiliging en sync aanstaan, en hoe de extensie opent, hoe de accountlijst wordt getoond (normaal, compact of met verborgen codes), en welke stijl ze gebruikt, licht of donker, en het besturingssysteem (Windows, macOS, Linux, ChromeOS)
pid, lid, has_key, key_hint
Welk ondertekend beleid en welke licentie de installatie op dit moment heeft, of ze de licentiesleutel al heeft, en de laatste vier tekens van die sleutel
opens
Hoe vaak de extensie op elke dag sinds de vorige check-in is geopend (hooguit de laatste 14 dagen) — een aantal per dag, niets over wat er is bekeken
events
Betaalgebeurtenissen uit een gesloten lijst, in een wachtrij: de limiet is bereikt, de paywall is getoond of gesloten, er is een plan gekozen, er is een betaalpagina geopend of een sleutel geprobeerd

Nooit verzonden: geheimen, accountnamen, uitgevers, codes, of de sites waarop ze worden gebruikt. De server leidt het land af uit het verzoek, aan de edge, en slaat het IP-adres niet op. De feedbackpagina bij het verwijderen krijgt dezelfde installatie-id mee als ?i=: de pagina meldt aan de server dat de installatie is verwijderd en koppelt er een optioneel antwoord op de enquête aan.

Hiervoor bewaard in chrome.storage.local: de installatie-id, het laatste ondertekende beleid en de laatste licentie, de licentiesleutel die deze installatie heeft (ingetypt, of ontvangen na een aankoop op deze installatie), het aantal keer geopend per dag tot de server dat heeft, en de gebeurtenissen in de wachtrij — geen accountgegevens. Zolang sync aanstaat, gaan er ook twee dingen naar Chrome Sync: of de installatie dateert van vóór het betaalde plan, en die licentiesleutel — zodat de andere Chrome-profielen van de gebruiker ze overnemen en Pro vanzelf aanzetten. Sync uitzetten haalt beide weer uit Chrome Sync; sync weer aanzetten zet ze terug.

Wat er terugkomt, en hoe de extensie daarmee omgaat:

Tokens
Het beleid (de accountlimiet van het gratis plan) en de licentie (Pro) zijn JWS, ondertekend met ES256 (ECDSA P-256)
Verificatie
WebCrypto, tegen de publieke sleutels die in de build zijn meegecompileerd (src/billing/keys.ts). Een build voor de Chrome Web Store vertrouwt alleen de productiesleutel, en de buildcontrole weigert een build die een andere sleutel bevat
Offline
De limiet en de licentie worden lokaal gecontroleerd, aan de hand van de laatste ondertekende tokens. Zolang er nog geen ondertekend beleid is ontvangen, is niets beperkt — een onbereikbare server kan nooit iemand buitensluiten
Wat de limiet kan
Voorkomen dat een account wordt toegevoegd, en verder niets. Hij kan geen account verbergen, vergrendelen, verwijderen of wijzigen, en geen codes, export, back-ups, herstel of sync blokkeren
Aankopen
Stripe Checkout in een nieuw tabblad, met Link als officiële verkoper (merchant of record). Het e-mailadres van de koper bereikt ons via Stripe, om de licentiesleutel uit te geven en te herstellen; sleutels worden als hash opgeslagen, plus een versleutelde kopie zodat een verloren sleutel opnieuw kan worden verstuurd
Licentiesleutel
Het antwoord op de check-in bevat de sleutel zelf alleen voor de installatie waarop Pro is gekocht, en alleen tot de vier tekens die ze in key_hint meldt bij die sleutel passen — zo vervangt een later gekochte sleutel een oudere; een installatie waarin de sleutel is ingetypt, krijgt er nooit een toegestuurd

De limiet is geen DRM: de code is open source, en een fork kan hem verwijderen. Wat nooit mogelijk mag zijn, is dat iemand de licentie van een ander bemachtigt of gebruikt, of de installatie van een ander beperkt maakt — dat is een kwetsbaarheid, en valt binnen de scope van ons beveiligingsbeleid.

Uitgaand netwerkverkeer

Twee soorten verzoeken doet de extensie uit zichzelf: de klokcontrole en de dagelijkse check-in voor het gratis plan en Pro. Al het andere hieronder gebeurt doordat de gebruiker ergens op klikte. Manifest V3 verbiedt externe code, en niets hiervan draagt accounts, geheime sleutels of gegenereerde codes mee. De volledige lijst:

HostWanneerBevat gebruikersgegevens?
time.akamai.com
timeapi.io
cloudflare.com
Controle op klokafwijking, gecachet; is geen enkele bron bereikbaar, dan meldt Instellingen dat. Bij een afwijkende klok werkt TOTP niet meer.Nee
authenticator.shWelkomstpagina bij installatie; feedbackpagina bij verwijderen, met de installatie-id in het adres; hulp- en supportpagina's wanneer ze vanuit de pop-up worden geopend.De installatie-id, alleen op de feedbackpagina bij verwijderen; verder nee (gewone webserverlogs)
api.authenticator.shDe check-in, één keer per dag; aanroepen voor afrekenen, licentie en klantenportaal wanneer de gebruiker Pro koopt, een sleutel invoert, een aankoop herstelt of een abonnement beheert.De velden die hierboven staan — nooit accountgegevens. Een licentiesleutel of een e-mailadres alleen als de gebruiker er een intypt
checkout.stripe.com
billing.stripe.com
Stripe Checkout en het klantenportaal, in een tabblad geopend wanneer de gebruiker Pro koopt of een abonnement beheert.Wat de koper daar invult. De eigen voorwaarden van Stripe en Link zijn van toepassing
chromewebstore.google.comStorepagina, alleen bij een klik in een tabblad geopend: het beoordelingsformulier vanuit de beoordelingsvraag.Nee (voor Google is het een bezoek aan een storepagina zoals elk ander)

Er worden geen lettertypen, scripts, stylesheets of afbeeldingen van hosts van derden geladen. Alles wat nodig is om de interface te tonen zit in het pakket.

Controleren wat we publiceren

U hoeft niets van het bovenstaande op ons woord aan te nemen. De extensie is open source en elke release is reproduceerbaar:

  1. Download de gepubliceerde .crx uit de Chrome Web Store en pak hem uit
  2. Check de bijbehorende git-tag uit
  3. Draai npm ci && npm run build op Node 20 LTS
  4. Vergelijk de resulterende dist/ met het uitgepakte pakket

Bij elke GitHub-release publiceren we een SHA-256 van elk bestand in de geproduceerde dist/, als SHA256SUMS-v<version>.txt en in sha256sum-formaat, zodat stap 4 neerkomt op één keer sha256sum -c draaien. Verschillen zouden beperkt moeten blijven tot de bestandsvolgorde binnen archieven en witruimte in geminificeerde uitvoer tussen Node-patchversies.

De runtime-afhankelijkheden zijn bewust beperkt — OTPAuth, React, jsQR, Zustand, Framer Motion en Lucide — vastgezet door een ingecheckt lockfile. Een CycloneDX-SBOM is op verzoek beschikbaar.

Bekijk de broncode

Wat we niet hebben

We zijn een kleine onafhankelijke leverancier. Is een van de volgende punten een harde eis in uw proces, dan kunt u dat beter nu weten dan drie weken later in de beoordeling:

  • Geen SOC 2-, ISO 27001- of vergelijkbare certificering
  • Tot op heden geen externe pentest laten uitvoeren
  • Geen betaald bug-bountyprogramma

Wat we er wel tegenover zetten: volledige transparantie van de broncode, een verifieerbare build, een minimaal permissieoppervlak en een server die uw geheimen, accountnamen of codes überhaupt nooit te zien krijgt.

Contact

Kwetsbaarheidsmeldingen en security-vragen: security@authenticator.sh. Ons openbaarmakingsbeleid, de scope, reactietijden en safe-harbourvoorwaarden staan op de pagina beveiligingsbeleid; wat er wordt opgeslagen en wat het apparaat verlaat, staat in het privacybeleid.

We vullen graag een schriftelijke leveranciersvragenlijst in of lopen de code door met een securityteam.

Voor alles wat geen security-kwestie is — installatie, accountherstel, factuurvragen bij een uitrol — zie support. Stuur daar alstublieft geen kwetsbaarheidsmeldingen naartoe; die mailbox wordt niet als vertrouwelijk behandeld.