Bezpečnost — technický detail
Aktualizováno: 7. května 2026
Hledáte verzi srozumitelnou pro běžného uživatele? Přečtěte si Jak chráníme vaše údaje. Tato stránka je její implementačně věrný protějšek — psaná pro vývojáře, bezpečnostní výzkumníky a partnery dělající due diligence. Je to věcný popis primitiv, která máme nasazená v produkci.
I. Identitní data — Bank iD a Stripe Identity
K ověření, že naši profesionálové mohou legálně pracovat, používáme dva externí poskytovatele KYC. Žádný z nich neukládá citlivá identitní data trvale na našich serverech.
Bank iD (OIDC). Když si pracovník zvolí ověření přes Bank iD, jeho banka podepíše JWT obsahující pouze ověřená pole (jméno, datum narození, adresa, číslo dokladu) — nikdy přístupové údaje k bance. Token verifikujeme oproti veřejnému JWKS Bank iD (env BANKID_JWKS_URL) a strukturovaný výsledek persistujeme do worker_identity_verifications. Surový token po ověření zahodíme.
Stripe Identity. Když si pracovník zvolí naskenování dokladu, jeho zařízení nahrává obrázky přímo do Stripe — fyzické snímky pasu / OP nikdy neprocházejí naší backendovou infrastrukturou. Stripe nám pošle webhook s verified | requires_input | canceled a strukturovanými poli (jméno, datum narození, číslo dokladu) přes náš jediný KYC endpoint v worker-identity.service.ts. Snímky zůstávají u Stripu pod jejich retencí.
Audit a retence. Každá změna stavu se zapisuje do worker_identity_audit_events (modul worker-identity-audit). Strukturovaná data o ověření držíme po dobu zákonem požadovanou pro pracovněprávní vztah; surové webhook payloady a pending-verification sezení čistíme po 60 dnech.
II. Tokeny a relace
Při přihlášení klient dostane dva tokeny: krátkodobý JWT access token (15 minut) a opakovaně použitelný refresh token (14 dní klouzavě, 365 dní absolutně).
Refresh token je opaque. 64 znaků hexadecimálního náhodného řetězce — nikoli JWT. Nelze ho "rozšifrovat" mimo náš backend. Při každém použití je rotován: starý token se okamžitě označí jako spotřebovaný, vystaví se nový.
Detekce zneužití přes family_id. Každý refresh token nese family_id, které identifikuje rodičovskou autorizaci (např. původní přihlášení z konkrétního zařízení). Pokud je spotřebovaný token z téže rodiny předložen znovu — kdekoli ve světě — revokujeme celou rodinu okamžitě a všechny aktivní relace odvozené z této autorizace se odhlásí. To detekuje krádež cookies / replay útoky během sekund. Implementace: apps/api/src/modules/auth/, tabulka refresh_tokens.
Aktivní zařízení. Na /account/security/sessions (web) a /sessions (mobile) uživatel vidí všechny aktivní relace, jejich poslední aktivitu a zařízení / lokalitu (z User-Agent + aktuální IP — historické IP se neperzistují). Relace lze revokovat individuálně nebo hromadně; revokace zneplatní rodinu, ne jen jeden token.
III. Soubory a dokumenty
Smlouvy, profilové fotky, doklady k zakázkám — vše, co uživatelé do FixIt nahrají, ukládáme na vlastním MinIO clusteru (S3-kompatibilní), nikoli ve veřejně přístupné CDN.
Databáze nikdy neukládá URL. Persistujeme pouze object key (např. users/abc123/avatar.jpg). URL skládáme až při response v AssetUrlService (apps/api/src/modules/storage/asset-url.service.ts) — každý URL má krátkou platnost a storage providery lze měnit bez rewrites databáze.
Třístupňová přístupová kontrola (apps/api/src/modules/storage/storage.controller.ts):
- Public assets (loga firem, profilové fotky veřejných řemeslníků) — bez autorizace, ale vystavené jako signed URL s krátkou expirací.
- Authenticated assets (smlouvy, doklady k zakázkám) — JWT validace + ověření vlastnictví předtím, než vystavíme signed URL.
- Tenant-scoped assets (interní firemní dokumenty) — kontrolujeme i
X-Active-Company-Idheader, takže admin firmy A nemá přístup k souborům firmy B.
IV. Šifrování v přenosu i v klidu
V přenosu. TLS 1.3 (povinně), HSTS s preload subdomén, CSP s nonce. Konfigurace na nginx reverse proxy s Cloudflare jako WAF. Veškerý provoz mezi mobilní/web aplikací a API je šifrován mezi klientem a naším edge.
V klidu. PostgreSQL 16 s šifrováním na úrovni disku (LUKS na hostiteli). Zálohy databáze jsou šifrovány samostatným klíčem a uloženy mimo databázový container — viz bun run db:backup:dev / db:backup:staging / db:backup:prod, které volají on-VPS backup skript.
Cross-tenant scope safety. Veřejné dotazy na tabulku companies procházejí přes excludePlatformCompany() (apps/api/src/common/scopes/exclude-platform-company.scope.ts) — interní FIXIT_PLATFORM shell employer tak nemůže prosáknout do veřejných listingů, vyhledávání ani Elasticsearch indexace.
Vstupy uživatele. Volný text procházejí stripHtmlTags() sanitizací (apps/api/src/common/utils/sanitize.utils.ts) předtím, než ho uložíme — žádný regex, jen ověřený parser. Podezřelé HTML a nulové bajty zachytí globální SanitizeNullBytesInterceptor. URL zadané uživatelem (např. odkaz na portfolio) procházejí SSRF guard (@fixit/url-security) předtím, než si je server stáhne.
V. GDPR a kontakty
FixIt App s.r.o. je správcem osobních údajů podle článku 4 GDPR. Naše Zásady ochrany osobních údajů popisují právní rámec a vaše práva.
- Kontakt na ochranu údajů: legal@fixit.app
- Dozorový úřad: Úřad pro ochranu osobních údajů — Pplk. Sochora 27, 170 00 Praha 7
Hlášení bezpečnostních incidentů. Pokud objevíte zranitelnost, ozvěte se nám na security@fixit.app. Reagujeme do 48 hodin. Nezveřejňujte zranitelnost veřejně před opravou — postupujeme v duchu coordinated disclosure.
Tato stránka je věcný popis. Pokud najdete nesoulad mezi tím, co je tu popsáno, a tím, co skutečně děláme, je to bug — pošlete to na security@fixit.app a opravíme to.