[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"technologies":3,"\u002Fblog\u002Fssl-certificate-pinning-flutter":167,"team-members":2147,"blog-topics":2489,"mdc--a70y1b-key":2530,"glossary":2649},[4,10,15,21,26,32,38,43,48,53,58,63,68,73,77,82,87,92,97,103,108,113,118,123,128,133,138,143,147,152,157,162],{"title":5,"description":6,"slug":7,"category":8,"icon":9},"Android","Elke app die we uitbrengen bereikt Android — platform-API's, uitvoering op de achtergrond, permissies en de winkeleisen die daarbij horen.","android","framework","logos:android-icon",{"title":11,"description":12,"slug":13,"category":8,"icon":14},"Angular","Het front-endframework waarin we werken als een product al Angular is — een kant-en-klare structuur die past bij grote, langlevende applicaties.","angular","logos:angular-icon",{"title":16,"description":17,"slug":18,"category":19,"icon":20},"Dart","De taal waarin elke Flutter-app die we uitbrengen geschreven is — sound null safety, echte pattern matching, en een compiler voor native en web.","dart","language","logos:dart",{"title":22,"description":23,"slug":24,"category":8,"icon":25},"Django","Het Python-framework dat we pakken als een product op dag één een beheeromgeving, authenticatie en een echt datamodel nodig heeft.","django","logos:django-icon",{"title":27,"description":28,"slug":29,"category":30,"icon":31},"Docker","Elke service die we bouwen verschijnt als container, zodat wat op een laptop, in CI en in productie draait één artefact is en niet drie verschillende.","docker","infrastructure","logos:docker-icon",{"title":33,"description":34,"slug":35,"category":36,"icon":37},"Fastlane","De releaseautomatisering achter ons mobiele werk — ondertekenen, builds en winkeluploads draaien vanuit CI, niet vanaf één laptop.","fastlane","tools","logos:fastlane",{"title":39,"description":40,"slug":41,"category":8,"icon":42},"Flutter","Onze belangrijkste mobiele stack sinds 2018 — één Dart-codebase die uitkomt op iOS, Android, web en desktop zonder een apart team per platform.","flutter","logos:flutter",{"title":44,"description":45,"slug":46,"category":36,"icon":47},"Git","Elk project dat we aanraken leeft in Git — beoordeelde pull requests, CI op elke branch, en een historie die maanden later nog te volgen is.","git","logos:git-icon",{"title":49,"description":50,"slug":51,"category":19,"icon":52},"Go","Onze backendtaal voor realtime-API's en services onder belasting — kleine binaries, snelle builds, en gelijktijdigheid die leesbaar blijft.","go","logos:gopher",{"title":54,"description":55,"slug":56,"category":30,"icon":57},"Google Cloud","De cloud waarop onze productieworkloads draaien — beheerde Kubernetes, opslag en netwerken, zonder handgebouwde servers die niemand wil onderhouden.","gcp","logos:google-cloud",{"title":59,"description":60,"slug":61,"category":36,"icon":62},"Gradle","Het buildsysteem waar elke Android-release doorheen gaat — product flavours, ondertekeningsconfiguraties, en de afhankelijkheden onder een Flutter-app.","gradle","logos:gradle",{"title":64,"description":65,"slug":66,"category":30,"icon":67},"Helm","Hoe we een Kubernetes-uitrol verpakken — service, configuratie, secrets en ingress als één versiebeheerde eenheid die je kunt promoveren en terugdraaien.","helm","logos:helm",{"title":69,"description":70,"slug":71,"category":19,"icon":72},"Kotlin","Waar we naar grijpen als een Flutter-app echt Android eronder nodig heeft — platform channels, achtergrondwerk en integraties met native SDK's.","kotlin","logos:kotlin-icon",{"title":74,"description":75,"slug":76,"category":8,"icon":72},"Kotlin Multiplatform","Bedrijfslogica delen tussen iOS en Android terwijl elk platform zijn eigen native UI houdt — het alternatief wanneer Flutter niet past.","kmp",{"title":78,"description":79,"slug":80,"category":30,"icon":81},"Kubernetes","Hoe we services in productie draaien — Helm-uitrollen, rollouts die terug te draaien zijn, en schalen zonder dat iemand om 3 uur 's nachts wakker hoeft te zijn.","kubernetes","logos:kubernetes",{"title":83,"description":84,"slug":85,"category":30,"icon":86},"NATS","Lichtgewicht berichtenverkeer tussen services — publish\u002Fsubscribe en request\u002Freply zonder het beheergewicht van een volledig brokercluster.","nats","logos:nats-icon",{"title":88,"description":89,"slug":90,"category":8,"icon":91},"Nuxt","Vue met serverrendering, routering en SEO al geregeld — hoe we marketingsites en webapps bouwen die bij het eerste laden snel en vindbaar moeten zijn.","nuxt","logos:nuxt-icon",{"title":93,"description":94,"slug":95,"category":19,"icon":96},"PHP","Waar we met een bestaande PHP-backend werken — hem uitbreiden, ermee integreren, en de mobiele en webclients bouwen die hij moet bedienen.","php","logos:php",{"title":98,"description":99,"slug":100,"category":101,"icon":102},"PostgreSQL","Onze standaarddatabase — waar we naar grijpen tenzij een product een specifieke reden geeft om dat niet te doen, van schema tot indexen.","postgres","database","logos:postgresql",{"title":104,"description":105,"slug":106,"category":19,"icon":107},"Python","Onze taal voor backends, datawerk en AI-integraties — inclusief de Python-bindings die we bij onze eigen Rust-tooling leveren.","python","logos:python",{"title":109,"description":110,"slug":111,"category":8,"icon":112},"React","De front-endbibliotheek waarin we werken als een product al React is — componenten, hooks, en het ecosysteem dat eromheen is gegroeid.","react","logos:react",{"title":114,"description":115,"slug":116,"category":101,"icon":117},"Redis","Waar we data neerzetten die snel moet zijn en opnieuw op te bouwen valt — caches, sessies, verzoeklimieten en achtergrondwachtrijen.","redis","logos:redis",{"title":119,"description":120,"slug":121,"category":19,"icon":122},"Ruby","De taal waarin onze mobiele releaseautomatisering geschreven is — Fastlane-lanes, eigen acties, en de CI-lijm die builds naar de winkels brengt.","ruby","logos:ruby",{"title":124,"description":125,"slug":126,"category":19,"icon":127},"Rust","Waar we heen gaan als prestaties én correctheid allebei tellen — documenten renderen, CLI-tooling, en services die snel en voorspelbaar moeten blijven.","rust","simple-icons:rust",{"title":129,"description":130,"slug":131,"category":101,"icon":132},"SQLite","De database die meereist in de app — lokale caches, offline-first opslag, en alles wat ook zonder netwerk moet blijven werken.","sqlite","logos:sqlite",{"title":134,"description":135,"slug":136,"category":8,"icon":137},"Strapi","Een headless CMS waar we naar grijpen als redacteuren de content moeten beheren — een echte beheeromgeving en een schone API, zonder ze zelf te bouwen.","strapi","logos:strapi-icon",{"title":139,"description":140,"slug":141,"category":19,"icon":142},"Swift","Waar we naar grijpen als een Flutter-app echt iOS eronder nodig heeft — platform channels, integraties met native SDK's, widgets en App Clips.","swift","logos:swift",{"title":144,"description":145,"slug":146,"category":8,"icon":142},"SwiftUI","Het UI-framework van Apple, waar we de native oppervlakken bouwen die een Flutter-app niet kan afdekken — widgets, App Clips, extensies en systeemintegraties.","swiftui",{"title":148,"description":149,"slug":150,"category":8,"icon":151},"Tailwind CSS","Hoe we elke front-end die we bouwen vormgeven — utility-klassen en designtokens in plaats van een stylesheet die alleen maar groeit en nooit wordt opgeschoond.","tailwind","logos:tailwindcss-icon",{"title":153,"description":154,"slug":155,"category":19,"icon":156},"TypeScript","De standaard voor alles wat we voor de browser en voor Node-services schrijven — types die integratiebugs vangen voordat ze een review halen.","typescript","logos:typescript-icon",{"title":158,"description":159,"slug":160,"category":8,"icon":161},"Vue","Ons front-endframework voor beheerpanelen, dashboards voor winkeliers en webapps die jaren onderhoudbaar moeten blijven, geen sprints.","vue","logos:vue",{"title":163,"description":164,"slug":165,"category":36,"icon":166},"Whisper","Het opensource spraakherkenningsmodel van OpenAI — de transcriptiemotor achter spraakinvoer, draaiend als backendservice én op het toestel in de app.","whisper","simple-icons:openai",{"id":168,"title":169,"author":170,"body":171,"description":2128,"extension":2129,"hero":2130,"meta":2133,"metaDescription":2134,"metaTitle":2135,"navigation":2136,"ogDescription":2137,"path":2138,"seo":2139,"slug":2140,"stem":2141,"summary":2142,"timestamp":2143,"topic":2144,"updated":2145,"__hash__":2146},"blog_nl\u002Fblog\u002Fssl-certificate-pinning-flutter.md","Je SSL-pinning werkt waarschijnlijk niet","nixan",{"type":172,"value":173,"toc":2106},"minimark",[174,183,189,196,199,204,259,263,269,281,284,290,296,299,304,314,317,320,323,327,338,406,412,416,423,430,438,442,448,465,468,472,475,540,547,558,561,567,570,573,579,583,587,590,597,600,723,726,776,783,787,794,797,993,996,1244,1251,1255,1266,1350,1353,1360,1364,1376,1391,1398,1402,1409,1424,1434,1437,1449,1453,1456,1461,1483,1495,1506,1517,1520,1541,1547,1752,1770,1784,1801,1821,1840,1855,1858,1882,1886,1889,1892,1899,1905,1909,1912,1915,1918,1921,1925,1928,2062,2066,2083,2086,2102],[175,176,177,178,182],"p",{},"De eigen Android-documentatie van Google zegt dat certificate pinning ",[179,180,181],"strong",{},"\"niet wordt aanbevolen voor Android-apps\"",".",[175,184,185,186,182],{},"Apple zegt ",[179,187,188],{},"\"in de meeste gevallen is pinning niet nodig en kun je het beter vermijden\"",[175,190,191,192,195],{},"En hier is een Chromium-engineer die een vraag beantwoordt over hoe je pinning fatsoenlijk opzet. Hij begint zijn antwoord zo: ",[179,193,194],{},"\"Hoewel pinning in het algemeen nooit aangemoedigd zou moeten worden, omdat het de veiligheid en stabiliteit van het internet als geheel actief schaadt, is de Network Security Config van Android de aangewezen aanpak als je op een private CA pint.\""," Hij vertelt je letterlijk hoe het moet, en hij kan het nog steeds niet laten.",[175,197,198],{},"Ondertussen zit pinning in ongeveer de helft van de mobiele apps die we bekijken, inclusief de apps die voor een audit op ons bureau belanden. Goed nieuws: zit je op Flutter, dan werkt het waarschijnlijk niet. Slecht nieuws: dezelfde zin.",[200,201,203],"h2",{"id":202},"kernpunten","Kernpunten",[205,206,207,214,220,226,232,238,253],"ul",{},[208,209,210,213],"li",{},[179,211,212],{},"Pinning is een smalle maatregel tegen een dreiging die is gekrompen",", met een schadestraal ter grootte van je hele gebruikersbestand. Certificate Transparency, CAA-records en de wijziging in de vertrouwensopslag van Android 7 hebben het grootste deel van zijn oorspronkelijke werk weggenomen.",[208,215,216,219],{},[179,217,218],{},"Het is werkelijk noodzakelijk voor financiën, zorg en overheid"," — apps die tegen MAS-L2 van OWASP moeten worden getoetst, het profiel met verdediging in de diepte voor software die gevoelige gegevens verwerkt. Voor alle anderen zeggen Google, Apple, Cloudflare en de helft van OWASP: niet doen.",[208,221,222,225],{},[179,223,224],{},"De levensduur van certificaten stort in",": 200 dagen sinds maart 2026, 100 in 2027, 47 in 2029. Pin je een blad en kun je niet wisselen zonder een appversie uit te brengen, dan zijn dat acht gedwongen releases per jaar.",[208,227,228,231],{},[179,229,230],{},"Pin de sleutel (SPKI), nooit het certificaat",", en lever altijd een reservepin mee voor een offline sleutel die je zelf beheert.",[208,233,234,237],{},[179,235,236],{},"Lever nooit direct naar harde afwijzing"," — en bouw je eigen faaltelemetrie, want geen van beide platforms heeft er een.",[208,239,240,252],{},[179,241,242,243,247,248,251],{},"Op Flutter doet ",[244,245,246],"code",{},"\u003Cpin-set>"," in ",[244,249,250],{},"network_security_config.xml"," niets voor Dart-verkeer."," Dart draait zijn eigen TLS-stack. De bug staat sinds januari 2022 open.",[208,254,255,258],{},[179,256,257],{},"De test van 30 minuten"," aan het eind van dit artikel vertelt je welke van deze gevallen op jouw app slaat. De naïeve versie van die test liegt tegen je.",[200,260,262],{"id":261},"waar-pinning-je-werkelijk-tegen-beschermt","Waar pinning je werkelijk tegen beschermt",[175,264,265,266],{},"Snel de uitgangspositie, zodat we dezelfde woorden gebruiken. Normaal vertrouwt je app elk certificaat dat door elke certificaatautoriteit in de vertrouwensopslag van het systeem is ondertekend, en die opslag bevat ongeveer honderdvijftig hoofdcertificaten. Pinning versmalt dat tot iets specifieks: ",[179,267,268],{},"deze sleutel, en niets anders.",[175,270,271,272,275,276,280],{},"Eerst iets over de naam. Iedereen zegt \"SSL-pinning\", wij ook, in de titel van dit artikel. ",[179,273,274],{},"SSL is al meer dan tien jaar dood"," — het is allemaal ",[277,278],"term",{"slug":279},"tls"," nu. Maar daar zoeken mensen op, dus blijft het staan. Weet alleen: zegt iemand \"SSL\" en meent hij dat in 2026, dan zegt dat iets over hoe vers zijn bronnen zijn.",[175,282,283],{},"Nu het eerlijke dreigingsmodel. Twee dingen waar pinning werkelijk tegen verdedigt:",[175,285,286,289],{},[179,287,288],{},"Een gecompromitteerde of malafide certificaatautoriteit."," Dat is de eigenlijke taak van pinning. Het schoolvoorbeeld is DigiNotar in 2011: aanvallers braken in bij een Nederlandse CA, gaven frauduleuze certificaten voor Google-domeinen uit, en onderschepten Iraanse gebruikers. Pinning is wat het ving — Chrome leverde een vooraf geladen pin voor google.com mee.",[175,291,292,295],{},[179,293,294],{},"Onderschepping binnen bedrijven."," DLP-proxy's, antivirussoftware die TLS inspecteert, een MDM die op systeemniveau een root installeert. Pinning breekt ze allemaal. Dat is het punt — maar het betekent ook dat je de legitieme proxy van je zakelijke klant samen met die van de aanvaller breekt.",[175,297,298],{},"En nu het deel dat de blogposts uit 2015 niet dekken, want niets ervan bestond toen.",[300,301,303],"h3",{"id":302},"de-dreiging-van-een-malafide-ca-is-fors-gekrompen","De dreiging van een malafide CA is fors gekrompen",[175,305,306,309,310,313],{},[179,307,308],{},"Certificate Transparency"," publiceert elk publiek vertrouwd certificaat naar open logboeken waar alleen aan toegevoegd kan worden. Het mechanisme startte in 2013, maar werd in de praktijk in 2018 verplicht: Chrome begon het in versie 68 af te dwingen, en Apple dwingt het ",[179,311,312],{},"op OS-niveau"," af, wat betekent: binnen apps, niet alleen in Safari.",[175,315,316],{},"Dat is geen theorie. September 2015 was de eerste keer dat CT een verkeerd uitgegeven certificaat in het wild ving — een niet-geautoriseerd certificaat voor google.com dat Symantec bij interne tests produceerde. Geen kwade opzet, één dag geldig. Het was ook de eerste barst in wat in 2018 eindigde met browsers die Symantec volledig wantrouwden.",[175,318,319],{},"Een recent geval dook op in september 2025: de Kroatische CA Fina had twaalf niet-geautoriseerde certificaten voor 1.1.1.1 van Cloudflare uitgegeven, verspreid over februari 2024 en augustus 2025. Achttien maanden lang merkte niemand het; CT-logboeken brachten ze uiteindelijk boven water. En let op — Fina staat helemaal niet in de mobiele vertrouwensopslag, dus die certificaten zouden op een telefoon toch nooit hebben gevalideerd. Het logboek ving ze ongeacht wie de CA vertrouwt. Precies daar zit de waarde.",[175,321,322],{},"Daarbovenop: CAA-records, DNS-records die verklaren welke CA's voor jouw domein mogen uitgeven, plus verplichte domeinvalidatie vanuit meerdere perspectieven.",[300,324,326],{"id":325},"het-platform-deed-een-deel-al-voor-je","Het platform deed een deel al voor je",[175,328,329,330,333,334,337],{},"Vanaf Android 7 — preciezer: ",[244,331,332],{},"targetSdk"," 24 — ",[179,335,336],{},"vertrouwen apps standaard geen certificaten die de gebruiker heeft geïnstalleerd",". Dat is sinds 2016 de standaard. Het scenario \"het slachtoffer installeerde een kwaadaardige root\" is dus niet iets waar pinning je nog van redt. Het besturingssysteem was je voor.",[339,340,341,361],"table",{},[342,343,344],"thead",{},[345,346,347,352,355,358],"tr",{},[348,349,350],"th",{},[244,351,332],{},[348,353,354],{},"Systeem-CA's",[348,356,357],{},"Door gebruiker geïnstalleerde CA's",[348,359,360],{},"HTTP zonder versleuteling",[362,363,364,380,392],"tbody",{},[345,365,366,370,373,377],{},[367,368,369],"td",{},"≤ 23",[367,371,372],{},"vertrouwd",[367,374,375],{},[179,376,372],{},[367,378,379],{},"toegestaan",[345,381,382,385,387,390],{},[367,383,384],{},"24–27",[367,386,372],{},[367,388,389],{},"niet vertrouwd",[367,391,379],{},[345,393,394,397,399,401],{},[367,395,396],{},"≥ 28",[367,398,372],{},[367,400,389],{},[367,402,403],{},[179,404,405],{},"standaard geblokkeerd",[175,407,408,409,411],{},"Die tabel hangt aan ",[244,410,332],{},", niet aan de Android-versie van het toestel — en daarom vertrouwt een oude app op een nieuwe telefoon nog steeds wat de gebruiker heeft geïnstalleerd.",[300,413,415],{"id":414},"waar-pinning-helemaal-niet-tegen-beschermt","Waar pinning helemaal niet tegen beschermt",[175,417,418,419,422],{},"De formulering van OWASP is botweg: heeft een aanvaller het toestel in handen, dan zet hij je pinninglogica simpelweg uit. Op een geroote Android of een gejailbreakte iPhone is dat één commando in ",[244,420,421],{},"objection"," — en voor Flutter bestaat er speciaal gereedschap, waar we op terugkomen, want dat is interessant.",[175,424,425,426,429],{},"Dus: ",[179,427,428],{},"pinning beschermt je echte gebruikers tegen onderschepping op een vijandig netwerk. Het beschermt je API niet tegen de eigenaar van het toestel."," Gebruik je pinning om sleutels te verbergen of reverse-engineering tegen te houden, dan heb je al verloren. Daar is attestatie aan de serverkant voor — Play Integrity, App Attest — geen trucs aan de clientkant.",[431,432,435],"info-box",{":leading-icon":433,":title":434},"lucide:scale","OWASP spreekt zichzelf hier tegen, en dat telt wanneer iemand je een rapport overhandigt",[175,436,437],{},"MASVS vereist pinning wel — maar alleen voor een app die tegen MAS-L2 moet worden getoetst, het profiel met verdediging in de diepte van OWASP voor software die gevoelige gegevens verwerkt. Het is geen basismaatregel. De testgids zegt ronduit dat het niet als kwetsbaarheid gerapporteerd hoort te worden wanneer een app geen pinning implementeert. En de Pinning Cheat Sheet van OWASP zegt: \"De eerste vraag zou moeten zijn: moet ik pinnen? Het antwoord daarop is waarschijnlijk nooit.\" Die formulering is in maart 2023 toegevoegd; daarvoor stond ze er niet. Markeert een pentestrapport dus \"ontbrekende SSL-pinning\" als bevinding, dan is dat meestal een geautomatiseerde checklist en geen besluit over jouw dreigingsmodel.",[200,439,441],{"id":440},"heb-je-certificate-pinning-werkelijk-nodig","Heb je certificate pinning werkelijk nodig?",[175,443,444,445],{},"Korte checklist. Slaat ook maar één van deze op jou — ",[179,446,447],{},"niet pinnen.",[205,449,450,453,456,459,462],{},[208,451,452],{},"Je beheert niet beide kanten: je server en je app",[208,454,455],{},"Je kunt de pinset niet veilig en snel bijwerken",[208,457,458],{},"Pins bijwerken vraagt een appversie uitbrengen",[208,460,461],{},"Je kunt het sleutelpaar niet kennen voordat het in productie gaat",[208,463,464],{},"Het is geen native mobiele app",[175,466,467],{},"Wie het werkelijk nodig heeft: financiën, zorg, overheidsdiensten — overal waar de gegevens gevoelig zijn en het dreigingsmodel uitgaat van een vijandig netwerk. Dat is het MAS-L2-geval. Alle anderen: waarschijnlijk niet.",[300,469,471],{"id":470},"waarom-dit-in-2026-urgent-werd","Waarom dit in 2026 urgent werd",[175,473,474],{},"In april 2025 stemde het CA\u002FBrowser Forum om de levensduur van TLS-certificaten af te bouwen. Apple diende het voorstel in. Het schema:",[339,476,477,490],{},[342,478,479],{},[345,480,481,484,487],{},[348,482,483],{},"Vanaf",[348,485,486],{},"Maximale levensduur",[348,488,489],{},"Wisselingen per jaar",[362,491,492,503,518,529],{},[345,493,494,497,500],{},[367,495,496],{},"vóór maart 2026",[367,498,499],{},"398 dagen",[367,501,502],{},"~1",[345,504,505,510,515],{},[367,506,507],{},[179,508,509],{},"15 maart 2026",[367,511,512],{},[179,513,514],{},"200 dagen",[367,516,517],{},"~2",[345,519,520,523,526],{},[367,521,522],{},"15 maart 2027",[367,524,525],{},"100 dagen",[367,527,528],{},"~4",[345,530,531,534,537],{},[367,532,533],{},"15 maart 2029",[367,535,536],{},"47 dagen",[367,538,539],{},"~8",[175,541,542,543,546],{},"De trap van 200 dagen geldt nu. Zevenenveertig dagen is ruwweg ",[179,544,545],{},"acht certificaatwisselingen per jaar",". Pin je het blad en kun je pins niet bijwerken zonder uit te brengen, dan zijn dat acht gedwongen app-updates per jaar — met review in de appstore, en met gebruikers die niet bijwerken.",[175,548,549,550,553,554,557],{},"En het ecosysteem bevecht pinning met opzet. In 2024 begon Let's Encrypt ",[179,551,552],{},"de uitgevende tussenlaag willekeurig te kiezen",", specifiek om de gewoonte tussenlagen te pinnen te breken. In november 2025 nam het dezelfde regel mee naar een nieuwe hiërarchie van zes tussenlagen, en de aankondiging is er duidelijk over: ",[179,555,556],{},"\"zoals eerder kiest elke uitgifte willekeurig welke tussenlaag wordt gebruikt, om het pinnen van tussenlaagsleutels te ontmoedigen.\""," De meestgebruikte CA op het internet is nu twee jaar bezig geweest dat soort pinning bewust te breken.",[175,559,560],{},"Dat is geen kwaadwilligheid. Het is duurbetaalde ervaring, en de browserwereld ging er als eerste doorheen.",[175,562,563,566],{},[179,564,565],{},"HPKP"," — pinning via een HTTP-header, in 2015 door Google-engineers gespecificeerd — mislukte volledig. Chrome faseerde het uit in versie 67 en haalde het weg in versie 72, in januari 2019. De opgegeven redenen van Google: zeer laag gebruik, kans op dienstweigering, en vijandige pinning.",[175,568,569],{},"Het beroemde slachtoffer is Smashing Magazine, oktober 2016: vier dagen offline voor de meeste lezers. Ze wisselden naar een certificaat met een nieuwe sleutel, stuurden een header met alleen de nieuwe pin, en iedereen met een gecachte pinset was buitengesloten. Terugdraaien kon niet, want het oude certificaat was al verlopen.",[175,571,572],{},"Aan de mobiele kant: Barclays, november 2016. De app zou een verouderde tussenlaag hebben gepind, de keten veranderde, en betalingen stopten — aan de vooravond van Black Friday. Om het op tijd te herstellen gaf Symantec een certificaat onder de oude tussenlaag uit, en moest daarvoor de eis van het CA\u002FBrowser Forum over serienummers schenden.",[175,574,575,576],{},"In beide gevallen dezelfde moraal: ",[179,577,578],{},"pinning breekt niet wanneer je wordt aangevallen. Het breekt op een gewone dinsdag, wanneer iemand een certificaat vernieuwt.",[200,580,582],{"id":581},"vijf-regels-als-je-toch-pint","Vijf regels als je toch pint",[300,584,586],{"id":585},"regel-1-pin-de-sleutel-niet-het-certificaat","Regel 1. Pin de sleutel, niet het certificaat",[175,588,589],{},"Technisch is dat de SPKI — SubjectPublicKeyInfo, de structuur in het certificaat die de publieke sleutel bevat. Je pint de met base64 gecodeerde SHA-256-hash ervan.",[175,591,592,593,596],{},"Waarom dat het hele spel is: ",[179,594,595],{},"het certificaat verandert bij elke vernieuwing, de sleutel niet."," Pin de sleutel en een vernieuwing met dezelfde sleutel raakt je pin nooit. Pin de vingerafdruk van het hele certificaat en het breekt elke keer — twee keer per jaar bij 200 dagen, acht keer bij 47.",[175,598,599],{},"Dit is het commando voor een live host:",[601,602,607],"pre",{"className":603,"code":604,"language":605,"meta":606,"style":606},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","openssl s_client -connect api.example.com:443 -servername api.example.com \u003C\u002Fdev\u002Fnull 2>\u002Fdev\u002Fnull \\\n  | openssl x509 -pubkey -noout \\\n  | openssl pkey -pubin -outform der \\\n  | openssl dgst -sha256 -binary \\\n  | openssl enc -base64\n","bash","",[244,608,609,651,671,692,710],{"__ignoreMap":606},[610,611,614,618,622,626,629,632,635,639,642,645,647],"span",{"class":612,"line":613},"line",1,[610,615,617],{"class":616},"sbgvK","openssl",[610,619,621],{"class":620},"s_sjI"," s_client",[610,623,625],{"class":624},"stzsN"," -connect",[610,627,628],{"class":620}," api.example.com:443",[610,630,631],{"class":624}," -servername",[610,633,634],{"class":620}," api.example.com",[610,636,638],{"class":637},"smGrS"," \u003C",[610,640,641],{"class":620},"\u002Fdev\u002Fnull",[610,643,644],{"class":637}," 2>",[610,646,641],{"class":620},[610,648,650],{"class":649},"s_hVV"," \\\n",[610,652,654,657,660,663,666,669],{"class":612,"line":653},2,[610,655,656],{"class":637},"  |",[610,658,659],{"class":616}," openssl",[610,661,662],{"class":620}," x509",[610,664,665],{"class":624}," -pubkey",[610,667,668],{"class":624}," -noout",[610,670,650],{"class":649},[610,672,674,676,678,681,684,687,690],{"class":612,"line":673},3,[610,675,656],{"class":637},[610,677,659],{"class":616},[610,679,680],{"class":620}," pkey",[610,682,683],{"class":624}," -pubin",[610,685,686],{"class":624}," -outform",[610,688,689],{"class":620}," der",[610,691,650],{"class":649},[610,693,695,697,699,702,705,708],{"class":612,"line":694},4,[610,696,656],{"class":637},[610,698,659],{"class":616},[610,700,701],{"class":620}," dgst",[610,703,704],{"class":624}," -sha256",[610,706,707],{"class":624}," -binary",[610,709,650],{"class":649},[610,711,713,715,717,720],{"class":612,"line":712},5,[610,714,656],{"class":637},[610,716,659],{"class":616},[610,718,719],{"class":620}," enc",[610,721,722],{"class":624}," -base64\n",[175,724,725],{},"En dezelfde hash uit een private sleutel die je hebt gegenereerd maar nog niet hebt laten certificeren — zo bereken je een reservepin:",[601,727,729],{"className":603,"code":728,"language":605,"meta":606,"style":606},"openssl pkey -in backup.key -pubout -outform der \\\n  | openssl dgst -sha256 -binary \\\n  | openssl enc -base64\n",[244,730,731,752,766],{"__ignoreMap":606},[610,732,733,735,737,740,743,746,748,750],{"class":612,"line":613},[610,734,617],{"class":616},[610,736,680],{"class":620},[610,738,739],{"class":624}," -in",[610,741,742],{"class":620}," backup.key",[610,744,745],{"class":624}," -pubout",[610,747,686],{"class":624},[610,749,689],{"class":620},[610,751,650],{"class":649},[610,753,754,756,758,760,762,764],{"class":612,"line":653},[610,755,656],{"class":637},[610,757,659],{"class":616},[610,759,701],{"class":620},[610,761,704],{"class":624},[610,763,707],{"class":624},[610,765,650],{"class":649},[610,767,768,770,772,774],{"class":612,"line":673},[610,769,656],{"class":637},[610,771,659],{"class":616},[610,773,719],{"class":620},[610,775,722],{"class":624},[175,777,778,779,782],{},"Het goede nieuws: beide platforms ondersteunen van nature ",[179,780,781],{},"alleen"," SPKI, zowel op Android als op iOS. Pin je dus een heel certificaat, dan heb je het zelf gebouwd of een bibliotheek gekozen die het voor je doet — en vrijwel zeker zonder goede reden.",[300,784,786],{"id":785},"regel-2-reservepins-zijn-verplicht","Regel 2. Reservepins zijn verplicht",[175,788,789,790,793],{},"Minstens één sleutel ",[179,791,792],{},"volledig onder jouw beheer."," Dat is de formulering van Google, en \"volledig onder jouw beheer\" is het dragende deel. Niet \"een tweede certificaat van dezelfde CA\". Een sleutelpaar dat je vooraf hebt gegenereerd en offline bewaart, en waarvoor je op afroep een certificaat kunt krijgen. De pin ervan gaat naast de live pin mee in de app en wacht daar simpelweg af.",[175,795,796],{},"Die ontbrekende reservepin is precies wat van de slechte dag van Smashing Magazine vier dagen uitval maakte. Ze hadden de oude sleutel nog — hun hostingpartij had hem bewaard. Wat ze in de header hadden meegestuurd was alleen de vingerafdruk van de nieuwe.",[601,798,802],{"className":799,"code":800,"language":801,"meta":606,"style":606},"language-xml shiki shiki-themes material-theme-lighter github-light github-dark","\u003C!-- res\u002Fxml\u002Fnetwork_security_config.xml -->\n\u003Cnetwork-security-config>\n  \u003Cdomain-config>\n    \u003Cdomain includeSubdomains=\"true\">api.example.com\u003C\u002Fdomain>\n    \u003Cpin-set expiration=\"2027-03-01\">\n      \u003C!-- actieve sleutel -->\n      \u003Cpin digest=\"SHA-256\">YLh1dUR9y6Kja30RrAn7JKnbQG\u002FuEtLMkBgFF2Fuihg=\u003C\u002Fpin>\n      \u003C!-- offline reservesleutel, nog nooit gebruikt voor verkeer -->\n      \u003Cpin digest=\"SHA-256\">sRHdihwgkaib1P1gxX8HFszlD+7\u002FgTfNvuAybgLPNis=\u003C\u002Fpin>\n    \u003C\u002Fpin-set>\n  \u003C\u002Fdomain-config>\n\u003C\u002Fnetwork-security-config>\n","xml",[244,803,804,810,823,833,871,892,898,930,936,964,974,984],{"__ignoreMap":606},[610,805,806],{"class":612,"line":613},[610,807,809],{"class":808},"sutJx","\u003C!-- res\u002Fxml\u002Fnetwork_security_config.xml -->\n",[610,811,812,816,820],{"class":612,"line":653},[610,813,815],{"class":814},"sP7_E","\u003C",[610,817,819],{"class":818},"sQzsp","network-security-config",[610,821,822],{"class":814},">\n",[610,824,825,828,831],{"class":612,"line":673},[610,826,827],{"class":814},"  \u003C",[610,829,830],{"class":818},"domain-config",[610,832,822],{"class":814},[610,834,835,838,841,845,848,852,855,857,860,864,867,869],{"class":612,"line":694},[610,836,837],{"class":814},"    \u003C",[610,839,840],{"class":818},"domain",[610,842,844],{"class":843},"s9AJx"," includeSubdomains",[610,846,847],{"class":814},"=",[610,849,851],{"class":850},"sjJ54","\"",[610,853,854],{"class":620},"true",[610,856,851],{"class":850},[610,858,859],{"class":814},">",[610,861,863],{"class":862},"su5hD","api.example.com",[610,865,866],{"class":814},"\u003C\u002F",[610,868,840],{"class":818},[610,870,822],{"class":814},[610,872,873,875,878,881,883,885,888,890],{"class":612,"line":712},[610,874,837],{"class":814},[610,876,877],{"class":818},"pin-set",[610,879,880],{"class":843}," expiration",[610,882,847],{"class":814},[610,884,851],{"class":850},[610,886,887],{"class":620},"2027-03-01",[610,889,851],{"class":850},[610,891,822],{"class":814},[610,893,895],{"class":612,"line":894},6,[610,896,897],{"class":808},"      \u003C!-- actieve sleutel -->\n",[610,899,901,904,907,910,912,914,917,919,921,924,926,928],{"class":612,"line":900},7,[610,902,903],{"class":814},"      \u003C",[610,905,906],{"class":818},"pin",[610,908,909],{"class":843}," digest",[610,911,847],{"class":814},[610,913,851],{"class":850},[610,915,916],{"class":620},"SHA-256",[610,918,851],{"class":850},[610,920,859],{"class":814},[610,922,923],{"class":862},"YLh1dUR9y6Kja30RrAn7JKnbQG\u002FuEtLMkBgFF2Fuihg=",[610,925,866],{"class":814},[610,927,906],{"class":818},[610,929,822],{"class":814},[610,931,933],{"class":612,"line":932},8,[610,934,935],{"class":808},"      \u003C!-- offline reservesleutel, nog nooit gebruikt voor verkeer -->\n",[610,937,939,941,943,945,947,949,951,953,955,958,960,962],{"class":612,"line":938},9,[610,940,903],{"class":814},[610,942,906],{"class":818},[610,944,909],{"class":843},[610,946,847],{"class":814},[610,948,851],{"class":850},[610,950,916],{"class":620},[610,952,851],{"class":850},[610,954,859],{"class":814},[610,956,957],{"class":862},"sRHdihwgkaib1P1gxX8HFszlD+7\u002FgTfNvuAybgLPNis=",[610,959,866],{"class":814},[610,961,906],{"class":818},[610,963,822],{"class":814},[610,965,967,970,972],{"class":612,"line":966},10,[610,968,969],{"class":814},"    \u003C\u002F",[610,971,877],{"class":818},[610,973,822],{"class":814},[610,975,977,980,982],{"class":612,"line":976},11,[610,978,979],{"class":814},"  \u003C\u002F",[610,981,830],{"class":818},[610,983,822],{"class":814},[610,985,987,989,991],{"class":612,"line":986},12,[610,988,866],{"class":814},[610,990,819],{"class":818},[610,992,822],{"class":814},[175,994,995],{},"Het iOS-equivalent, dat declaratief is en door het besturingssysteem wordt afgedwongen:",[601,997,999],{"className":799,"code":998,"language":801,"meta":606,"style":606},"\u003C!-- Info.plist -->\n\u003Ckey>NSAppTransportSecurity\u003C\u002Fkey>\n\u003Cdict>\n  \u003Ckey>NSPinnedDomains\u003C\u002Fkey>\n  \u003Cdict>\n    \u003Ckey>api.example.com\u003C\u002Fkey>\n    \u003Cdict>\n      \u003Ckey>NSIncludesSubdomains\u003C\u002Fkey>\u003Ctrue\u002F>\n      \u003Ckey>NSPinnedLeafIdentities\u003C\u002Fkey>\n      \u003Carray>\n        \u003Cdict>\u003Ckey>SPKI-SHA256-BASE64\u003C\u002Fkey>\u003Cstring>YLh1dUR9y6Kja30RrAn7JKnbQG\u002FuEtLMkBgFF2Fuihg=\u003C\u002Fstring>\u003C\u002Fdict>\n        \u003Cdict>\u003Ckey>SPKI-SHA256-BASE64\u003C\u002Fkey>\u003Cstring>sRHdihwgkaib1P1gxX8HFszlD+7\u002FgTfNvuAybgLPNis=\u003C\u002Fstring>\u003C\u002Fdict>\n      \u003C\u002Farray>\n    \u003C\u002Fdict>\n  \u003C\u002Fdict>\n\u003C\u002Fdict>\n",[244,1000,1001,1006,1024,1033,1050,1058,1074,1082,1105,1122,1131,1171,1207,1217,1226,1235],{"__ignoreMap":606},[610,1002,1003],{"class":612,"line":613},[610,1004,1005],{"class":808},"\u003C!-- Info.plist -->\n",[610,1007,1008,1010,1013,1015,1018,1020,1022],{"class":612,"line":653},[610,1009,815],{"class":814},[610,1011,1012],{"class":818},"key",[610,1014,859],{"class":814},[610,1016,1017],{"class":862},"NSAppTransportSecurity",[610,1019,866],{"class":814},[610,1021,1012],{"class":818},[610,1023,822],{"class":814},[610,1025,1026,1028,1031],{"class":612,"line":673},[610,1027,815],{"class":814},[610,1029,1030],{"class":818},"dict",[610,1032,822],{"class":814},[610,1034,1035,1037,1039,1041,1044,1046,1048],{"class":612,"line":694},[610,1036,827],{"class":814},[610,1038,1012],{"class":818},[610,1040,859],{"class":814},[610,1042,1043],{"class":862},"NSPinnedDomains",[610,1045,866],{"class":814},[610,1047,1012],{"class":818},[610,1049,822],{"class":814},[610,1051,1052,1054,1056],{"class":612,"line":712},[610,1053,827],{"class":814},[610,1055,1030],{"class":818},[610,1057,822],{"class":814},[610,1059,1060,1062,1064,1066,1068,1070,1072],{"class":612,"line":894},[610,1061,837],{"class":814},[610,1063,1012],{"class":818},[610,1065,859],{"class":814},[610,1067,863],{"class":862},[610,1069,866],{"class":814},[610,1071,1012],{"class":818},[610,1073,822],{"class":814},[610,1075,1076,1078,1080],{"class":612,"line":900},[610,1077,837],{"class":814},[610,1079,1030],{"class":818},[610,1081,822],{"class":814},[610,1083,1084,1086,1088,1090,1093,1095,1097,1100,1102],{"class":612,"line":932},[610,1085,903],{"class":814},[610,1087,1012],{"class":818},[610,1089,859],{"class":814},[610,1091,1092],{"class":862},"NSIncludesSubdomains",[610,1094,866],{"class":814},[610,1096,1012],{"class":818},[610,1098,1099],{"class":814},">\u003C",[610,1101,854],{"class":818},[610,1103,1104],{"class":814},"\u002F>\n",[610,1106,1107,1109,1111,1113,1116,1118,1120],{"class":612,"line":938},[610,1108,903],{"class":814},[610,1110,1012],{"class":818},[610,1112,859],{"class":814},[610,1114,1115],{"class":862},"NSPinnedLeafIdentities",[610,1117,866],{"class":814},[610,1119,1012],{"class":818},[610,1121,822],{"class":814},[610,1123,1124,1126,1129],{"class":612,"line":966},[610,1125,903],{"class":814},[610,1127,1128],{"class":818},"array",[610,1130,822],{"class":814},[610,1132,1133,1136,1138,1140,1142,1144,1147,1149,1151,1153,1156,1158,1160,1162,1164,1167,1169],{"class":612,"line":976},[610,1134,1135],{"class":814},"        \u003C",[610,1137,1030],{"class":818},[610,1139,1099],{"class":814},[610,1141,1012],{"class":818},[610,1143,859],{"class":814},[610,1145,1146],{"class":862},"SPKI-SHA256-BASE64",[610,1148,866],{"class":814},[610,1150,1012],{"class":818},[610,1152,1099],{"class":814},[610,1154,1155],{"class":818},"string",[610,1157,859],{"class":814},[610,1159,923],{"class":862},[610,1161,866],{"class":814},[610,1163,1155],{"class":818},[610,1165,1166],{"class":814},">\u003C\u002F",[610,1168,1030],{"class":818},[610,1170,822],{"class":814},[610,1172,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199,1201,1203,1205],{"class":612,"line":986},[610,1174,1135],{"class":814},[610,1176,1030],{"class":818},[610,1178,1099],{"class":814},[610,1180,1012],{"class":818},[610,1182,859],{"class":814},[610,1184,1146],{"class":862},[610,1186,866],{"class":814},[610,1188,1012],{"class":818},[610,1190,1099],{"class":814},[610,1192,1155],{"class":818},[610,1194,859],{"class":814},[610,1196,957],{"class":862},[610,1198,866],{"class":814},[610,1200,1155],{"class":818},[610,1202,1166],{"class":814},[610,1204,1030],{"class":818},[610,1206,822],{"class":814},[610,1208,1210,1213,1215],{"class":612,"line":1209},13,[610,1211,1212],{"class":814},"      \u003C\u002F",[610,1214,1128],{"class":818},[610,1216,822],{"class":814},[610,1218,1220,1222,1224],{"class":612,"line":1219},14,[610,1221,969],{"class":814},[610,1223,1030],{"class":818},[610,1225,822],{"class":814},[610,1227,1229,1231,1233],{"class":612,"line":1228},15,[610,1230,979],{"class":814},[610,1232,1030],{"class":818},[610,1234,822],{"class":814},[610,1236,1238,1240,1242],{"class":612,"line":1237},16,[610,1239,866],{"class":814},[610,1241,1030],{"class":818},[610,1243,822],{"class":814},[175,1245,1246,1247,1250],{},"Dat voorbeeld pint bladsleutels. De eigen documentatie van Apple gebruikt in plaats daarvan ",[244,1248,1249],{},"NSPinnedCAIdentities"," — dezelfde structuur één niveau hoger in de keten, en daar gaat regel 3 over.",[300,1252,1254],{"id":1253},"regel-3-kies-je-niveau-in-de-keten-en-weet-wat-het-kost","Regel 3. Kies je niveau in de keten — en weet wat het kost",[175,1256,1257,1258,1261,1262,1265],{},"Hier is geen consensus, dus volgen beide standpunten. ",[179,1259,1260],{},"Apple raadt aan de CA te pinnen in plaats van de server",", zodat je servercertificaten kunt wisselen zonder een app-update uit te brengen. ",[179,1263,1264],{},"OWASP raadt het omgekeerde aan",": pin het blad, maar altijd met een reserve.",[339,1267,1268,1284],{},[342,1269,1270],{},[345,1271,1272,1275,1278,1281],{},[348,1273,1274],{},"Niveau",[348,1276,1277],{},"Kosten van wisselen",[348,1279,1280],{},"Wat je vertrouwt",[348,1282,1283],{},"Oordeel in 2026",[362,1285,1286,1303,1320,1334],{},[345,1287,1288,1291,1294,1297],{},[367,1289,1290],{},"Blad",[367,1292,1293],{},"Gratis bij vernieuwing met dezelfde sleutel; anders een release",[367,1295,1296],{},"Precies één sleutel",[367,1298,1299,1300,1302],{},"Werkbaar ",[179,1301,781],{}," met reservepins",[345,1304,1305,1308,1311,1314],{},[367,1306,1307],{},"Tussenlaag",[367,1309,1310],{},"Onvoorspelbaar",[367,1312,1313],{},"Alles wat die tussenlaag uitgeeft",[367,1315,1316,1319],{},[179,1317,1318],{},"Van tafel"," bij publieke CA's",[345,1321,1322,1325,1328,1331],{},[367,1323,1324],{},"Publieke root",[367,1326,1327],{},"Zelden",[367,1329,1330],{},"Alles wat die CA ooit zal uitgeven",[367,1332,1333],{},"Zo breed dat het bijna zinloos is",[345,1335,1336,1341,1344,1347],{},[367,1337,1338],{},[179,1339,1340],{},"Je eigen private root",[367,1342,1343],{},"Jij bepaalt",[367,1345,1346],{},"Je eigen PKI",[367,1348,1349],{},"Het ene duidelijk goede geval",[175,1351,1352],{},"Een tussenlaag pinnen voor publiek vertrouwde certificaten is sinds 2024 in wezen van tafel: Let's Encrypt heeft nu zes tussenlagen en je kunt niet voorspellen welke jouw certificaat krijgt. Zit je op een publieke CA, dan blijft het blad met reservepins over, of de root.",[175,1354,1355,1356,1359],{},"De opstelling waarin pinning nog steeds werkelijk een goed idee is, is ",[179,1357,1358],{},"je eigen private CA",", een die je van begin tot eind beheert. Op je eigen root pinnen is verstandig, voorspelbaar, en breekt niet doordat iemand anders van gedachten verandert. En dat is trouwens precies wat die Chromium-engineer in het citaat bovenaan zei.",[300,1361,1363],{"id":1362},"regel-4-lever-nooit-direct-naar-harde-afwijzing","Regel 4. Lever nooit direct naar harde afwijzing",[175,1365,1366,1367,1370,1371,1375],{},"De volgorde is: eerst ",[179,1368,1369],{},"alleen-rapporteren"," — de pin wordt gecontroleerd, de verbinding wordt niet geblokkeerd, en missers gaan naar telemetrie. Een week kijken. Dan harde afwijzing voor één procent van de gebruikers. Daarna een ",[277,1372,1374],{"slug":1373},"staged-rollout","gefaseerde uitrol"," naar iedereen.",[175,1377,1378,1379,1382,1383,1386,1387,1390],{},"En nu het vervelende deel: ",[179,1380,1381],{},"op geen van beide platforms bestaat ingebouwde foutrapportage."," Niet in de Network Security Config van Android, niet in App Transport Security van iOS. Het ene mechanisme dat dit ooit deed was ",[244,1384,1385],{},"report-uri"," van HPKP, en dat is dood. Dus bouw je de telemetrie zelf: vang de exceptie, bouw een gebeurtenis — domein, sleutel die je kreeg, sleutels die je verwachtte, appversie — en stuur die ",[179,1388,1389],{},"over een kanaal dat niet gepind is",", anders komt het bericht over je storing er niet uit.",[175,1392,1393,1394,1397],{},"Nog iets: leer een storing van een aanval te onderscheiden. ",[179,1395,1396],{},"Faalt iedereen tegelijk, dan heb je je eigen wisseling gebroken."," Verspreide fouten wijzen op een bedrijfsproxy, een antivirus, of een echte onderscheppingspoging.",[300,1399,1401],{"id":1400},"regel-5-bouw-de-noodrem-voordat-je-hem-nodig-hebt","Regel 5. Bouw de noodrem voordat je hem nodig hebt",[175,1403,1404,1405,1408],{},"Op Android zit er een ingebouwd — het attribuut ",[244,1406,1407],{},"expiration"," op de pinset. Na die datum stopt pinning simpelweg: pins worden niet gecontroleerd, verkeer loopt alsof er niets aan de hand was. Een noodrem op een tijdklok.",[175,1410,1411,1412,1415,1416,1419,1420,1423],{},"Maar er hangt een prijskaartje aan, en Google zegt dat zelf: ",[179,1413,1414],{},"\"een vervaltijd op pins instellen kan aanvallers in staat stellen je gepinde certificaten te omzeilen.\""," Het is dus tegelijk je verzekering tegen een onbruikbare app ",[179,1417,1418],{},"en"," een manier om je pinning te omzeilen door te wachten. Op een andere pagina van diezelfde documentatie beveelt Google ",[179,1421,1422],{},"\"een voldoende korte vervalperiode\""," aan. Beide uitspraken zijn officieel en beide zijn actueel.",[175,1425,1426,1427,1433],{},"En nu ons favoriete detail in dit hele verhaal. ",[179,1428,1429,1430,182],{},"Het canonieke voorbeeld in de documentatie van Google draagt nog steeds ",[244,1431,1432],{},"expiration=\"2018-01-01\""," Kopieerde je het — en iedereen kopieert het — dan staat je pinning al acht jaar volledig uit. Ze werkten die pagina in juni 2026 bij. De datum bleef staan.",[175,1435,1436],{},"Op iOS bestaat helemaal geen vergelijkbaar mechanisme. Pinning uitzetten betekent een release uitbrengen.",[175,1438,1439,1440,1444,1445,1448],{},"Bouw je een noodrem via configuratie op afstand of ",[277,1441,1443],{"slug":1442},"feature-flags","functieschakelaars",", let dan op het kip-en-eiprobleem: het bezorgkanaal mag niet door dezelfde pins beschermd worden, anders kun je nooit herstellen. Het juiste antwoord is ",[179,1446,1447],{},"de pinset ondertekenen met een sleutel waarvan de publieke helft in de app is gebakken."," Dan maakt het niet uit over welk kanaal ze binnenkwam. Voeg een oplopende versieteller toe zodat niemand een oudere, geldig ondertekende set kan herhalen.",[200,1450,1452],{"id":1451},"flutter-waar-certificate-pinning-uit-elkaar-valt","Flutter: waar certificate pinning uit elkaar valt",[175,1454,1455],{},"Alles hierboven geldt voor iedereen. Dit is het deel waarvoor we het artikel schreven — wij bouwen Flutter-apps, en hier hebben we onze handen in gehad.",[175,1457,1458],{},[179,1459,1460],{},"Dit is de kop, en hij druist in tegen wat de meeste mensen aannemen: Dart gebruikt de netwerkstack van het platform niet.",[175,1462,1463,1466,1467,1470,1471,1474,1475,1478,1479,1482],{},[244,1464,1465],{},"HttpClient"," uit ",[244,1468,1469],{},"dart:io"," gaat niet via ",[244,1472,1473],{},"OkHttp"," of ",[244,1476,1477],{},"HttpURLConnection"," op Android, of ",[244,1480,1481],{},"NSURLSession"," op iOS. Het opent een socket en draait de TLS-handshake zelf, via een kopie van BoringSSL die in de Flutter-engine is gecompileerd.",[175,1484,1485,1486,1494],{},"Het praktische gevolg: ",[179,1487,1488,1489,247,1491,1493],{},"je ",[244,1490,246],{},[244,1492,250],{}," doet niets voor Dart-verkeer."," Stilzwijgend. Geen fout, geen waarschuwing — het verzoek gaat gewoon door.",[175,1496,1497,1498,1505],{},"Dit is niet onze ontdekking. Het is ",[1499,1500,1504],"a",{"href":1501,"rel":1502},"https:\u002F\u002Fgithub.com\u002Fflutter\u002Fflutter\u002Fissues\u002F96722",[1503],"nofollow","Flutter-issue #96722",", ingediend in januari 2022 en nog steeds open. Een lid van het Flutter-team reproduceerde het en schreef de bevinding op: op iOS wordt het verzoek netjes geannuleerd, op Android gaat het door, en op een native Android-controleapp met dezelfde configuratie faalt het zoals het hoort.",[175,1507,1508,1509,1512,1513,1516],{},"Op iOS is het gedrag anders: er wordt gemeld dat het werkt, omdat Dart op Apple-platforms het vertrouwensbesluit aan ",[244,1510,1511],{},"SecTrust"," van het systeem overlaat. Dat staat nergens gedocumenteerd, en eerlijk gezegd is het de slechtste uitkomst. ",[179,1514,1515],{},"Pinning die op het ene platform werkt en op het andere stilzwijgend niets doet is gevaarlijker dan pinning die nergens werkt"," — want je hebt het getest. Op een iPhone.",[175,1518,1519],{},"Dan drie valkuilen. We zijn ze alle drie in productie tegengekomen.",[175,1521,1522,1525,1526,1529,1530,1533,1534,1533,1537,1540],{},[179,1523,1524],{},"Dart geeft je geen toegang tot de SPKI."," De klasse ",[244,1527,1528],{},"X509Certificate"," toont ",[244,1531,1532],{},"der",", ",[244,1535,1536],{},"pem",[244,1538,1539],{},"sha1",", onderwerp, uitgever en geldigheidsdata. De publieke sleutel zit daar niet bij. Vrijwel elke handleiding pint dus een SHA-256 van het hele certificaat — wat volgens regel 1 bij elke vernieuwing breekt. Het goed doen betekent ASN.1 met de hand ontleden.",[175,1542,1543,1546],{},[179,1544,1545],{},"De eigen README van dio toont twee manieren om dit fout te doen."," Dit is de eerste, samengevat:",[601,1548,1551],{"className":1549,"code":1550,"language":18,"meta":606,"style":606},"language-dart shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F Voorbeeld uit de README van dio. Drie problemen in negen regels.\ndio.httpClientAdapter = IOHttpClientAdapter(\n  createHttpClient: () {\n    final client = HttpClient(context: SecurityContext(withTrustedRoots: false));\n    client.badCertificateCallback = (cert, host, port) => true;\n    return client;\n  },\n  validateCertificate: (cert, host, port) =>\n      cert != null && fingerprint == sha256.convert(cert.der).toString(),\n);\n",[244,1552,1553,1558,1577,1588,1625,1659,1670,1678,1698,1745],{"__ignoreMap":606},[610,1554,1555],{"class":612,"line":613},[610,1556,1557],{"class":808},"\u002F\u002F Voorbeeld uit de README van dio. Drie problemen in negen regels.\n",[610,1559,1560,1563,1565,1568,1570,1574],{"class":612,"line":653},[610,1561,1562],{"class":862},"dio",[610,1564,182],{"class":814},[610,1566,1567],{"class":862},"httpClientAdapter ",[610,1569,847],{"class":637},[610,1571,1573],{"class":1572},"sZMiF"," IOHttpClientAdapter",[610,1575,1576],{"class":862},"(\n",[610,1578,1579,1582,1585],{"class":612,"line":673},[610,1580,1581],{"class":862},"  createHttpClient",[610,1583,1584],{"class":637},":",[610,1586,1587],{"class":862}," () {\n",[610,1589,1590,1594,1597,1599,1602,1605,1607,1610,1613,1615,1619,1622],{"class":612,"line":694},[610,1591,1593],{"class":1592},"sbsja","    final",[610,1595,1596],{"class":862}," client ",[610,1598,847],{"class":637},[610,1600,1601],{"class":1572}," HttpClient",[610,1603,1604],{"class":862},"(context",[610,1606,1584],{"class":637},[610,1608,1609],{"class":1572}," SecurityContext",[610,1611,1612],{"class":862},"(withTrustedRoots",[610,1614,1584],{"class":637},[610,1616,1618],{"class":1617},"s39Yj"," false",[610,1620,1621],{"class":862},"))",[610,1623,1624],{"class":814},";\n",[610,1626,1627,1630,1632,1635,1637,1640,1643,1646,1648,1651,1654,1657],{"class":612,"line":712},[610,1628,1629],{"class":862},"    client",[610,1631,182],{"class":814},[610,1633,1634],{"class":862},"badCertificateCallback ",[610,1636,847],{"class":637},[610,1638,1639],{"class":862}," (cert",[610,1641,1642],{"class":814},",",[610,1644,1645],{"class":862}," host",[610,1647,1642],{"class":814},[610,1649,1650],{"class":862}," port) ",[610,1652,1653],{"class":637},"=>",[610,1655,1656],{"class":1617}," true",[610,1658,1624],{"class":814},[610,1660,1661,1665,1668],{"class":612,"line":894},[610,1662,1664],{"class":1663},"sVHd0","    return",[610,1666,1667],{"class":862}," client",[610,1669,1624],{"class":814},[610,1671,1672,1675],{"class":612,"line":900},[610,1673,1674],{"class":862},"  }",[610,1676,1677],{"class":814},",\n",[610,1679,1680,1683,1685,1687,1689,1691,1693,1695],{"class":612,"line":932},[610,1681,1682],{"class":862},"  validateCertificate",[610,1684,1584],{"class":637},[610,1686,1639],{"class":862},[610,1688,1642],{"class":814},[610,1690,1645],{"class":862},[610,1692,1642],{"class":814},[610,1694,1650],{"class":862},[610,1696,1697],{"class":637},"=>\n",[610,1699,1700,1703,1706,1709,1712,1715,1718,1721,1723,1727,1730,1732,1735,1737,1740,1743],{"class":612,"line":938},[610,1701,1702],{"class":862},"      cert ",[610,1704,1705],{"class":637},"!=",[610,1707,1708],{"class":1617}," null",[610,1710,1711],{"class":637}," &&",[610,1713,1714],{"class":862}," fingerprint ",[610,1716,1717],{"class":637},"==",[610,1719,1720],{"class":862}," sha256",[610,1722,182],{"class":814},[610,1724,1726],{"class":1725},"sGLFI","convert",[610,1728,1729],{"class":862},"(cert",[610,1731,182],{"class":814},[610,1733,1734],{"class":862},"der)",[610,1736,182],{"class":814},[610,1738,1739],{"class":1725},"toString",[610,1741,1742],{"class":862},"()",[610,1744,1677],{"class":814},[610,1746,1747,1750],{"class":612,"line":966},[610,1748,1749],{"class":862},")",[610,1751,1624],{"class":814},[175,1753,1754,1757,1758,1761,1762,1765,1766,1769],{},[244,1755,1756],{},"withTrustedRoots: false"," plus een allesomvattende ",[244,1759,1760],{},"badCertificateCallback"," zet het opbouwen van de keten, de verloopcontrole en de hostnaamverificatie uit. De vingerafdruk die het vergelijkt is een hash van ",[244,1763,1764],{},"cert.der"," — het hele certificaat, wat volgens regel 1 bij elke vernieuwing breekt. En ",[244,1767,1768],{},"validateCertificate"," draait later dan je denkt; zie hieronder.",[175,1771,1772,1773,1775,1776,1779,1780,1783],{},"De tweede variant in de README is erger: die pint binnen ",[244,1774,1760],{}," zelf en vergelijkt ",[244,1777,1778],{},"cert.pem"," met een opgeslagen PEM. Die callback vuurt per definitie ",[179,1781,1782],{},"alleen wanneer de validatie al is mislukt."," Toont een aanvaller een certificaat dat geldig door een vertrouwde CA is ondertekend, dan draait hij helemaal niet en wordt er nooit iets vergeleken. Als pinningmechanisme is het per ontwerp stuk.",[175,1785,1786,1789,1790,1792,1793,1796,1797,1800],{},[179,1787,1788],{},"De juiste haak draait te laat."," dio heeft er wel een — ",[244,1791,1768],{},", die bij geslaagde validatie vuurt. Maar hij draait ",[179,1794,1795],{},"nadat het verzoek de deur uit is en het antwoord al binnen is."," Het zijn niet alleen je verzoeklichaam en je ",[244,1798,1799],{},"Authorization","-header die over de lijn gingen: je hebt de antwoordstatus en de headers al geaccepteerd. Het enige wat die controle nog redt is het antwoordlichaam. Het verzoek is al gelekt.",[431,1802,1805],{":leading-icon":1803,":title":1804},"lucide:git-branch","En dan de pakketten",[175,1806,1807,1808,1812,1813,1816,1817,1820],{},"Het populairste pinningpakket op pub.dev — ongeveer honderdzestig likes en bijna vijftigduizend downloads per maand toen we in augustus 2026 keken — gebruikt een brug via een ",[277,1809,1811],{"slug":1810},"platform-channels","platform channel"," om een ",[179,1814,1815],{},"apart native verzoek"," naar je host te doen, controleert de vingerafdruk daarop, en stuurt bij een match het echte verzoek over een ",[179,1818,1819],{},"andere verbinding"," waarop helemaal geen pinning zit. De ene verbinding wordt gevalideerd; een andere draagt je gegevens. Bovendien is elk verzoek nu twee verzoeken. We noemen het niet bij naam, want het gaat niet om één beheerder: lees de broncode van welk pinningpakket je ook gebruikt, en zoek uit welke verbinding het werkelijk controleert.",[175,1822,1823,1826,1827,1830,1831,1833,1834,247,1836,1839],{},[179,1824,1825],{},"Wat je dan wel moet doen."," Op Apple-platforms is er een net antwoord: verhuis je HTTP-laag naar ",[244,1828,1829],{},"cupertino_http",". Dat is een echte ",[244,1832,1481],{},", wat betekent dat pinning declaratief is — ",[244,1835,1043],{},[244,1837,1838],{},"Info.plist",", precies als in regel 2. Door het besturingssysteem afgedwongen, op basis van SPKI, zonder code van derden.",[175,1841,1842,1843,1846,1847,1850,1851,1854],{},"Op Android bestaat geen equivalent. Cronet heeft een eigen pinning-API, maar het pakket ",[244,1844,1845],{},"cronet_http"," ",[179,1848,1849],{},"exporteert die niet",". De JNI-koppeling staat er gewoon in. Wat het pakket wel exporteert is ",[244,1852,1853],{},"enablePublicKeyPinningBypassForLocalTrustAnchors"," — de schakelaar die pinning omzeilt, niet de pinning zelf.",[175,1856,1857],{},"Er is ook één vraag die niemand op internet helder beantwoordt, en die markeren we liever dan te doen alsof: of de Network Security Config van Android geldt voor verkeer dat via Cronet loopt. Bronnen spreken elkaar tegen en er is geen officiële documentatie in welke richting dan ook. Test het zelf voordat je op een van beide antwoorden leunt.",[175,1859,1860,1861,1864,1865,1869,1870,1877,1878,1881],{},"Tot slot twee waarschuwingen specifiek voor Flutter. ",[179,1862,1863],{},"SDK's van derden"," — analyse, crashrapportage, betalingen — doen netwerkaanroepen via hun eigen native clients, en jouw pinning raakt ze niet. Dezelfde splitsing maakt ",[1499,1866,1868],{"href":1867},"\u002Fblog\u002Fdeferred-deep-linking-app-clips-install-referrer","uitgestelde deep linking op Flutter"," een probleem per platform in plaats van één implementatie. En ",[179,1871,1872,1873,1876],{},"migreer je naar ",[244,1874,1875],{},"native_dio_adapter"," om de native netwerkstack te krijgen, dan schakelt de op dio gebaseerde pinning die je uit een blogpost kopieerde stilzwijgend uit",", want ",[244,1879,1880],{},"SecurityContext"," en beide callbacks worden niet meer aangeroepen.",[200,1883,1885],{"id":1884},"hoe-je-je-pinning-in-een-half-uur-controleert","Hoe je je pinning in een half uur controleert",[175,1887,1888],{},"Doe dit fatsoenlijk, want de naïeve versie liegt tegen je.",[175,1890,1891],{},"De naïeve versie is: zet een proxy op, installeer het certificaat ervan op de telefoon, kijk of de app breekt. Hij breekt. En dat zegt niets — want zoals hierboven besproken vertrouwen apps sinds Android 7 toch al geen door de gebruiker geïnstalleerde certificaten. Je concludeert dat pinning je beschermt terwijl het besturingssysteem je de hele tijd beschermde.",[1893,1894],"how-to-schema",{":description":1895,":name":1896,":steps":1897,":total-time":1898},"Een test van 30 minuten die echte certificate pinning onderscheidt van de standaardinstellingen van het platform, apart gedraaid op Android en iOS.","Zo verifieer je certificate pinning in een mobiele app","[{\"name\": \"Bouw een releasebuild\", \"text\": \"Test de build die je gebruikers krijgen. Debugbuilds dragen vaak een soepeler netwerkbeveiligingsconfiguratie, een debug-overrides-blok, of een proxyvriendelijke HTTP-client die productie niet heeft.\"}, {\"name\": \"Installeer het proxycertificaat in de SYSTEEMOPSLAG\", \"text\": \"Niet in de gebruikersopslag. Sinds targetSdk 24 vertrouwen Android-apps geen door de gebruiker geïnstalleerde certificaten, dus een breuk in die opstelling bewijst niets over je pinning. Gebruik een emulator met een schrijfbare systeempartitie, of Magisk op een geroot toestel. Installeer op iOS het profiel en zet daarna in Instellingen volledig vertrouwen voor het hoofdcertificaat aan.\"}, {\"name\": \"Zorg dat het verkeer de proxy werkelijk bereikt\", \"text\": \"Op Flutter is dit een aparte stap: Dart negeert proxy-instellingen van het systeem, dus een proxyconfiguratie op toestelniveau vangt dart:io-verkeer niet op. Route het met een VPN-gebaseerde opname of iptables en bevestig dat je verzoeken ziet binnenkomen voordat je iets concludeert.\"}, {\"name\": \"Lees het opgenomen verkeer\", \"text\": \"Is je API-verkeer leesbaar, dan heb je geen werkende pinning, wat het configuratiebestand ook zegt. Doorloop een echte stroom achter een login, niet alleen het splashscherm, zodat je elke client dekt die de app gebruikt.\"}, {\"name\": \"Let op het patroon met twee stacks\", \"text\": \"Kijk specifiek of verkeer van externe SDK-pakketten zichtbaar is terwijl dat van jezelf dat niet is. Dat is niet je pinning die werkt; dat zijn twee verschillende netwerkstacks, waarvan je pinning er één niet dekt.\"}, {\"name\": \"Herhaal op het andere platform\", \"text\": \"Draai de hele test apart op Android en op iOS. De twee vergelijken is het hele punt: de gebruikelijke uitkomst op Flutter is pinning die op iOS standhoudt en op Android stilzwijgend niets doet.\"}]","PT30M",[1900,1901],"checklist",{":groups":1902,":subtitle":1903,":title":1904},"[{\"title\": \"Opzet\", \"items\": [\"Releasebuild, geen debug — een debug-overrides-blok maakt de hele test ongeldig\", \"Proxycertificaat geïnstalleerd in de SYSTEEMOPSLAG, niet in de gebruikersopslag\", \"Flutter: bevestigd dat verkeer de proxy bereikt via VPN-opname of iptables, want Dart negeert proxy-instellingen van het systeem\", \"Een echte stroom achter een login doorlopen, niet alleen het eerste scherm\"]}, {\"title\": \"Waar je op let\", \"items\": [\"Je API-verkeer is onleesbaar — kun je het lezen, dan heb je geen werkende pinning\", \"Verkeer van externe SDK-pakketten en je eigen verkeer gedragen zich hetzelfde, of je weet waarom niet\", \"De fout die je ziet is een pinningfout, geen fout in de vertrouwensopslag\", \"Hetzelfde resultaat op Android en op iOS\"]}, {\"title\": \"Controleer daarna de configuratie zelf\", \"items\": [\"Pins zijn SPKI-hashes, geen certificaatvingerafdrukken\", \"Minstens één reservepin, voor een offline sleutel die je beheert\", \"Android: de vervaldatum van de pinset ligt in de toekomst en is bewust gekozen\", \"Er is een noodrem die geen release in de appstore vraagt\", \"Faaltelemetrie gaat over een kanaal dat zelf niet gepind is\"]}]","Draai hem één keer per platform. Alles wat niet is afgevinkt is een bevinding.","De pinningverificatie van 30 minuten",[200,1906,1908],{"id":1907},"de-korte-versie","De korte versie",[175,1910,1911],{},"Pinning is een smalle maatregel tegen een dreiging die veel onwaarschijnlijker is geworden, met een schadestraal over je hele gebruikersbestand. Het is werkelijk noodzakelijk voor financiën, zorg en overheid. Voor alle anderen zeggen Google, Apple, Cloudflare en de helft van OWASP: niet doen, en ze hebben redenen — dezelfde redenen die HPKP de das omdeden en die de levensduur van certificaten nu naar 47 dagen terugbrengen.",[175,1913,1914],{},"Doe je het toch: de sleutel, niet het certificaat. Reservepins met een offline sleutel. Eerst waarnemen, dan pas hard afwijzen. Je eigen noodrem. En een helder beeld van wat je betaalt voor je positie in de keten.",[175,1916,1917],{},"En zit je op Flutter, zoek dan eerst uit of het überhaupt werkt. De drie waarschijnlijkste antwoorden: pinning uitgeschakeld door een datum uit 2018 die uit de documentatie van Google is gekopieerd; pinning aangegeven in een configuratiebestand dat Dart nooit leest; of pinning die een verbinding valideert waarover je gegevens niet lopen.",[175,1919,1920],{},"De blogpostversie van dit advies zegt \"zet pinning aan, bescherm je gebruikers\". De technische werkelijkheid is uitzoeken waaraan je werkelijk gepind bent, verifiëren dat het op beide platforms afgaat, en een reservesleutel in een kluis leggen. Daar maakt niemand virale video's over.",[200,1922,1924],{"id":1923},"bronnen","Bronnen",[175,1926,1927],{},"Elke bewering hierboven is te controleren, dus hier staat waar ze vandaan komt.",[205,1929,1930,1938,1956,1964,1976,1984,1992,2000,2008,2016,2023,2038,2049],{},[208,1931,1932,1933],{},"Google, \"Security with HTTPS and SSL\" — pinning \"wordt niet aanbevolen voor Android-apps\"; reservepins en \"minstens één sleutel die volledig in jouw beheer is\": ",[1499,1934,1937],{"href":1935,"rel":1936},"https:\u002F\u002Fdeveloper.android.com\u002Fprivacy-and-security\u002Fsecurity-ssl",[1503],"developer.android.com\u002Fprivacy-and-security\u002Fsecurity-ssl",[208,1939,1940,1941,1943,1944,1946,1947,1950,1951],{},"Google, \"Network security configuration\" — het attribuut ",[244,1942,1407],{},", de omzeilwaarschuwing, \"alleen ",[244,1945,916],{}," wordt ondersteund\", en het voorbeeld met ",[244,1948,1949],{},"2018-01-01",": ",[1499,1952,1955],{"href":1953,"rel":1954},"https:\u002F\u002Fdeveloper.android.com\u002Fprivacy-and-security\u002Fsecurity-config",[1503],"developer.android.com\u002Fprivacy-and-security\u002Fsecurity-config",[208,1957,1958,1959],{},"Apple, \"Identity Pinning: How to configure server certificates for your app\" — \"in de meeste gevallen is pinning niet nodig en kun je het beter vermijden\", en de aanbeveling voor CA boven blad: ",[1499,1960,1963],{"href":1961,"rel":1962},"https:\u002F\u002Fdeveloper.apple.com\u002Fnews\u002F?id=g9ejcf8y",[1503],"developer.apple.com\u002Fnews",[208,1965,1966,1967,1970,1971],{},"Ryan Sleevi op ",[244,1968,1969],{},"net-dev@chromium.org",", maart 2021 — het citaat over private CA's in zijn geheel: ",[1499,1972,1975],{"href":1973,"rel":1974},"https:\u002F\u002Fgroups.google.com\u002Fa\u002Fchromium.org\u002Fg\u002Fnet-dev\u002Fc\u002Fq_oSyjtMhts",[1503],"groups.google.com",[208,1977,1978,1979],{},"OWASP Pinning Cheat Sheet — \"Het antwoord daarop is waarschijnlijk nooit\", en de lijst van gevallen om niet te pinnen: ",[1499,1980,1983],{"href":1981,"rel":1982},"https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FPinning_Cheat_Sheet.html",[1503],"cheatsheetseries.owasp.org",[208,1985,1986,1987],{},"OWASP MASTG, netwerkcommunicatie — \"Implementeert een app geen pinning, dan hoort dat niet als kwetsbaarheid gerapporteerd te worden. Moet de app echter tegen MAS-L2 worden getoetst, dan moet het geïmplementeerd zijn\": ",[1499,1988,1991],{"href":1989,"rel":1990},"https:\u002F\u002Fgithub.com\u002FOWASP\u002Fmastg\u002Fblob\u002Fmaster\u002FDocument\u002F0x04f-Testing-Network-Communication.md",[1503],"github.com\u002FOWASP\u002Fmastg",[208,1993,1994,1995],{},"Voorstel SC-081v3 van het CA\u002FBrowser Forum — het schema 398 → 200 → 100 → 47, ingediend door Apple, aangenomen op 11 april 2025: ",[1499,1996,1999],{"href":1997,"rel":1998},"https:\u002F\u002Fcabforum.org\u002F2025\u002F04\u002F11\u002Fballot-sc081v3-introduce-schedule-of-reducing-validity-and-data-reuse-periods\u002F",[1503],"cabforum.org",[208,2001,2002,2003],{},"Let's Encrypt, \"Generation Y\" — zes tussenlagen, willekeurig gekozen \"om het pinnen van tussenlaagsleutels te ontmoedigen\": ",[1499,2004,2007],{"href":2005,"rel":2006},"https:\u002F\u002Fletsencrypt.org\u002F2025\u002F11\u002F24\u002Fgen-y-hierarchy\u002F",[1503],"letsencrypt.org",[208,2009,2010,2011],{},"Cloudflare — de twaalf niet-geautoriseerde certificaten voor 1.1.1.1, en het ontbreken van Fina in de mobiele vertrouwensopslag: ",[1499,2012,2015],{"href":2013,"rel":2014},"https:\u002F\u002Fblog.cloudflare.com\u002Funauthorized-issuance-of-certificates-for-1-1-1-1",[1503],"blog.cloudflare.com",[208,2017,2018,2019],{},"Flutter-issue #96722 — open sinds januari 2022: ",[1499,2020,2022],{"href":1501,"rel":2021},[1503],"github.com\u002Fflutter\u002Fflutter",[208,2024,2025,2026,2029,2030,2032,2033],{},"dio, ",[244,2027,2028],{},"IOHttpClientAdapter"," — waar ",[244,2031,1768],{}," werkelijk draait, in de broncode: ",[1499,2034,2037],{"href":2035,"rel":2036},"https:\u002F\u002Fgithub.com\u002Fcfug\u002Fdio\u002Fblob\u002Fmain\u002Fdio\u002Flib\u002Fsrc\u002Fadapters\u002Fio_adapter.dart",[1503],"github.com\u002Fcfug\u002Fdio",[208,2039,2040,2041,2043,2044],{},"Dart ",[244,2042,1528],{}," — de volledige lijst van wat het toont, zonder publieke sleutel: ",[1499,2045,2048],{"href":2046,"rel":2047},"https:\u002F\u002Fapi.dart.dev\u002Fstable\u002Fdart-io\u002FX509Certificate-class.html",[1503],"api.dart.dev",[208,2050,2051,1533,2053,2056,2057],{},[244,2052,1845],{},[244,2054,2055],{},"CronetEngine.build"," — de parameterlijst, mét omzeiling en zónder pinning: ",[1499,2058,2061],{"href":2059,"rel":2060},"https:\u002F\u002Fpub.dev\u002Fdocumentation\u002Fcronet_http\u002Flatest\u002Fcronet_http\u002FCronetEngine\u002Fbuild.html",[1503],"pub.dev",[2063,2064],"questions",{":items":2065},"[{\"title\": \"Moet ik certificate pinning gebruiken in mijn mobiele app?\", \"text\": \"Waarschijnlijk niet, tenzij je in financiën, zorg of overheidsdiensten zit, of je dreigingsmodel uitdrukkelijk uitgaat van een vijandig netwerk en een niet-vertrouwd toestel. Google noemt pinning niet aanbevolen voor Android-apps, Apple zegt dat het in de meeste gevallen onnodig is en beter vermeden kan worden, en de Pinning Cheat Sheet van OWASP beantwoordt de vraag of je moet pinnen met waarschijnlijk nooit. Pin niet wanneer je de pinset niet kunt bijwerken zonder een appversie uit te brengen, het sleutelpaar niet kunt kennen vóór productie, of niet zowel de server als de app beheert.\"}, {\"title\": \"Is ontbrekende SSL-pinning een echte kwetsbaarheid?\", \"text\": \"Op zichzelf niet. De Mobile Application Security Testing Guide van OWASP stelt dat een app die geen pinning implementeert niet als kwetsbaarheid gerapporteerd hoort te worden, en MASVS vereist het alleen voor apps die tegen MAS-L2 moeten worden getoetst, het profiel met verdediging in de diepte voor software die gevoelige gegevens verwerkt. Een pentestrapport dat ontbrekende SSL-pinning als bevinding markeert, is meestal de uitvoer van een geautomatiseerde checklist en geen oordeel over jouw dreigingsmodel.\"}, {\"title\": \"Wat moet ik pinnen — het certificaat, de publieke sleutel, of de CA?\", \"text\": \"De publieke sleutel, als SPKI-SHA-256-hash. Het certificaat verandert bij elke vernieuwing; de sleutel niet, dus een vernieuwing met dezelfde sleutel raakt je pin nooit. Zowel Android als iOS ondersteunt van nature alleen pinning op SPKI. Voor het niveau in de keten: een tussenlaag pinnen is bij publieke CA-hiërarchieën van tafel sinds de grootste publieke CA in 2024 tussenlagen willekeurig begon te kiezen, een publieke root is zo breed dat het bijna zinloos is, en het blad werkt alleen met reservepins. Je eigen private root is het ene duidelijk goede geval.\"}, {\"title\": \"Werkt certificate pinning in Flutter?\", \"text\": \"Niet zoals de meeste mensen het instellen. Dart gebruikt de netwerkstack van het platform niet: dart:io draait TLS via BoringSSL dat in de Flutter-engine is gecompileerd, dus een pinset in network_security_config.xml wordt op Android nooit voor Dart-verkeer geraadpleegd. Dat is Flutter-issue 96722, ingediend in januari 2022 en nog steeds open. Op iOS wordt gemeld dat het werkt omdat Dart het vertrouwensbesluit aan SecTrust overlaat, wat betekent dat de gebruikelijke uitkomst pinning is die op het ene platform standhoudt en op het andere stilzwijgend niets doet.\"}, {\"title\": \"Waarom is badCertificateCallback de verkeerde plek om in Dart te pinnen?\", \"text\": \"Omdat hij pas vuurt nadat de validatie al is mislukt. Toont een aanvaller een certificaat dat geldig door een vertrouwde CA is ondertekend, dan draait de callback nooit en wordt er geen vingerafdruk vergeleken. Het veel gekopieerde voorbeeld dat hem combineert met SecurityContext withTrustedRoots false zet bovendien het opbouwen van de keten, de verloopcontrole en de hostnaamverificatie uit. De haak validateCertificate in dio komt dichter in de buurt, maar draait nadat het verzoek is verstuurd en het antwoord is ontvangen, en beschermt dus alleen het antwoordlichaam.\"}, {\"title\": \"Wat betekenen de nieuwe certificaatlevensduren van 47 dagen voor pinning?\", \"text\": \"Ze maken bladpinning die een appversie vereist onhoudbaar. Het voorstel van het CA\u002FBrowser Forum dat in april 2025 werd aangenomen brengt de maximale levensduur van TLS-certificaten terug van 398 dagen naar 200 in maart 2026, 100 in maart 2027 en 47 in maart 2029. Zevenenveertig dagen is ongeveer acht wisselingen per jaar. Kan je pinset alleen met een release in de appstores veranderen, dan zijn dat acht gedwongen releases per jaar, elk onderhevig aan beoordeling en aan gebruikers die niet bijwerken. De sleutel pinnen in plaats van het certificaat haalt het grootste deel van die kosten weg, want een vernieuwing met dezelfde sleutel verandert de pin niet.\"}, {\"title\": \"Hoe test ik of mijn pinning werkelijk werkt?\", \"text\": \"Installeer je proxycertificaat in de vertrouwensopslag van het systeem, niet in die van de gebruiker, met een emulator met een schrijfbare systeempartitie of met Magisk, en lees daarna het opgenomen verkeer uit een releasebuild. De naïeve test met een door de gebruiker geïnstalleerd certificaat bewijst niets, want Android-apps vertrouwen sinds targetSdk 24 geen door de gebruiker geïnstalleerde certificaten. Bevestig op Flutter apart dat het verkeer de proxy überhaupt bereikt, want Dart negeert proxy-instellingen van het systeem. Draai daarna dezelfde test op het andere platform en vergelijk.\"}]",[175,2067,2068,2069,2073,2074,2078,2079,182],{},"Wil je de achtergrond bij waarvoor een certificaatautoriteit werkelijk instaat, dan schreven we dat op in ",[1499,2070,2072],{"href":2071},"\u002Fblog\u002Fhttps-free-ssl-certificate","HTTPS eenvoudig gemaakt",", en de cryptografie eronder in ",[1499,2075,2077],{"href":2076},"\u002Fblog\u002Fencryption-explained","hoe versleuteling werkt",". Dit artikel hoort bij onze video-aflevering over hetzelfde onderwerp; de vorige was ",[1499,2080,2082],{"href":2081},"\u002Fblog\u002Feveryone-got-the-timeline-wrong","iedereen had de tijdlijn mis",[175,2084,2085],{},"En vond je iets verrassends toen je de test draaide — zeker de fout op één platform — dan horen we dat werkelijk graag. Onze inzet is dat de helft van alle Flutter-apps pinning heeft die op minstens één platform faalt.",[2087,2088,2089],"post-credits",{},[175,2090,2091,2092,2096,2097,2101],{},"Ilya Nixan is oprichter en lead developer bij ",[1499,2093,2095],{"href":2094},"\u002F","Nerdy Production",", een Flutter-first bureau dat apps bouwt en onderhoudt in fintech, zorg en retail. We doen ook ",[1499,2098,2100],{"href":2099},"\u002Fservices\u002Fflutter-app-development","Flutter-appontwikkeling"," voor teams die dit liever niet in productie ontdekken.",[2103,2104,2105],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":606,"searchDepth":653,"depth":653,"links":2107},[2108,2109,2114,2117,2124,2125,2126,2127],{"id":202,"depth":653,"text":203},{"id":261,"depth":653,"text":262,"children":2110},[2111,2112,2113],{"id":302,"depth":673,"text":303},{"id":325,"depth":673,"text":326},{"id":414,"depth":673,"text":415},{"id":440,"depth":653,"text":441,"children":2115},[2116],{"id":470,"depth":673,"text":471},{"id":581,"depth":653,"text":582,"children":2118},[2119,2120,2121,2122,2123],{"id":585,"depth":673,"text":586},{"id":785,"depth":673,"text":786},{"id":1253,"depth":673,"text":1254},{"id":1362,"depth":673,"text":1363},{"id":1400,"depth":673,"text":1401},{"id":1451,"depth":653,"text":1452},{"id":1884,"depth":653,"text":1885},{"id":1907,"depth":653,"text":1908},{"id":1923,"depth":653,"text":1924},"De eigen Android-documentatie van Google zegt dat certificate pinning \"niet wordt aanbevolen voor Android-apps\".","md",{"type":2131,"url":2132},"youtube","https:\u002F\u002Fyoutu.be\u002F44y-WOyP5WQ",{},"Google en Apple raden certificate pinning af. Wat het werkelijk beschermt, vijf regels als je het uitlevert, en waarom het op Flutter stilletjes niets doet.","Waarom je certificate pinning niet werkt",true,"De helft van de apps die we beoordelen levert certificate pinning mee. Het meeste staat uit, mikt op de verkeerde dreiging, of valideert — op Flutter — een verbinding waarover de data niet loopt.","\u002Fblog\u002Fssl-certificate-pinning-flutter",{"title":169,"description":2128},"ssl-certificate-pinning-flutter","blog\u002Fssl-certificate-pinning-flutter","Google zegt dat certificate pinning \"niet wordt aanbevolen voor Android-apps\". Apple zegt \"in de meeste gevallen is pinning niet nodig en kun je het beter vermijden\". OWASP zegt inmiddels dat het antwoord op \"moet ik pinnen?\" luidt: \"waarschijnlijk nooit\". Ondertussen zit pinning in ruwweg de helft van de mobiele apps die we beoordelen — en op Flutter beschermt het meestal helemaal niets, omdat Dart de netwerkstack van het platform niet gebruikt, zodat de pinset in je network_security_config.xml nooit wordt gelezen. Dit is wat pinning je in 2026 werkelijk oplevert, of je het nodig hebt, vijf regels als je het toch doet, waarom Flutter ze allemaal breekt, en hoe je in een half uur uitzoekt welk geval op jouw app slaat.","2026-08-27T12:00:00Z","cybersecurity",null,"YrMPHpPOGVGduf8fRP2qyBIrCpTqrfNxr6O2v1X8ZMs",[2148,2205,2252,2302,2391,2443],{"id":2149,"bio":2150,"expertise":2155,"extension":2171,"links":2172,"meta":2176,"metaDescription":2177,"name":2182,"ogDescription":2185,"photo":2190,"role":2191,"seniority":2196,"skills":2197,"slug":2198,"specialization":2199,"stem":2198,"__hash__":2204},"team_members\u002Fdima.yaml",{"en":2151,"ru":2152,"es":2153,"nl":2154},"Dima has been building with [Flutter](\u002Ftechnologies\u002Fflutter) since 2021, and specializes in :term[state management]{slug=\"state-management\"} and app architecture.\n\nHe has hands-on experience with real-time communication protocols — :term{slug=\"webrtc\"} for audio and video, and :term{slug=\"xmpp\"} for messaging — which makes him comfortable with the network-heavy, stateful features many teams struggle to get right.\n","Дима работает с [Flutter](\u002Ftechnologies\u002Fflutter) с 2021 года и специализируется на :term[state management]{slug=\"state-management\"} и архитектуре приложений.\n\nУ него есть практический опыт с протоколами реального времени — :term{slug=\"webrtc\"} для аудио и видео и :term{slug=\"xmpp\"} для обмена сообщениями, — поэтому ему близки сетевые stateful-фичи, которые многим командам даются с трудом.\n","Dima trabaja con [Flutter](\u002Ftechnologies\u002Fflutter) desde 2021 y se especializa en :term[gestión de estado]{slug=\"state-management\"} y arquitectura de aplicaciones.\n\nTiene experiencia práctica con protocolos de comunicación en tiempo real —:term{slug=\"webrtc\"} para audio y vídeo, y :term{slug=\"xmpp\"} para mensajería—, lo que le hace sentirse cómodo con esas funcionalidades con mucho estado y mucha red que a muchos equipos se les atragantan.\n","Dima werkt sinds 2021 met [Flutter](\u002Ftechnologies\u002Fflutter) en is gespecialiseerd in :term[state management]{slug=\"state-management\"} en applicatiearchitectuur.\n\nHij heeft praktijkervaring met protocollen voor realtime communicatie — :term{slug=\"webrtc\"} voor audio en video, en :term{slug=\"xmpp\"} voor berichten — waardoor hij zich thuis voelt bij de netwerkzware functies met veel staat waar veel teams moeite mee hebben.\n",[2156,2159,2164,2166],{"en":2157,"ru":2157,"es":2158,"nl":2157},"State management","Gestión de estado",{"en":2160,"ru":2161,"es":2162,"nl":2163},"Application architecture","Архитектура приложений","Arquitectura de aplicaciones","Applicatiearchitectuur",{"en":2165,"ru":2165,"es":2165,"nl":2165},"WebRTC",{"en":2167,"ru":2168,"es":2169,"nl":2170},"XMPP messaging","Обмен сообщениями по XMPP","Mensajería XMPP","Berichten via XMPP","yaml",[2173],{"type":2174,"address":2175},"email","konopatov@nerdy.pro",{},{"en":2178,"ru":2179,"es":2180,"nl":2181},"Lead Flutter developer at Nerdy Production, building real-time apps since 2021 — state management, app architecture, WebRTC and XMPP.","Ведущий Flutter-разработчик Nerdy Production: приложения реального времени с 2021 года — state management, архитектура, WebRTC и XMPP.","Lead de desarrollo Flutter en Nerdy Production, construyendo apps en tiempo real desde 2021: gestión de estado, arquitectura de aplicaciones, WebRTC y XMPP.","Lead Flutter-developer bij Nerdy Production, bouwt sinds 2021 realtime-apps — state management, applicatiearchitectuur, WebRTC en XMPP.",{"en":2183,"ru":2184,"es":2183,"nl":2183},"Dima","Дима",{"en":2186,"ru":2187,"es":2188,"nl":2189},"Lead Flutter developer at Nerdy Production — real-time apps since 2021, and the state management that keeps them from falling over.","Ведущий Flutter-разработчик Nerdy Production: приложения реального времени с 2021 года и state management, на котором они держатся.","Lead de desarrollo Flutter en Nerdy Production: apps en tiempo real desde 2021 y la gestión de estado que evita que se caigan.","Lead Flutter-developer bij Nerdy Production — realtime-apps sinds 2021, en het state management dat ze overeind houdt.","\u002Fteam\u002Fdima.webp",{"en":2192,"ru":2193,"es":2194,"nl":2195},"Lead Flutter Developer","Ведущий Flutter-разработчик","Lead de desarrollo Flutter","Lead Flutter-developer","lead",[41,18,155,160,90,111,150,95],"dima",{"en":2200,"ru":2201,"es":2202,"nl":2203},"Real-time apps, state management, and architecture","Приложения реального времени, state management и архитектура","Apps en tiempo real, gestión de estado y arquitectura","Realtime-apps, state management en architectuur","1TuxUt3zjdVkQUvMf9dBITG872C0exrdZyyoKw1beiE",{"id":2206,"bio":2207,"expertise":2212,"extension":2171,"links":2145,"meta":2223,"metaDescription":2224,"name":2229,"ogDescription":2232,"photo":2237,"role":2238,"seniority":2243,"skills":2244,"slug":2245,"specialization":2246,"stem":2245,"__hash__":2251},"team_members\u002Fmasha.yaml",{"en":2208,"ru":2209,"es":2210,"nl":2211},"Masha builds [Flutter](\u002Ftechnologies\u002Fflutter) apps where design and copy are treated as one job.\n\nShe specializes in UI\u002FUX — turning product requirements into clean, usable interfaces — and in the writing inside the app, from onboarding flows to the microcopy that makes a screen make sense. The result is apps that feel considered, not just functional.\n","Маша делает приложения на [Flutter](\u002Ftechnologies\u002Fflutter), где дизайн и текст — одна задача.\n\nОна специализируется на UI\u002FUX, превращая продуктовые требования в чистые и удобные интерфейсы, и на текстах внутри приложения — от онбординга до микрокопирайта, который делает экран понятным. В итоге приложения получаются продуманными, а не просто рабочими.\n","Masha crea apps en [Flutter](\u002Ftechnologies\u002Fflutter) donde el diseño y el texto se tratan como un mismo trabajo.\n\nSe especializa en UI\u002FUX —convertir requisitos de producto en interfaces limpias y usables— y en la escritura dentro de la app, desde los flujos de onboarding hasta el microcopy que hace que una pantalla se entienda. El resultado son apps que se sienten pensadas, no solo funcionales.\n","Masha bouwt [Flutter](\u002Ftechnologies\u002Fflutter)-apps waarin ontwerp en tekst als één taak worden behandeld.\n\nZe is gespecialiseerd in UI\u002FUX — productwensen omzetten in heldere, bruikbare interfaces — en in de teksten binnen de app, van onboarding tot de microteksten die een scherm begrijpelijk maken. Het resultaat zijn apps die doordacht aanvoelen en niet alleen werken.\n",[2213,2218],{"en":2214,"ru":2215,"es":2216,"nl":2217},"UI\u002FUX design","UI\u002FUX-дизайн","Diseño UI\u002FUX","UI\u002FUX-ontwerp",{"en":2219,"ru":2220,"es":2221,"nl":2222},"Product copywriting","Продуктовый копирайтинг","Redacción de producto","Productteksten",{},{"en":2225,"ru":2226,"es":2227,"nl":2228},"Flutter developer at Nerdy Production working where design meets copy — UI\u002FUX, onboarding flows, and the microcopy that makes a screen make sense.","Flutter-разработчик Nerdy Production на стыке дизайна и текста: UI\u002FUX, онбординг и микрокопирайт, который делает экран понятным.","Desarrolladora Flutter en Nerdy Production, donde el diseño se encuentra con el texto: UI\u002FUX, onboarding y el microcopy que hace que una pantalla se entienda.","Flutter-developer bij Nerdy Production op het snijvlak van ontwerp en tekst — UI\u002FUX, onboarding, en de microteksten die een scherm begrijpelijk maken.",{"en":2230,"ru":2231,"es":2230,"nl":2230},"Masha","Маша",{"en":2233,"ru":2234,"es":2235,"nl":2236},"Flutter developer at Nerdy Production working where design meets copy — onboarding flows and the microcopy that makes a screen make sense.","Flutter-разработчик Nerdy Production на стыке дизайна и текста: онбординг и микрокопирайт, который делает экран понятным.","Desarrolladora Flutter en Nerdy Production, donde el diseño se encuentra con el texto: onboarding y el microcopy que hace entender una pantalla.","Flutter-developer bij Nerdy Production op het snijvlak van ontwerp en tekst — onboarding en de microteksten die een scherm begrijpelijk maken.","\u002Fteam\u002Fmasha.webp",{"en":2239,"ru":2240,"es":2241,"nl":2242},"Flutter Developer","Flutter-разработчик","Desarrolladora Flutter","Flutter-developer","middle",[41,18],"masha",{"en":2247,"ru":2248,"es":2249,"nl":2250},"UI\u002FUX and copywriting for Flutter apps","UI\u002FUX и копирайтинг для Flutter-приложений","UI\u002FUX y redacción para apps Flutter","UI\u002FUX en teksten voor Flutter-apps","z3c64iWR39RIrn8wE0elSy00IfCITYgNkbgp_G_sslk",{"id":2253,"bio":2254,"expertise":2259,"extension":2171,"links":2270,"meta":2273,"metaDescription":2274,"name":2279,"ogDescription":2282,"photo":2287,"role":2288,"seniority":2293,"skills":2294,"slug":2295,"specialization":2296,"stem":2295,"__hash__":2301},"team_members\u002Fmaxim.yaml",{"en":2255,"ru":2256,"es":2257,"nl":2258},"Maxim is a polyglot engineer who moves comfortably across [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython), and [TypeScript](\u002Ftechnologies\u002Ftypescript), which lets him own a feature from the backend to the screen.\n\nHe came up at Ozon, one of Russia's largest marketplaces, where scale makes reliability non-negotiable — and it shows in his work: testing is a first-class part of how he builds, not an afterthought bolted on at the end.\n","Максим — полиглот-инженер, свободно работающий с [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython) и [TypeScript](\u002Ftechnologies\u002Ftypescript), что позволяет ему вести фичу от бэкенда до экрана.\n\nОн вырос в Ozon, одном из крупнейших маркетплейсов России, где масштаб делает надёжность обязательной, — и это видно в его работе: тестирование для него первоклассная часть разработки, а не то, что прикручивают в конце.\n","Maxim es un ingeniero políglota que se mueve con soltura entre [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython) y [TypeScript](\u002Ftechnologies\u002Ftypescript), lo que le permite hacerse cargo de una funcionalidad desde el backend hasta la pantalla.\n\nSe formó en Ozon, uno de los mayores marketplaces de Rusia, donde la escala hace que la fiabilidad no sea negociable, y se le nota: el testing es para él una parte de primera clase de cómo construye, no un añadido al final.\n","Maxim is een polyglotte engineer die zich moeiteloos beweegt tussen [Go](\u002Ftechnologies\u002Fgo), [Flutter](\u002Ftechnologies\u002Fflutter), [Python](\u002Ftechnologies\u002Fpython) en [TypeScript](\u002Ftechnologies\u002Ftypescript), waardoor hij een functie van de backend tot het scherm kan dragen.\n\nHij is opgegroeid bij Ozon, een van de grootste marktplaatsen van Rusland, waar schaal betrouwbaarheid onderhandelbaar maakt noch toestaat — en dat zie je terug in zijn werk: testen is bij hem een volwaardig onderdeel van hoe hij bouwt, geen bijzaak die er aan het eind bij komt.\n",[2260,2265],{"en":2261,"ru":2262,"es":2263,"nl":2264},"Automated testing","Автоматизированное тестирование","Testing automatizado","Geautomatiseerd testen",{"en":2266,"ru":2267,"es":2268,"nl":2269},"High-load backend systems","Высоконагруженные бэкенд-системы","Sistemas backend de alta carga","Backendsystemen met hoge belasting",[2271],{"type":2174,"address":2272},"maxim@nerdy.pro",{},{"en":2275,"ru":2276,"es":2277,"nl":2278},"Senior engineer at Nerdy Production across Go, Flutter, Python and TypeScript — from backend to screen, with automated testing built in.","Старший инженер Nerdy Production: Go, Flutter, Python и TypeScript — от бэкенда до экрана, с автотестами как частью разработки.","Ingeniero senior en Nerdy Production con Go, Flutter, Python y TypeScript: del backend a la pantalla, con testing automatizado incorporado.","Senior engineer bij Nerdy Production met Go, Flutter, Python en TypeScript — van backend tot scherm, met geautomatiseerd testen ingebouwd.",{"en":2280,"ru":2281,"es":2280,"nl":2280},"Maxim","Максим",{"en":2283,"ru":2284,"es":2285,"nl":2286},"Senior engineer at Nerdy Production working from backend to screen — Go, Flutter, Python and TypeScript, with the tests written as he goes.","Старший инженер Nerdy Production, работающий от бэкенда до экрана: Go, Flutter, Python и TypeScript — и тесты, которые пишутся по ходу дела.","Ingeniero senior en Nerdy Production, del backend a la pantalla: Go, Flutter, Python y TypeScript, con los tests escritos sobre la marcha.","Senior engineer bij Nerdy Production, van backend tot scherm — Go, Flutter, Python en TypeScript, met de tests die hij onderweg schrijft.","\u002Fteam\u002Fmax.webp",{"en":2289,"ru":2290,"es":2291,"nl":2292},"Senior Software Engineer","Старший инженер-программист","Ingeniero de software senior","Senior software engineer","senior",[41,18,51,106,155,13,150],"maxim",{"en":2297,"ru":2298,"es":2299,"nl":2300},"Backend and Flutter engineering with a testing focus","Бэкенд и Flutter с фокусом на тестирование","Ingeniería backend y Flutter con foco en testing","Backend- en Flutter-engineering met focus op testen","a-8Kx1i-PQXIGU92_Rimqr3bPEur6ZwMfPKQWr2uk4I",{"id":2303,"bio":2304,"expertise":2309,"extension":2171,"links":2351,"meta":2363,"metaDescription":2364,"name":2369,"ogDescription":2372,"photo":2377,"role":2378,"seniority":2383,"skills":2384,"slug":170,"specialization":2385,"stem":170,"__hash__":2390},"team_members\u002Fnixan.yaml",{"en":2305,"ru":2306,"es":2307,"nl":2308},"Ilya founded Nerdy Production and leads its engineering. He has been building software since 2010 and shipping production Flutter since 2018.\n\nBefore that he was CTO of QIWI, one of Russia's largest payment platforms, where he ran roughly 12 engineering teams spanning web products down to card processing, :term{slug=\"pci-dss\"} scope, and contactless payments — including building contactless card payments on Android via :term[Host Card Emulation]{slug=\"host-card-emulation\"} over ISO\u002FIEC 14443, with EMV Contactless (Visa PayWave) on top.\n\nHe was also a principal developer at Yandex, where he worked on Yandex.Auto — taking native Android deep into the vehicle, with heavy CAN-bus integration through a custom CAN shield — and a principal at Evotor, whose point-of-sale devices run on a forked :term{slug=\"aosp\"}, giving him a low-level view of Android most app developers never touch.\n\nToday he leads delivery on the agency's flagship apps — from the chart-heavy fintech UI of [ExtraETF](\u002Fportfolio\u002Fextraetf) to the fully custom design system of [Arcana](\u002Fportfolio\u002Farcana). He writes most of the essays on this blog and maintains the agency's open-source work, including the [dxpdf](\u002Fopen-source\u002Fdxpdf) DOCX-to-PDF engine.\n\nHe works across [Flutter](\u002Ftechnologies\u002Fflutter), native iOS and Android, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes), and [Docker](\u002Ftechnologies\u002Fdocker), with a focus on app architecture, cross-platform delivery, and building teams that ship.\n","Илья основал Nerdy Production и руководит инженерной командой. Он занимается разработкой с 2010 года и выпускает продакшн-приложения на Flutter с 2018-го.\n\nДо этого он был CTO QIWI — одной из крупнейших платёжных платформ России, — где руководил примерно 12 инженерными командами: от веб-продуктов до карточного процессинга, зоны :term{slug=\"pci-dss\"} и бесконтактных платежей, включая бесконтактную оплату картой на Android через :term[Host Card Emulation]{slug=\"host-card-emulation\"} поверх ISO\u002FIEC 14443, с платёжным протоколом EMV Contactless (Visa PayWave).\n\nОн также был принципал-разработчиком в Яндексе, где работал над Яндекс.Авто, уводя нативный Android глубоко в автомобиль, с серьёзной интеграцией по шине CAN через собственный CAN-шилд, и принципалом в Эвоторе, чьи кассовые устройства работают на форке :term{slug=\"aosp\"}, что дало ему низкоуровневый взгляд на Android, недоступный большинству прикладных разработчиков.\n\nСейчас он ведёт поставку флагманских приложений агентства — от насыщенного графиками финтех-интерфейса [ExtraETF](\u002Fportfolio\u002Fextraetf) до полностью кастомной дизайн-системы [Arcana](\u002Fportfolio\u002Farcana). Он пишет большую часть материалов этого блога и поддерживает open-source агентства, включая движок [dxpdf](\u002Fopen-source\u002Fdxpdf) для конвертации DOCX в PDF.\n\nРаботает с [Flutter](\u002Ftechnologies\u002Fflutter), нативными iOS и Android, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes) и [Docker](\u002Ftechnologies\u002Fdocker); его фокус — архитектура приложений, кросс-платформенная поставка и построение команд, которые доводят продукт до релиза.\n","Ilya fundó Nerdy Production y dirige su ingeniería. Lleva construyendo software desde 2010 y entregando Flutter en producción desde 2018.\n\nAntes fue CTO de QIWI, una de las mayores plataformas de pago de Rusia, donde dirigió alrededor de 12 equipos de ingeniería que abarcaban desde productos web hasta procesamiento de tarjetas, alcance :term{slug=\"pci-dss\"} y pagos contactless, incluida la construcción de pagos contactless con tarjeta en Android mediante :term[Host Card Emulation]{slug=\"host-card-emulation\"} sobre ISO\u002FIEC 14443, con EMV Contactless (Visa PayWave) por encima.\n\nTambién fue principal developer en Yandex, donde trabajó en Yandex.Auto —llevando Android nativo hasta el interior del vehículo, con una integración intensiva por bus CAN a través de un CAN shield propio— y principal en Evotor, cuyos terminales de punto de venta funcionan sobre un fork de :term{slug=\"aosp\"}, lo que le dio una visión de bajo nivel de Android que la mayoría de desarrolladores de apps nunca llega a tocar.\n\nHoy lidera la entrega de las apps insignia de la agencia, desde la interfaz fintech cargada de gráficos de [ExtraETF](\u002Fportfolio\u002Fextraetf) hasta el sistema de diseño totalmente a medida de [Arcana](\u002Fportfolio\u002Farcana). Escribe la mayoría de los artículos de este blog y mantiene el trabajo de código abierto de la agencia, incluido el motor de conversión de DOCX a PDF [dxpdf](\u002Fopen-source\u002Fdxpdf).\n\nTrabaja con [Flutter](\u002Ftechnologies\u002Fflutter), iOS y Android nativos, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes) y [Docker](\u002Ftechnologies\u002Fdocker), con foco en arquitectura de aplicaciones, entrega multiplataforma y la construcción de equipos que entregan.\n","Ilya richtte Nerdy Production op en leidt de engineering. Hij bouwt software sinds 2010 en levert sinds 2018 Flutter in productie.\n\nDaarvoor was hij CTO van QIWI, een van de grootste betaalplatforms van Rusland, waar hij zo'n 12 engineeringteams aanstuurde die reikten van webproducten tot kaartverwerking, :term{slug=\"pci-dss\"}-scope en contactloos betalen — waaronder het bouwen van contactloze kaartbetalingen op Android via :term[Host Card Emulation]{slug=\"host-card-emulation\"} over ISO\u002FIEC 14443, met EMV Contactless (Visa PayWave) daarbovenop.\n\nHij was ook principal developer bij Yandex, waar hij aan Yandex.Auto werkte — native Android diep de auto in brengen, met zware integratie over de CAN-bus via een eigen CAN-shield — en principal bij Evotor, waarvan de kassa-apparaten op een fork van :term{slug=\"aosp\"} draaien, wat hem een blik op Android op laag niveau gaf die de meeste app-ontwikkelaars nooit krijgen.\n\nVandaag leidt hij de oplevering van de vlaggenschipapps van het bureau — van de grafiekzware fintech-UI van [ExtraETF](\u002Fportfolio\u002Fextraetf) tot het volledig eigen designsysteem van [Arcana](\u002Fportfolio\u002Farcana). Hij schrijft de meeste artikelen op deze blog en onderhoudt het opensourcewerk van het bureau, waaronder de DOCX-naar-PDF-motor [dxpdf](\u002Fopen-source\u002Fdxpdf).\n\nHij werkt met [Flutter](\u002Ftechnologies\u002Fflutter), native iOS en Android, [Go](\u002Ftechnologies\u002Fgo), [Rust](\u002Ftechnologies\u002Frust), [TypeScript](\u002Ftechnologies\u002Ftypescript), [Kotlin](\u002Ftechnologies\u002Fkotlin), [Kubernetes](\u002Ftechnologies\u002Fkubernetes) en [Docker](\u002Ftechnologies\u002Fdocker), met de nadruk op applicatiearchitectuur, cross-platform oplevering en het bouwen van teams die opleveren.\n",[2310,2315,2320,2325,2330,2335,2340,2345,2347,2349],{"en":2311,"ru":2312,"es":2313,"nl":2314},"iOS development","Разработка под iOS","Desarrollo iOS","iOS-ontwikkeling",{"en":2316,"ru":2317,"es":2318,"nl":2319},"Software architecture","Архитектура ПО","Arquitectura de software","Softwarearchitectuur",{"en":2321,"ru":2322,"es":2323,"nl":2324},"Engineering team leadership","Руководство инженерными командами","Liderazgo de equipos de ingeniería","Leidinggeven aan engineeringteams",{"en":2326,"ru":2327,"es":2328,"nl":2329},"Payment systems","Платёжные системы","Sistemas de pago","Betaalsystemen",{"en":2331,"ru":2332,"es":2333,"nl":2334},"Card processing","Карточный процессинг","Procesamiento de tarjetas","Kaartverwerking",{"en":2336,"ru":2337,"es":2338,"nl":2339},"PCI-DSS compliance","Соответствие PCI-DSS","Cumplimiento de PCI-DSS","Naleving van PCI-DSS",{"en":2341,"ru":2342,"es":2343,"nl":2344},"NFC and contactless payments","NFC и бесконтактные платежи","NFC y pagos contactless","NFC en contactloos betalen",{"en":2346,"ru":2346,"es":2346,"nl":2346},"Host Card Emulation",{"en":2348,"ru":2348,"es":2348,"nl":2348},"EMV Contactless",{"en":2350,"ru":2350,"es":2350,"nl":2350},"AOSP",[2352,2355,2358,2361],{"type":2353,"url":2354},"github","https:\u002F\u002Fgithub.com\u002Fthenixan",{"type":2356,"url":2357},"linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fthenixan\u002F",{"type":2359,"url":2360},"telegram","https:\u002F\u002Ft.me\u002Fthenixan",{"type":2174,"address":2362},"nixan@nerdy.pro",{},{"en":2365,"ru":2366,"es":2367,"nl":2368},"Founder and lead developer at Nerdy Production, former CTO of QIWI. Flutter architecture, payments infrastructure, and engineering teams that ship.","Основатель и ведущий разработчик Nerdy Production, экс-CTO QIWI. Архитектура на Flutter, платёжная инфраструктура и команды, которые доводят до релиза.","Fundador y lead developer en Nerdy Production, ex-CTO de QIWI. Arquitectura Flutter, infraestructura de pagos y equipos de ingeniería que entregan.","Oprichter en lead developer bij Nerdy Production, oud-CTO van QIWI. Flutter-architectuur, betaalinfrastructuur en teams die opleveren.",{"en":2370,"ru":2371,"es":2370,"nl":2370},"Ilya Nixan","Илья Никсан",{"en":2373,"ru":2374,"es":2375,"nl":2376},"Founder and lead developer at Nerdy Production, former CTO of QIWI — Flutter architecture, payments infrastructure, and teams that ship.","Основатель и ведущий разработчик Nerdy Production, экс-CTO QIWI: архитектура на Flutter, платёжная инфраструктура и команды, которые доводят до релиза.","Fundador y lead developer en Nerdy Production, ex-CTO de QIWI: arquitectura Flutter, infraestructura de pagos y equipos que entregan.","Oprichter en lead developer bij Nerdy Production, oud-CTO van QIWI — Flutter-architectuur, betaalinfrastructuur en teams die opleveren.","\u002Fteam\u002Fnixan.webp",{"en":2379,"ru":2380,"es":2381,"nl":2382},"Founder & Lead Developer","Основатель и ведущий разработчик","Fundador y lead developer","Oprichter & lead developer","founder_lead",[41,18,71,141,76,146,51,126,121,155,160,90,13,150,136,7,29,80],{"en":2386,"ru":2387,"es":2388,"nl":2389},"Flutter architecture and leading delivery teams","Архитектура на Flutter и руководство командами поставки","Arquitectura Flutter y liderazgo de equipos de entrega","Flutter-architectuur en het leiden van opleverteams","BmeQZ7JyhRFIx8527WO2t1-WklP8AAvevAxiN9U4TEs",{"id":2392,"bio":2393,"expertise":2398,"extension":2171,"links":2410,"meta":2415,"metaDescription":2416,"name":2421,"ogDescription":2424,"photo":2429,"role":2430,"seniority":2293,"skills":2435,"slug":2436,"specialization":2437,"stem":2436,"__hash__":2442},"team_members\u002Froma.yaml",{"en":2394,"ru":2395,"es":2396,"nl":2397},"Roman works across native iOS and Android and [Flutter](\u002Ftechnologies\u002Fflutter), and pairs that mobile depth with [Python](\u002Ftechnologies\u002Fpython) and applied AI.\n\nMuch of his background is in ERP and CRM development — complex, data-heavy business systems where getting the domain model right matters more than the UI — which gives him a pragmatic eye for how an app fits the process behind it.\n","Роман работает с нативными iOS и Android и с [Flutter](\u002Ftechnologies\u002Fflutter), дополняя мобильную экспертизу [Python](\u002Ftechnologies\u002Fpython) и прикладным AI.\n\nЗначительная часть его опыта — разработка ERP и CRM, сложных систем с большим объёмом данных, где правильная доменная модель важнее интерфейса, — что даёт ему прагматичный взгляд на то, как приложение встраивается в процесс за ним.\n","Roman trabaja con iOS y Android nativos y con [Flutter](\u002Ftechnologies\u002Fflutter), y combina esa profundidad móvil con [Python](\u002Ftechnologies\u002Fpython) e IA aplicada.\n\nBuena parte de su trayectoria está en el desarrollo de ERP y CRM —sistemas de negocio complejos y con mucho dato, donde acertar con el modelo de dominio importa más que la interfaz—, lo que le da una mirada pragmática sobre cómo encaja una app en el proceso que hay detrás.\n","Roman werkt met native iOS en Android en met [Flutter](\u002Ftechnologies\u002Fflutter), en combineert die mobiele diepgang met [Python](\u002Ftechnologies\u002Fpython) en toegepaste AI.\n\nEen groot deel van zijn achtergrond ligt in ERP- en CRM-ontwikkeling — complexe, datazware bedrijfssystemen waarin het domeinmodel goed krijgen zwaarder weegt dan de interface — wat hem een pragmatische blik geeft op hoe een app in het proces erachter past.\n",[2399,2400,2405],{"en":2311,"ru":2312,"es":2313,"nl":2314},{"en":2401,"ru":2402,"es":2403,"nl":2404},"Applied AI","Прикладной AI","IA aplicada","Toegepaste AI",{"en":2406,"ru":2407,"es":2408,"nl":2409},"ERP and CRM systems","ERP- и CRM-системы","Sistemas ERP y CRM","ERP- en CRM-systemen",[2411,2413],{"type":2356,"url":2412},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fromanbatler\u002F",{"type":2174,"address":2414},"roma@nerdy.pro",{},{"en":2417,"ru":2418,"es":2419,"nl":2420},"Senior mobile developer at Nerdy Production — native iOS and Android, Flutter, applied AI, and a background in data-heavy ERP and CRM systems.","Старший мобильный разработчик Nerdy Production: нативные iOS и Android, Flutter, прикладной AI и опыт в ERP- и CRM-системах.","Desarrollador móvil senior en Nerdy Production: iOS y Android nativos, Flutter, IA aplicada y experiencia en sistemas ERP y CRM con gran volumen de datos.","Senior mobiele developer bij Nerdy Production — native iOS en Android, Flutter, toegepaste AI, en een achtergrond in datazware ERP- en CRM-systemen.",{"en":2422,"ru":2423,"es":2422,"nl":2422},"Roman","Рома",{"en":2425,"ru":2426,"es":2427,"nl":2428},"Senior mobile developer at Nerdy Production — native iOS and Android, Flutter, applied AI, and years of data-heavy ERP and CRM behind him.","Старший мобильный разработчик Nerdy Production: нативные iOS и Android, Flutter, прикладной AI и годы работы с нагруженными ERP и CRM.","Desarrollador móvil senior en Nerdy Production: iOS y Android nativos, Flutter, IA aplicada y años de ERP y CRM con mucha carga de datos.","Senior mobiele developer bij Nerdy Production — native iOS en Android, Flutter, toegepaste AI, en jaren datazware ERP en CRM achter zich.","\u002Fteam\u002Froma.webp",{"en":2431,"ru":2432,"es":2433,"nl":2434},"Senior Mobile Developer","Старший мобильный разработчик","Desarrollador móvil senior","Senior mobiele developer",[7,41,18,141,146,106],"roma",{"en":2438,"ru":2439,"es":2440,"nl":2441},"Mobile, AI, and ERP\u002FCRM systems","Мобильная разработка, AI и системы ERP\u002FCRM","Móvil, IA y sistemas ERP\u002FCRM","Mobiel, AI en ERP\u002FCRM-systemen","caPSPO2BX35G9lEjNb6p3U4uF_V7yh8cexCe6l5hm8Y",{"id":2444,"bio":2445,"expertise":2450,"extension":2171,"links":2145,"meta":2461,"metaDescription":2462,"name":2467,"ogDescription":2470,"photo":2475,"role":2476,"seniority":2293,"skills":2481,"slug":2482,"specialization":2483,"stem":2482,"__hash__":2488},"team_members\u002Fxsox.yaml",{"en":2446,"ru":2447,"es":2448,"nl":2449},"Eugene builds the systems that apps depend on. His background is in backend engineering — designing and running the APIs, data models, and services behind a product, primarily in [Python](\u002Ftechnologies\u002Fpython) and [Django](\u002Ftechnologies\u002Fdjango).\n\nHe also works in [Flutter](\u002Ftechnologies\u002Fflutter), so he can reason about a feature from the database to the device and build the backend so the app that consumes it stays simple.\n","Женя строит системы, на которые опираются приложения. Его основной опыт — бэкенд-инженерия: проектирование и эксплуатация API, моделей данных и сервисов за продуктом, в первую очередь на [Python](\u002Ftechnologies\u002Fpython) и [Django](\u002Ftechnologies\u002Fdjango).\n\nОн также работает с [Flutter](\u002Ftechnologies\u002Fflutter), поэтому может рассуждать о фиче от базы данных до устройства и строить бэкенд так, чтобы потребляющее его приложение оставалось простым.\n","Eugene construye los sistemas de los que dependen las apps. Su trayectoria está en la ingeniería backend: diseñar y operar las APIs, los modelos de datos y los servicios que hay detrás de un producto, principalmente en [Python](\u002Ftechnologies\u002Fpython) y [Django](\u002Ftechnologies\u002Fdjango).\n\nTambién trabaja con [Flutter](\u002Ftechnologies\u002Fflutter), así que puede razonar sobre una funcionalidad desde la base de datos hasta el dispositivo y construir el backend de forma que la app que lo consume siga siendo simple.\n","Eugene bouwt de systemen waar apps op leunen. Zijn achtergrond ligt in backend-engineering: de API's, datamodellen en services achter een product ontwerpen en draaien, vooral in [Python](\u002Ftechnologies\u002Fpython) en [Django](\u002Ftechnologies\u002Fdjango).\n\nHij werkt daarnaast met [Flutter](\u002Ftechnologies\u002Fflutter), zodat hij over een functie kan nadenken van de database tot het toestel en de backend zo kan bouwen dat de app die hem verbruikt eenvoudig blijft.\n",[2451,2456],{"en":2452,"ru":2453,"es":2454,"nl":2455},"API design","Проектирование API","Diseño de APIs","API-ontwerp",{"en":2457,"ru":2458,"es":2459,"nl":2460},"Data modelling","Моделирование данных","Modelado de datos","Datamodellering",{},{"en":2463,"ru":2464,"es":2465,"nl":2466},"Senior backend developer at Nerdy Production building the APIs, data models and services behind our apps, primarily in Python and Django.","Старший бэкенд-разработчик Nerdy Production: API, модели данных и сервисы за нашими приложениями, в первую очередь на Python и Django.","Desarrollador backend senior en Nerdy Production: las APIs, modelos de datos y servicios detrás de nuestras apps, sobre todo en Python y Django.","Senior backenddeveloper bij Nerdy Production die de API's, datamodellen en services achter onze apps bouwt, vooral in Python en Django.",{"en":2468,"ru":2469,"es":2468,"nl":2468},"Eugene Xsox","Женя Xsox",{"en":2471,"ru":2472,"es":2473,"nl":2474},"Senior backend developer at Nerdy Production — the APIs, data models and services our apps run on, mostly in Python and Django.","Старший бэкенд-разработчик Nerdy Production: API, модели данных и сервисы, на которых работают наши приложения, в основном на Python и Django.","Desarrollador backend senior en Nerdy Production: las APIs, modelos de datos y servicios sobre los que corren nuestras apps, en Python y Django.","Senior backenddeveloper bij Nerdy Production — de API's, datamodellen en services waarop onze apps draaien, vooral in Python en Django.","\u002Fteam\u002Fxsox.webp",{"en":2477,"ru":2478,"es":2479,"nl":2480},"Senior Backend Developer","Старший бэкенд-разработчик","Desarrollador backend senior","Senior backenddeveloper",[106,24,41,18,155,160,90,150],"xsox",{"en":2484,"ru":2485,"es":2486,"nl":2487},"Python\u002FDjango backends, plus Flutter","Бэкенды на Python\u002FDjango и Flutter","Backends en Python\u002FDjango, además de Flutter","Backends in Python\u002FDjango, plus Flutter","w_QAKKq_Mh_dtMvAxFpAJBC10PW1I7zgVOxELwRHwO8",[2490,2498,2505,2510,2520],{"id":2491,"extension":2171,"meta":2492,"name":2493,"slug":2144,"stem":2144,"__hash__":2497},"blog_topics\u002Fcybersecurity.yaml",{},{"en":2494,"ru":2495,"es":2496,"nl":2494},"Cybersecurity","Кибербезопасность","Ciberseguridad","kt1rdsMy5W4b9MvGHm6mZVCr0inHidEuqaosPYmmk4E",{"id":2499,"extension":2171,"meta":2500,"name":2501,"slug":2503,"stem":2503,"__hash__":2504},"blog_topics\u002Fdevops.yaml",{},{"en":2502,"ru":2502,"es":2502,"nl":2502},"DevOps","devops","D3mxlZuFHKCGeszKZuWCiQmHmP5EiCvzg1qEAIQhnyU",{"id":2506,"extension":2171,"meta":2507,"name":2508,"slug":41,"stem":41,"__hash__":2509},"blog_topics\u002Fflutter.yaml",{},{"en":39,"ru":39,"es":39,"nl":39},"OOeALAmwFuEqByuJvTGk4g15FIHENPfYMdpfZh_HTAE",{"id":2511,"extension":2171,"meta":2512,"name":2513,"slug":2518,"stem":2518,"__hash__":2519},"blog_topics\u002Ffor-founders.yaml",{},{"en":2514,"ru":2515,"es":2516,"nl":2517},"For Founders","Для основателей","Para fundadores","Voor oprichters","for-founders","lp2u6Ol2qF4DGxpB83HxbbGzWioT-e40mJhD4lr28-U",{"id":2521,"extension":2171,"meta":2522,"name":2523,"slug":2528,"stem":2528,"__hash__":2529},"blog_topics\u002Fsoftware-engineering.yaml",{},{"en":2524,"ru":2525,"es":2526,"nl":2527},"Software Engineering","Разработка ПО","Ingeniería de software","Software-engineering","software-engineering","aZMb3aUDptoFQk_cEE6l5H4wrYcgZuBYUubmKqOAaaU",{"data":2531,"body":2532},{},{"type":2533,"children":2534},"root",[2535,2542,2560,2571,2599],{"type":2536,"tag":175,"props":2537,"children":2538},"element",{},[2539],{"type":2540,"value":2541},"text","Ilya richtte Nerdy Production op en leidt de engineering. Hij bouwt software sinds 2010 en levert sinds 2018 Flutter in productie.",{"type":2536,"tag":175,"props":2543,"children":2544},{},[2545,2547,2551,2553,2558],{"type":2540,"value":2546},"Daarvoor was hij CTO van QIWI, een van de grootste betaalplatforms van Rusland, waar hij zo'n 12 engineeringteams aanstuurde die reikten van webproducten tot kaartverwerking, ",{"type":2536,"tag":277,"props":2548,"children":2550},{"slug":2549},"pci-dss",[],{"type":2540,"value":2552},"-scope en contactloos betalen — waaronder het bouwen van contactloze kaartbetalingen op Android via ",{"type":2536,"tag":277,"props":2554,"children":2556},{"slug":2555},"host-card-emulation",[2557],{"type":2540,"value":2346},{"type":2540,"value":2559}," over ISO\u002FIEC 14443, met EMV Contactless (Visa PayWave) daarbovenop.",{"type":2536,"tag":175,"props":2561,"children":2562},{},[2563,2565,2569],{"type":2540,"value":2564},"Hij was ook principal developer bij Yandex, waar hij aan Yandex.Auto werkte — native Android diep de auto in brengen, met zware integratie over de CAN-bus via een eigen CAN-shield — en principal bij Evotor, waarvan de kassa-apparaten op een fork van ",{"type":2536,"tag":277,"props":2566,"children":2568},{"slug":2567},"aosp",[],{"type":2540,"value":2570}," draaien, wat hem een blik op Android op laag niveau gaf die de meeste app-ontwikkelaars nooit krijgen.",{"type":2536,"tag":175,"props":2572,"children":2573},{},[2574,2576,2582,2584,2590,2592,2598],{"type":2540,"value":2575},"Vandaag leidt hij de oplevering van de vlaggenschipapps van het bureau — van de grafiekzware fintech-UI van ",{"type":2536,"tag":1499,"props":2577,"children":2579},{"href":2578},"\u002Fportfolio\u002Fextraetf",[2580],{"type":2540,"value":2581},"ExtraETF",{"type":2540,"value":2583}," tot het volledig eigen designsysteem van ",{"type":2536,"tag":1499,"props":2585,"children":2587},{"href":2586},"\u002Fportfolio\u002Farcana",[2588],{"type":2540,"value":2589},"Arcana",{"type":2540,"value":2591},". Hij schrijft de meeste artikelen op deze blog en onderhoudt het opensourcewerk van het bureau, waaronder de DOCX-naar-PDF-motor ",{"type":2536,"tag":1499,"props":2593,"children":2595},{"href":2594},"\u002Fopen-source\u002Fdxpdf",[2596],{"type":2540,"value":2597},"dxpdf",{"type":2540,"value":182},{"type":2536,"tag":175,"props":2600,"children":2601},{},[2602,2604,2609,2611,2616,2617,2622,2623,2628,2629,2634,2635,2640,2642,2647],{"type":2540,"value":2603},"Hij werkt met ",{"type":2536,"tag":1499,"props":2605,"children":2607},{"href":2606},"\u002Ftechnologies\u002Fflutter",[2608],{"type":2540,"value":39},{"type":2540,"value":2610},", native iOS en Android, ",{"type":2536,"tag":1499,"props":2612,"children":2614},{"href":2613},"\u002Ftechnologies\u002Fgo",[2615],{"type":2540,"value":49},{"type":2540,"value":1533},{"type":2536,"tag":1499,"props":2618,"children":2620},{"href":2619},"\u002Ftechnologies\u002Frust",[2621],{"type":2540,"value":124},{"type":2540,"value":1533},{"type":2536,"tag":1499,"props":2624,"children":2626},{"href":2625},"\u002Ftechnologies\u002Ftypescript",[2627],{"type":2540,"value":153},{"type":2540,"value":1533},{"type":2536,"tag":1499,"props":2630,"children":2632},{"href":2631},"\u002Ftechnologies\u002Fkotlin",[2633],{"type":2540,"value":69},{"type":2540,"value":1533},{"type":2536,"tag":1499,"props":2636,"children":2638},{"href":2637},"\u002Ftechnologies\u002Fkubernetes",[2639],{"type":2540,"value":78},{"type":2540,"value":2641}," en ",{"type":2536,"tag":1499,"props":2643,"children":2645},{"href":2644},"\u002Ftechnologies\u002Fdocker",[2646],{"type":2540,"value":27},{"type":2540,"value":2648},", met de nadruk op applicatiearchitectuur, cross-platform oplevering en het bouwen van teams die opleveren.",[2650,2662,2682,2695,2709,2723,2737,2754,2767,2782,2796,2809,2823,2837,2851,2861,2874,2885,2895,2908,2918,2929,2944,2953,2966,2980,2987,2999,3012,3023,3038,3051,3064,3075,3088,3101,3114,3125,3136,3148,3159,3171,3181,3193,3206,3218,3230,3244,3255,3267,3280,3289,3301],{"slug":2567,"term":2350,"definition":2651,"category":2652,"aliases":2653,"links":2655,"related":2659,"readMore":-1,"target":2660,"hasArticle":2661},"Het Android Open Source Project — Android zonder de Google-laag erbovenop, door iedereen vrij te forken. Kassaterminals, kiosken en autosystemen draaien op forks ervan, en werken op dat niveau legt delen van het OS bloot die een app-ontwikkelaar nooit ziet.","platform",[2654],"Android Open Source Project",[2656],{"kind":2657,"url":2658},"website","https:\u002F\u002Fsource.android.com\u002F",[2555],"\u002Fglossary#aosp",false,{"slug":2663,"term":2664,"definition":2665,"category":2666,"aliases":2667,"links":2672,"related":2678,"readMore":-1,"target":2681,"hasArticle":2661},"gdpr","AVG","De EU-verordening over persoonsgegevens van mensen in de EU: een rechtsgrond om ze te verzamelen, echte toestemming voor tracking, en het recht om ze in te zien en te laten wissen. Ze volgt je gebruikers, niet je servers, en geldt dus ongeacht waar het bedrijf staat ingeschreven.","practice",[2668,2669,2670,2671],"GDPR","General Data Protection Regulation","Algemene verordening gegevensbescherming","gegevensbescherming",[2673,2675],{"kind":2657,"url":2674},"https:\u002F\u002Fgdpr.eu\u002F",{"kind":2676,"url":2677},"wikipedia","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGeneral_Data_Protection_Regulation",[2549,2679,2680],"soc-2","hipaa","\u002Fglossary#gdpr",{"slug":2683,"term":2684,"definition":2685,"category":2652,"aliases":2686,"links":2687,"related":2691,"readMore":-1,"target":2694,"hasArticle":2661},"app-clips","App Clips","Een Apple-functie die een klein stuk van een iOS-app draait — onder de 15 MB — zonder het geheel te installeren. Te starten via een QR-code, een NFC-tag of een link, voor als de eerste handeling van een gebruiker niet eerst een gang naar de App Store zou moeten vergen.",[],[2688],{"kind":2689,"url":2690},"documentation","https:\u002F\u002Fdeveloper.apple.com\u002Fapp-clips\u002F",[2692,2693],"deep-linking","install-referrer","\u002Fglossary#app-clips",{"slug":2696,"term":2697,"definition":2698,"category":2652,"aliases":2699,"links":2700,"related":2705,"readMore":-1,"target":2708,"hasArticle":2661},"bigquery","BigQuery","Het analytische datawarehouse van Google Cloud. Je laat SQL los op miljarden rijen en het scant ze in seconden, op opslag die losstaat van de machines die de query uitvoeren — en juist dat houdt rapportage en verkenning weg bij de database die het live verkeer bedient.",[],[2701,2703],{"kind":2657,"url":2702},"https:\u002F\u002Fcloud.google.com\u002Fbigquery",{"kind":2676,"url":2704},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBigQuery",[2706,2707],"object-storage","elasticsearch","\u002Fglossary#bigquery",{"slug":2710,"term":2711,"definition":2712,"category":2666,"aliases":2713,"links":2718,"related":2721,"readMore":-1,"target":2722,"hasArticle":2661},"ci-cd","CI\u002FCD","Automatisering die elke wijziging bouwt, test en uitlevert zonder dat iemand handmatig commando's uitvoert. Op mobiel is het wat van een release een druk op de knop maakt in plaats van een middag wachten tot iemand anders tijd heeft.",[2714,2715,2716,2717],"CI","continuous integration","continuous delivery","continue integratie",[2719],{"kind":2676,"url":2720},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCI\u002FCD",[],"\u002Fglossary#ci-cd",{"slug":2724,"term":2725,"definition":2726,"category":2727,"aliases":2728,"links":2730,"related":2733,"readMore":-1,"target":2736,"hasArticle":2661},"crud","CRUD","Create, read, update, delete — de vier bewerkingen achter zo goed als elk formulier en elk beheerscherm. Verzamelnaam voor de helft van een app die routinematig gegevensbeheer is, tegenover de delen die echte domeinlogica dragen.","architecture",[2729],"Create, Read, Update, Delete",[2731],{"kind":2676,"url":2732},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCreate,_read,_update_and_delete",[2734,2735],"rest","graphql","\u002Fglossary#crud",{"slug":2738,"term":2739,"definition":2740,"category":2727,"aliases":2741,"links":2747,"related":2750,"readMore":-1,"target":2753,"hasArticle":2661},"container-registry","Containerregistry","Een gehoste opslagplaats voor container-images, aangesproken op naam en tag — Docker Hub, GitHub Container Registry, of die van je eigen cloudprovider. Een build daarheen pushen verandert 'werkt op mijn machine' in een image die iedereen ongewijzigd kan pullen en draaien.",[2742,2743,2744,2745,2746],"container registry","image registry","Docker registry","GHCR","ghcr.io",[2748],{"kind":2689,"url":2749},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Fpackages\u002Fworking-with-a-github-packages-registry\u002Fworking-with-the-container-registry",[2751,2752],"dev-container","multi-arch-image","\u002Fglossary#container-registry",{"slug":2692,"term":2755,"definition":2756,"category":2652,"aliases":2757,"links":2762,"related":2765,"readMore":2766,"target":2766,"hasArticle":2136},"Deep linking","Een link die een specifiek scherm in een geïnstalleerde app opent in plaats van het beginscherm of een webpagina. Deferred deep linking overleeft een installatie, zodat een tik die via de appwinkel loopt alsnog op het juiste scherm landt.",[2758,2759,2760,2761],"deferred deep linking","universal links","Android App Links","diepe links",[2763],{"kind":2676,"url":2764},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMobile_deep_linking",[2683,2693],"\u002Fglossary\u002Fdeep-linking",{"slug":2751,"term":2768,"definition":2769,"category":2666,"aliases":2770,"links":2775,"related":2780,"readMore":-1,"target":2781,"hasArticle":2661},"Dev Container","Een ontwikkelomgeving die één keer beschreven staat in devcontainer.json — het OS, de tools en de runtimeversies die een project nodig heeft — en die bij iedere bijdrager identiek in een container opent, in plaats van een installatiehandleiding die iedereen anders leest.",[2771,2772,2773,2774],"devcontainer.json","Dev Containers","VS Code Dev Containers","ontwikkelcontainer",[2776,2778],{"kind":2657,"url":2777},"https:\u002F\u002Fcontainers.dev\u002F",{"kind":2689,"url":2779},"https:\u002F\u002Fcode.visualstudio.com\u002Fdocs\u002Fdevcontainers\u002Fcontainers",[2738],"\u002Fglossary#dev-container",{"slug":2783,"term":2784,"definition":2785,"category":2727,"aliases":2786,"links":2791,"related":2794,"readMore":-1,"target":2795,"hasArticle":2661},"floating-point","Drijvende komma","Het binaire IEEE 754-formaat achter double en float. Het kan 0,1 niet exact weergeven, dus 0.1 + 0.2 is 0.30000000000000004 — onzichtbaar in graphics en fataal bij geld, dat thuishoort in gehele kleinste eenheden (centen) of een decimaal type.",[2787,2788,2789,2790],"IEEE 754","double","floating point","zwevende komma",[2792],{"kind":2676,"url":2793},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FIEEE_754",[],"\u002Fglossary#floating-point",{"slug":2707,"term":2797,"definition":2798,"category":2652,"aliases":2799,"links":2802,"related":2807,"readMore":-1,"target":2808,"hasArticle":2661},"Elasticsearch","Een zoek- en analysemachine die records indexeert zodat je ze interactief kunt filteren en doorzoeken in plaats van ze te scannen. Waar je naar grijpt als de vraag 'laat me deze specifieke sessies zien, op zes manieren ingeperkt' is en niet 'tel deze kolom op'.",[2800,2801],"Elastic","ELK",[2803,2805],{"kind":2657,"url":2804},"https:\u002F\u002Fwww.elastic.co\u002Felasticsearch",{"kind":2676,"url":2806},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FElasticsearch",[2696],"\u002Fglossary#elasticsearch",{"slug":2810,"term":2811,"definition":2812,"category":2727,"aliases":2813,"links":2817,"related":2820,"readMore":-1,"target":2822,"hasArticle":2661},"end-to-end-encryption","End-to-end-versleuteling","Versleuteling die op het verzendende apparaat wordt aangebracht en pas op het ontvangende weer wordt weggehaald, zodat de dienst die het bericht vervoert het niet kan lezen — niet na een vordering, niet na een datalek. Het beschermt de inhoud, nooit de metadata.",[2814,2815,2816],"E2EE","end-to-end encryption","end-to-end versleuteld",[2818],{"kind":2676,"url":2819},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEnd-to-end_encryption",[279,2821],"jwt","\u002Fglossary#end-to-end-encryption",{"slug":2824,"term":2825,"definition":2826,"category":2727,"aliases":2827,"links":2831,"related":2832,"readMore":-1,"target":2836,"hasArticle":2661},"fan-out","Fan-out","Eén keer uit een bron lezen en elke update afleveren bij iedere client die erop geabonneerd is. De naïeve versie schrijft in een lus naar abonnees en loopt vast zodra één socket traag is; een echte buffert per client en laat vallen wie niet kan bijbenen.",[2828,2829,2830],"fanout","broadcast","uitzending",[],[2833,2834,2835],"websocket","pub-sub","server-sent-events","\u002Fglossary#fan-out",{"slug":1442,"term":2838,"definition":2839,"category":2666,"aliases":2840,"links":2844,"related":2847,"readMore":-1,"target":2850,"hasArticle":2661},"Feature flags","Schakelaars die functionaliteit aan- of uitzetten vanuit configuratie in plaats van vanuit een release. Ze laten één binary zich per merk, markt of gebruiker anders gedragen, en laten je een riskante functie uitzetten zonder een nieuwe build door de review te sturen.",[2841,2842,2843],"feature flag","feature toggle","functieschakelaar",[2845],{"kind":2676,"url":2846},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FFeature_toggle",[1373,2848,2849],"white-label","multi-tenancy","\u002Fglossary#feature-flags",{"slug":1373,"term":2852,"definition":2853,"category":2666,"aliases":2854,"links":2858,"related":2859,"readMore":-1,"target":2860,"hasArticle":2661},"Gefaseerde uitrol","Een build eerst naar een klein percentage gebruikers uitbrengen en pas verbreden als het crashvrije percentage standhoudt. Een slechte build die je bij tien procent betrapt is een slechte middag; dezelfde build bij honderd procent is een slechte week.",[2855,2856,2857],"staged rollout","gefaseerde release","canary release",[],[1442,2710],"\u002Fglossary#staged-rollout",{"slug":2862,"term":2863,"definition":2864,"category":2666,"aliases":2865,"links":2869,"related":2872,"readMore":-1,"target":2873,"hasArticle":2661},"golden-test","Golden test","Een test die een widget rendert en het resultaat pixel voor pixel vergelijkt met een opgeslagen referentiebeeld. In Flutter is het de goedkoopste manier om te zien of een herontwerp de empty state brak op 320pt, in donkere modus, bij 200% tekstgrootte.",[2866,2867,2868],"golden tests","screenshottest","snapshottest",[2870],{"kind":2689,"url":2871},"https:\u002F\u002Fapi.flutter.dev\u002Fflutter\u002Fflutter_test\u002FmatchesGoldenFile.html",[2710],"\u002Fglossary#golden-test",{"slug":2735,"term":2875,"definition":2876,"category":2727,"aliases":2877,"links":2878,"related":2883,"readMore":-1,"target":2884,"hasArticle":2661},"GraphQL","Een querytaal voor API's waarin de client precies de velden benoemt die hij wil en één antwoord terugkrijgt dat daarop past. Het haalt de over-fetching weg waar REST-endpoints naar afglijden, en voegt er een eigen faalmodus aan toe: een onbegrensde query die het hele datamodel afloopt.",[],[2879,2881],{"kind":2657,"url":2880},"https:\u002F\u002Fgraphql.org\u002F",{"kind":2676,"url":2882},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGraphQL",[2734],"\u002Fglossary#graphql",{"slug":2680,"term":2886,"definition":2887,"category":2666,"aliases":2888,"links":2890,"related":2893,"readMore":-1,"target":2894,"hasArticle":2661},"HIPAA","De Amerikaanse wet over beschermde gezondheidsinformatie — hoe die opgeslagen, verstuurd, gelogd en gedeeld mag worden. Net als PCI-DSS is het een architectuurkeuze aan het begin, geen beleidsstuk dat je vlak voor de lancering toevoegt.",[2889],"Health Insurance Portability and Accountability Act",[2891],{"kind":2676,"url":2892},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHealth_Insurance_Portability_and_Accountability_Act",[2549,2663,2679],"\u002Fglossary#hipaa",{"slug":2896,"term":2897,"definition":2898,"category":2727,"aliases":2899,"links":2902,"related":2905,"readMore":-1,"target":2907,"hasArticle":2661},"headless-cms","Headless CMS","Een contentsysteem met een redactieomgeving en een API, maar zonder eigen front-end. Redacteuren publiceren op één plek, en de site of app rendert die content zelf — zodat de presentatielaag van jou is en niet van de CMS-leverancier.",[2900,2901],"headless contentmanagementsysteem","content-API",[2903],{"kind":2676,"url":2904},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHeadless_content_management_system",[2906,2734],"server-side-rendering","\u002Fglossary#headless-cms",{"slug":2555,"term":2346,"definition":2909,"category":2652,"aliases":2910,"links":2913,"related":2916,"readMore":-1,"target":2917,"hasArticle":2661},"Een Android-telefoon zich in software laten gedragen als een contactloze kaart via NFC, zonder secure element in hardware. Zo betaalt een wallet-app bij een terminal: de telefoon spreekt hetzelfde contactloze EMV-protocol als de plastic kaart zou hebben gedaan.",[2911,2348,2912],"HCE","contactloos betalen",[2914],{"kind":2689,"url":2915},"https:\u002F\u002Fdeveloper.android.com\u002Fdevelop\u002Fconnectivity\u002Fnfc\u002Fhce",[2549,2567],"\u002Fglossary#host-card-emulation",{"slug":2919,"term":2920,"definition":2921,"category":2652,"aliases":2922,"links":2923,"related":2926,"readMore":-1,"target":2928,"hasArticle":2661},"impeller","Impeller","De renderengine die Flutter vandaag gebruikt, standaard op iOS sinds 2023 en op Android sinds 2024. Hij compileert zijn shaders vooraf in plaats van tijdens de eerste animatie, wat de shader-jank wegnam die Flutters zichtbaarste productieprobleem was.",[],[2924],{"kind":2689,"url":2925},"https:\u002F\u002Fdocs.flutter.dev\u002Fperf\u002Fimpeller",[2927],"skia","\u002Fglossary#impeller",{"slug":2930,"term":2931,"definition":2932,"category":2933,"aliases":2934,"links":2938,"related":2941,"readMore":-1,"target":2943,"hasArticle":2661},"in-app-purchase","In-app-aankoop","Digitale goederen of een abonnement verkopen via de Apple- of Google-billing die beide winkels verplicht stellen voor digitale content en waarover ze commissie rekenen. Het moeilijke is nooit de aankoop; het is het herstellen ervan op een nieuw toestel en de rechten kloppend houden.","business",[2935,2936,2937],"IAP","in-app purchase","in-app-abonnement",[2939],{"kind":2689,"url":2940},"https:\u002F\u002Fdeveloper.apple.com\u002Fin-app-purchase\u002F",[2942],"product-market-fit","\u002Fglossary#in-app-purchase",{"slug":2693,"term":2945,"definition":2946,"category":2652,"aliases":2947,"links":2948,"related":2951,"readMore":-1,"target":2952,"hasArticle":2661},"Install Referrer","Een Google Play-API die een net geïnstalleerde Android-app de campagneparameters overhandigt van de link die tot de installatie leidde. De Android-helft van deferred deep linking, en de betrouwbare manier om te weten waar een gebruiker vandaan kwam.",[],[2949],{"kind":2689,"url":2950},"https:\u002F\u002Fdeveloper.android.com\u002Fgoogle\u002Fplay\u002Finstallreferrer",[2692,2683],"\u002Fglossary#install-referrer",{"slug":2821,"term":2954,"definition":2955,"category":2956,"aliases":2957,"links":2959,"related":2964,"readMore":-1,"target":2965,"hasArticle":2661},"JWT","Een ondertekend token dat zijn eigen claims meedraagt, zodat een server kan zien bij wie een verzoek hoort zonder een sessie op te zoeken. Standaard voor mobiele authenticatie. De handtekening bewijst dat er niets is gewijzigd; ze verbergt niet wat erin staat.","protocol",[2958],"JSON Web Token",[2960,2962],{"kind":2657,"url":2961},"https:\u002F\u002Fjwt.io\u002F",{"kind":2676,"url":2963},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJSON_Web_Token",[2549],"\u002Fglossary#jwt",{"slug":2967,"term":2968,"definition":2969,"category":2666,"aliases":2970,"links":2975,"related":2978,"readMore":-1,"target":2979,"hasArticle":2661},"kyc","KYC","Know Your Customer — de identiteitscontroles die een gereguleerd financieel product uitvoert voordat het iemand geld laat verplaatsen: documentscans, liveness, sanctie- en witwascontroles. Het bepaalt de onboarding sterker dan welke ontwerpkeuze ook.",[2971,2972,2973,2974],"Know Your Customer","AML","KYC\u002FAML","witwascontrole",[2976],{"kind":2676,"url":2977},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKnow_your_customer",[2549,2663],"\u002Fglossary#kyc",{"slug":76,"term":74,"definition":2981,"category":2652,"aliases":2982,"links":2984,"related":2985,"readMore":2986,"target":2986,"hasArticle":2661},"Bedrijfslogica geschreven in Kotlin delen tussen Android, iOS en de server, terwijl elk platform zijn eigen native UI houdt. Het alternatief voor Flutter wanneer de interface native moet zijn maar de regels erachter niet.",[2983],"KMP",[],[],"\u002Ftechnologies\u002Fkmp",{"slug":2988,"term":2989,"definition":2990,"category":2933,"aliases":2991,"links":2994,"related":2997,"readMore":-1,"target":2998,"hasArticle":2661},"mvp","MVP","De kleinste versie van een product die je aan echte gebruikers kunt uitbrengen en die nog steeds de vraag beantwoordt waarvoor je het bouwde. Een besluit over scope, niet over kwaliteit — een MVP moet nog altijd werken.",[2992,2993],"minimum viable product","minimaal levensvatbaar product",[2995],{"kind":2676,"url":2996},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMinimum_viable_product",[2848],"\u002Fglossary#mvp",{"slug":2752,"term":3000,"definition":3001,"category":2727,"aliases":3002,"links":3007,"related":3010,"readMore":-1,"target":3011,"hasArticle":2661},"Multi-architectuur-image","Eén image-tag die per CPU-architectuur naar andere binaries verwijst — linux\u002Famd64 en linux\u002Farm64 zijn het gebruikelijke paar — zodat dezelfde docker pull ongewijzigd werkt op Intel- en AMD-servers en op Apple Silicon-laptops.",[3003,3004,3005,3006],"multi-arch build","multi-arch image","multi-platform image","linux\u002Famd64 + linux\u002Farm64",[3008],{"kind":2689,"url":3009},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fmulti-platform\u002F",[2738],"\u002Fglossary#multi-arch-image",{"slug":2849,"term":3013,"definition":3014,"category":2727,"aliases":3015,"links":3018,"related":3021,"readMore":-1,"target":3022,"hasArticle":2661},"Multi-tenancy","Eén deployment die veel klanten bedient, waarbij elke klant alleen zijn eigen data, configuratie en ingeschakelde functies ziet doordat de tenantcontext per verzoek wordt bepaald. Het is wat een vloot merkapps één product maakt in plaats van veel forks.",[3016,3017],"multi-tenant","tenant",[3019],{"kind":2676,"url":3020},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMultitenancy",[2848,1442],"\u002Fglossary#multi-tenancy",{"slug":3024,"term":3025,"definition":3026,"category":2956,"aliases":3027,"links":3031,"related":3036,"readMore":-1,"target":3037,"hasArticle":2661},"oauth","OAuth","De standaard achter Inloggen met Apple, Google en de rest: de gebruiker geeft je app toestemming bij een aanbieder die hij al vertrouwt, en je app krijgt een token in plaats van zijn wachtwoord. Niemand hoeft nieuwe inloggegevens te verzinnen en jij bewaart ze nergens.",[3028,3029,3030],"OAuth 2.0","social login","Inloggen met Apple",[3032,3034],{"kind":2657,"url":3033},"https:\u002F\u002Foauth.net\u002F2\u002F",{"kind":2676,"url":3035},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FOAuth",[2821],"\u002Fglossary#oauth",{"slug":2706,"term":3039,"definition":3040,"category":2727,"aliases":3041,"links":3046,"related":3049,"readMore":-1,"target":3050,"hasArticle":2661},"Objectopslag","Opslag die een heel bestand onder een sleutel bewaart in plaats van in een mappenboom — Amazon S3 en de vele diensten die zijn API spreken. Goedkoop, praktisch onbeperkt, en de gebruikelijke plek voor ruwe events, back-ups en media: eenmaal geschreven, zelden gelezen, eeuwig bewaard.",[3042,3043,3044,3045],"S3","S3-compatibele opslag","object storage","blob storage",[3047],{"kind":2676,"url":3048},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObject_storage",[2696],"\u002Fglossary#object-storage",{"slug":2549,"term":3052,"definition":3053,"category":2666,"aliases":3054,"links":3057,"related":3062,"readMore":-1,"target":3063,"hasArticle":2661},"PCI-DSS","De beveiligingsstandaard van de kaartindustrie die iedereen bindt die kaartgegevens opslaat, verwerkt of verstuurt. De meeste apps blijven er bewust buiten door de kaartinvoer over te laten aan een gecertificeerde betaaldienstverlener.",[3055,3056],"PCI DSS","Payment Card Industry Data Security Standard",[3058,3060],{"kind":2657,"url":3059},"https:\u002F\u002Fwww.pcisecuritystandards.org\u002F",{"kind":2676,"url":3061},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPayment_Card_Industry_Data_Security_Standard",[2821],"\u002Fglossary#pci-dss",{"slug":1810,"term":3065,"definition":3066,"category":2652,"aliases":3067,"links":3070,"related":3073,"readMore":-1,"target":3074,"hasArticle":2661},"Platform channels","De brug waarmee een Flutter-app native iOS- en Android-code aanroept — Keychain en Keystore, biometrie, betaalschermen, elke SDK zonder Dart-pakket. Routinewerk maar echt werk, en de eerste plek waar een engineer die nooit buiten Dart kwam vastloopt.",[1811,3068,3069],"method channel","platformkanaal",[3071],{"kind":2689,"url":3072},"https:\u002F\u002Fdocs.flutter.dev\u002Fplatform-integration\u002Fplatform-channels",[76],"\u002Fglossary#platform-channels",{"slug":3076,"term":3077,"definition":3078,"category":2956,"aliases":3079,"links":3083,"related":3086,"readMore":-1,"target":3087,"hasArticle":2661},"post-quantum-cryptography","Post-quantumcryptografie","Versleutelingsalgoritmen die veilig moeten blijven tegen een toekomstige quantumcomputer, inmiddels gestandaardiseerd door NIST. De migratie is nu al dringend, nog vóór die hardware bestaat, omdat verkeer dat vandaag wordt onderschept later alsnog te ontsleutelen valt.",[3080,3081,3082],"PQC","post-quantum cryptography","postkwantumcryptografie",[3084],{"kind":2676,"url":3085},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPost-quantum_cryptography",[279,2810],"\u002Fglossary#post-quantum-cryptography",{"slug":2942,"term":3089,"definition":3090,"category":2933,"aliases":3091,"links":3095,"related":3098,"readMore":-1,"target":3100,"hasArticle":2661},"Product-market fit","Het punt waarop een product aantoonbaar mensen heeft gevonden die het willen — ze gebruiken het, komen terug en betalen. Daarvoor beantwoordt engineering een vraag; daarna bedient engineering de vraag.",[3092,3093,3094],"PMF","product\u002Fmarket fit","productmarktfit",[3096],{"kind":2676,"url":3097},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProduct-market_fit",[2988,3099],"time-to-market","\u002Fglossary#product-market-fit",{"slug":3102,"term":3103,"definition":3104,"category":2666,"aliases":3105,"links":3108,"related":3111,"readMore":-1,"target":3113,"hasArticle":2661},"prompt-injection","Prompt injection","Een aanval waarbij tekst van een gebruiker door een taalmodel als instructie wordt gelezen in plaats van als data, en het model zo om zijn eigen regels heen wordt gestuurd. De LLM-variant van SQL-injectie, en hij duikt overal op waar gebruikersinvoer in een prompt wordt geplakt.",[3106,3107],"injectieaanval","injection attack",[3109],{"kind":2676,"url":3110},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPrompt_injection",[3112],"rate-limiting","\u002Fglossary#prompt-injection",{"slug":2834,"term":3115,"definition":3116,"category":2727,"aliases":3117,"links":3120,"related":3123,"readMore":-1,"target":3124,"hasArticle":2661},"Pub\u002FSub","Een berichtenpatroon waarin een producent een gebeurtenis publiceert en willekeurig veel consumenten die elk voor zich lezen, met een broker ertussen. De producent wacht nooit op ze, en zo blijft een verzoekpad snel terwijl het tragere werk erachter gebeurt.",[3118,3119],"publish\u002Fsubscribe","Google Cloud Pub\u002FSub",[3121],{"kind":2676,"url":3122},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPublish%E2%80%93subscribe_pattern",[],"\u002Fglossary#pub-sub",{"slug":2734,"term":3126,"definition":3127,"category":2727,"aliases":3128,"links":3131,"related":3134,"readMore":-1,"target":3135,"hasArticle":2661},"REST","De gangbare stijl voor HTTP-API's: een URL benoemt een resource en het HTTP-werkwoord zegt wat ermee moet gebeuren. De standaardmanier waarop een app met een backend praat, en wat de meeste externe integraties verwachten aan te treffen.",[3129,3130],"REST API","Representational State Transfer",[3132],{"kind":2676,"url":3133},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FREST",[2833,2835],"\u002Fglossary#rest",{"slug":3112,"term":3137,"definition":3138,"category":2666,"aliases":3139,"links":3143,"related":3146,"readMore":-1,"target":3147,"hasArticle":2661},"Rate limiting","Een bovengrens op het aantal verzoeken dat één aanroeper binnen een bepaald venster mag doen. Het houdt tegen dat één enthousiaste gebruiker, een scraper of een bot in een middag een maand aan betaald API-budget opmaakt, en het moet aan jouw kant van de integratie staan.",[3140,3141,3142],"rate limit","throttling","verzoeklimiet",[3144],{"kind":2676,"url":3145},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRate_limiting",[3102],"\u002Fglossary#rate-limiting",{"slug":2679,"term":3149,"definition":3150,"category":2666,"aliases":3151,"links":3154,"related":3157,"readMore":-1,"target":3158,"hasArticle":2661},"SOC 2","Een rapport van een externe auditor over hoe een organisatie met klantgegevens omgaat — beveiliging, beschikbaarheid, vertrouwelijkheid — en niet een certificaat dat je koopt. Zakelijke kopers vragen erom, en het beperkt de architectuur lang voordat de audit dat doet.",[3152,3153],"SOC2","System and Organization Controls",[3155],{"kind":2676,"url":3156},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSystem_and_Organization_Controls",[2549,2663,2680],"\u002Fglossary#soc-2",{"slug":3160,"term":3161,"definition":3162,"category":2933,"aliases":3163,"links":3166,"related":3169,"readMore":-1,"target":3170,"hasArticle":2661},"saas","SaaS","Software die als doorlopend abonnement op een gehost product wordt verkocht in plaats van als eenmalige licentie die de klant zelf installeert en draait. De leverancier beheert de servers, levert doorlopend updates en factureert per gebruiker of per verbruik.",[3164,3165],"Software as a Service","software-as-a-service",[3167],{"kind":2676,"url":3168},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSoftware_as_a_service",[2849,2848],"\u002Fglossary#saas",{"slug":2835,"term":3172,"definition":3173,"category":2956,"aliases":3174,"links":3176,"related":3179,"readMore":-1,"target":3180,"hasArticle":2661},"Server-Sent Events","Een eenrichtingsstroom van server naar client over een gewone HTTP-verbinding. Eenvoudiger dan een WebSocket en het volstaat overal waar alleen de server iets te melden heeft — een voortgangsfeed, een AI-antwoord dat token voor token binnenkomt.",[3175],"SSE",[3177],{"kind":2676,"url":3178},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FServer-sent_events",[2833,2734],"\u002Fglossary#server-sent-events",{"slug":2906,"term":3182,"definition":3183,"category":2727,"aliases":3184,"links":3188,"related":3191,"readMore":-1,"target":3192,"hasArticle":2661},"Server-side rendering","Een pagina op de server als kant-en-klare HTML opbouwen, zodat het eerste antwoord de inhoud, koppen, metatags en gestructureerde data al meedraagt. Crawlers, linkvoorbeelden en trage apparaten lezen dat zonder JavaScript uit te voeren.",[3185,3186,3187],"SSR","server-rendered","renderen op de server",[3189],{"kind":2676,"url":3190},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FServer-side_scripting",[2896],"\u002Fglossary#server-side-rendering",{"slug":2927,"term":3194,"definition":3195,"category":2652,"aliases":3196,"links":3199,"related":3204,"readMore":-1,"target":3205,"hasArticle":2661},"Skia","De opensource 2D-graphicsbibliotheek van Google die Chrome, Android en — tot Impeller — elk Flutter-frame tekent. Ze rendert net zo goed naar PDF als naar een scherm, en dat is wat print-naar-PDF in Chrome doet.",[3197,3198],"Skia Graphics Engine","skia-safe",[3200,3202],{"kind":2657,"url":3201},"https:\u002F\u002Fskia.org\u002F",{"kind":2676,"url":3203},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSkia_Graphics_Engine",[2919],"\u002Fglossary#skia",{"slug":3207,"term":2157,"definition":3208,"category":2727,"aliases":3209,"links":3213,"related":3216,"readMore":-1,"target":3217,"hasArticle":2661},"state-management","Hoe een app bepaalt waar een waarde leeft, wie hem mag wijzigen en welke delen van het scherm opnieuw tekenen als dat gebeurt. In Flutter is de keuze tussen Riverpod, BLoC en Provider een van de eerste architectuurbesluiten en het lastigst te herzien.",[3210,3211,3212],"statemanagement","BLoC","Riverpod",[3214],{"kind":2689,"url":3215},"https:\u002F\u002Fdocs.flutter.dev\u002Fdata-and-backend\u002Fstate-mgmt\u002Foptions",[2862],"\u002Fglossary#state-management",{"slug":279,"term":3219,"definition":3220,"category":2956,"aliases":3221,"links":3225,"related":3228,"readMore":-1,"target":3229,"hasArticle":2661},"TLS","De versleutelingslaag onder HTTPS. Ze bewijst dat de server is wie zijn certificaat zegt, spreekt een verse sleutel voor de sessie af en versleutelt alles daarna — zodat het netwerk ertussen alleen cijfertekst ziet die het niet stiekem kan wijzigen.",[3222,3223,3224],"SSL","HTTPS","Transport Layer Security",[3226],{"kind":2676,"url":3227},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTransport_Layer_Security",[2810,2821],"\u002Fglossary#tls",{"slug":3231,"term":3232,"definition":3233,"category":2933,"aliases":3234,"links":3240,"related":3241,"readMore":-1,"target":3243,"hasArticle":2661},"staff-augmentation","Teamuitbreiding","Een inhuurmodel waarbij engineers van een externe partner bij je team komen en onder jouw aansturing werken — in jouw repository, jouw sprints, jouw proces — in plaats van een eigen project op te leveren. Je koopt capaciteit; de code en de context blijven bij jou.",[3235,3236,3237,3238,3239],"staff augmentation","team augmentation","dedicated developers","outstaffing","teamversterking",[],[3242,3099],"total-cost-of-ownership","\u002Fglossary#staff-augmentation",{"slug":3099,"term":3245,"definition":3246,"category":2933,"aliases":3247,"links":3250,"related":3253,"readMore":-1,"target":3254,"hasArticle":2661},"Time to market","Hoe lang het duurt om een product van besluit naar echte gebruikers te krijgen. De meeste discussies over stack en scope gaan eigenlijk over dit getal, want elke gewonnen week is een week omzet, feedback en concurrentiepositie.",[3248,3099,3249],"TTM","doorlooptijd tot de markt",[3251],{"kind":2676,"url":3252},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTime_to_market",[2988,3242],"\u002Fglossary#time-to-market",{"slug":3242,"term":3256,"definition":3257,"category":2933,"aliases":3258,"links":3262,"related":3265,"readMore":-1,"target":3266,"hasArticle":2661},"Total cost of ownership","Wat een product over zijn hele leven kost in plaats van alleen de bouw: onderhoud, upgrades, jaarlijkse OS- en winkelmigraties, en het tweede team dat je aanneemt om twee codebases gelijk te houden. Meestal groter dan de bouwofferte, en er vrijwel nooit in opgenomen.",[3259,3260,3261],"TCO","kosten van eigenaarschap","totale eigendomskosten",[3263],{"kind":2676,"url":3264},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTotal_cost_of_ownership",[3099],"\u002Fglossary#total-cost-of-ownership",{"slug":3268,"term":2165,"definition":3269,"category":2956,"aliases":3270,"links":3272,"related":3277,"readMore":-1,"target":3279,"hasArticle":2661},"webrtc","De standaard voor browsers en mobiel om audio, video en data rechtstreeks tussen twee apparaten te sturen, waarbij servers er alleen aan te pas komen om ze aan elkaar voor te stellen. Hierop is een videogesprek in een app gebouwd, tenzij er een gehuurde SDK onder zit.",[3271],"Web Real-Time Communication",[3273,3275],{"kind":2657,"url":3274},"https:\u002F\u002Fwebrtc.org\u002F",{"kind":2676,"url":3276},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebRTC",[3278,2833],"xmpp","\u002Fglossary#webrtc",{"slug":2833,"term":3281,"definition":3282,"category":2956,"aliases":3283,"links":3284,"related":3287,"readMore":-1,"target":3288,"hasArticle":2661},"WebSocket","Een protocol dat één verbinding openhoudt tussen client en server, zodat beide kanten op elk moment kunnen sturen in plaats van dat de client keer op keer vraagt. Waar live koersen, chat en aanwezigheidsindicatoren op draaien.",[],[3285],{"kind":2676,"url":3286},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebSocket",[2835,2734],"\u002Fglossary#websocket",{"slug":2848,"term":3290,"definition":3291,"category":2933,"aliases":3292,"links":3296,"related":3299,"readMore":3300,"target":3300,"hasArticle":2136},"White-label","Eén product dat onder veel merken wordt uitgebracht. Een white-label platform voor mobiel bouwt elke klant een winkelklare app met een eigen naam, ontwerp en content vanuit één gedeelde codebase, in plaats van het project per klant te forken.",[3293,3294,3295],"white label","multi-tenant app","merkloos product",[3297],{"kind":2676,"url":3298},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWhite-label_product",[2988],"\u002Fglossary\u002Fwhite-label",{"slug":3278,"term":3302,"definition":3303,"category":2956,"aliases":3304,"links":3307,"related":3312,"readMore":-1,"target":3313,"hasArticle":2661},"XMPP","Een open, gefedereerd berichtenprotocol, en al lang het alternatief voor zelf een chatbackend schrijven of er een huren. Het dekt ook aanwezigheid, typindicatoren en bestandsoverdracht, en het is oud genoeg dat elk platform een volwassen clientbibliotheek heeft.",[3305,3306],"Jabber","Extensible Messaging and Presence Protocol",[3308,3310],{"kind":2657,"url":3309},"https:\u002F\u002Fxmpp.org\u002F",{"kind":2676,"url":3311},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FXMPP",[3268,2833],"\u002Fglossary#xmpp"]