Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tecnologies, protocols i pràctiques

Introducció

Els criteris de disseny i els models de control d’accés vistos anteriorment necessiten suport tecnològic concret. Aquest document és una referència de les principals tecnologies, protocols i pràctiques que permeten implementar-los en sistemes reals.

Està organitzat en cinc àrees: autenticació i autorització, seguretat web, seguretat d’APIs, seguretat en el desenvolupament modern, i aplicacions mòbils. Cada secció pot llegir-se de forma independent com a consulta.

Autenticació i Autorització

OAuth i OpenID Connect

Imagina que la teva aplicació necessita accedir al calendari d’un usuari en un servei extern. L’alternativa ingènua seria demanar-li la contrasenya del servei — però llavors la teva aplicació tindria accés a tot el compte i l’usuari no podria revocar-lo sense canviar la contrasenya. OAuth resol aquest problema: permet que un usuari autoritzi la teva aplicació a accedir a un recurs concret en el seu nom, sense revelar les seves credencials.

El resultat és un token d’accés de vida limitada i scope limitat. Si es filtra, el dany és contenible: no és la contrasenya de l’usuari.

Hi ha dos fluxos principals segons el context:

  • Authorization Code Flow amb PKCE — per a qualsevol aplicació amb un usuari interactiu (web, mòbil, SPA). L’usuari és redirigit al proveïdor d’identitat (el teu servidor o un proveïdor extern), s’hi autentica, i el proveïdor retorna un codi de curta durada. L’aplicació intercanvia aquest codi per un token en una crida servidor a servidor. PKCE (Proof Key for Code Exchange) és una prova criptogràfica que impedeix que un codi interceptat pugui ser aprofitat per un tercer: el client genera un secret al principi del flux i ha de demostrar que el posseeix en fer l’intercanvi.

  • Client Credentials Flow — per a comunicació màquina a màquina, sense usuari interactiu. El servei s’autentica directament amb el proveïdor usant les seves pròpies credencials i obté un token. Típic en microserveis que es criden entre ells.

OpenID Connect (OIDC) és una capa d’identitat construïda sobre OAuth. La distinció clau: OAuth respon a “pots accedir a aquest recurs?”, OIDC respon a “qui ets?”. OIDC afegeix un ID Token — un JWT signat amb informació sobre l’usuari autenticat (nom, email, identificador). Aquest token és per al client, no per enviar-lo a APIs. És el mecanisme que hi ha darrere dels botons “Inicia sessió amb…” que veiem a tot arreu.

L’error més freqüent és implementar la lògica d’autenticació manualment. OAuth i OIDC tenen molts detalls subtils — validació del paràmetre state per evitar CSRF, verificació del nonce, comprovació del camp aud del token — que una implementació casolana fàcilment omet. Utilitza sempre una biblioteca establerta per al teu framework o un proveïdor d’identitat gestionat.

JSON Web Tokens

Un cop un usuari s’ha autenticat, el servidor necessita una manera de reconèixer-lo en les peticions següents sense demanar-li les credencials cada cop. Una opció és guardar l’estat al servidor (sessions); una altra és que el propi token porti la informació necessària per verificar-lo sense cap consulta externa. Els JSON Web Tokens (JWT) segueixen aquest segon enfocament: un JWT és un token autònom i signat que el servidor pot verificar criptogràficament sense accedir a cap base de dades.

Un JWT té tres parts separades per punts: header, payload i signature. El header especifica l’algoritme de signatura. El payload conté les claims (afirmacions): qui és l’usuari, quan expira el token, per a qui és vàlid. La signature assegura que ningú ha alterat el contingut. JWT és el format de token estàndard de facto: l’utilitzen els principals proveïdors d’identitat (Keycloak, etc.) i és el format que retornen els fluxos OAuth/OIDC descrits anteriorment.

Per a aplicacions on el client no pot mantenir secrets (SPAs, apps mòbils), s’han d’utilitzar algoritmes asimètrics com RS256 o ES256. El servidor signa amb la seva clau privada, i qualsevol pot verificar amb la clau pública. HS256, que utilitza una clau simètrica, només és apropiat per a comunicació servidor a servidor on ambdós extrems poden mantenir el secret de manera segura.

Les claims estàndard tenen significats específics: iss identifica qui ha emès el token, sub identifica el subjecte (usuari), aud especifica per a qui està destinat, exp marca quan expira, iat quan va ser emès i nbf abans de quina data no és vàlid. La validació estricta d’aquestes claims és fonamental per a la seguretat.

Una pràctica essencial és mantenir els tokens d’accés de curta durada, típicament entre 5 i 15 minuts. Això limita la finestra d’oportunitat si un token és compromès. Per evitar que l’usuari hagi de reautenticar-se constantment, s’utilitzen refresh tokens de llarga durada que permeten obtenir nous tokens d’accés. Els refresh tokens s’han de rotar cada vegada que s’utilitzen: quan el client utilitza un refresh token, rep un nou token d’accés i un nou refresh token, invalidant l’anterior. Si un refresh token robat s’utilitza, el servidor detecta la reutilització i pot revocar tota la cadena de tokens.

Per a l’emmagatzematge segur de tokens al navegador, vegeu Control d’accés — Identificador al client.

Passkeys i WebAuthn

Les contrasenyes tenen tres debilitats estructurals: es poden phishing (l’usuari les escriu en un lloc fals sense adonar-se’n), es poden robar de la base de dades del servidor si no estan ben protegides, i els usuaris les reutilitzen entre serveis. Per resoldre-ho hi ha tres conceptes relacionats però diferents:

  • WebAuthn és l’API del navegador que permet autenticar-se amb criptografia de clau pública en lloc de contrasenyes. Quan un usuari es registra, el dispositiu genera un parell de claus: la clau pública s’envia al servidor, i la clau privada mai surt del dispositiu.
  • FIDO2 és l’estàndard complet (definit per la FIDO Alliance) que inclou WebAuthn (la part del navegador) i CTAP (el protocol de comunicació amb autenticadors externs com claus USB).
  • Passkeys són la implementació pràctica d’usuari de FIDO2: credencials WebAuthn que es sincronitzen entre dispositius a través del clauer del sistema operatiu. Això resol el problema històric de l’autenticació sense contrasenya: què passa si perds el dispositiu?

Durant l’autenticació, el servidor envia un repte (challenge) aleatori. El dispositiu signa aquest repte amb la clau privada, i el servidor verifica la signatura amb la clau pública emmagatzemada. Això elimina completament els atacs de phishing tradicionals: no hi ha contrasenya per robar, i les credencials estan vinculades al domini específic.

La implementació requereix suport tant al client (navegador) com al servidor. El client utilitza l’API WebAuthn del navegador per crear credencials i generar assertions. El servidor ha de validar aquestes assertions, verificant signatures i comprovant que el repte, origen i altres paràmetres són correctes.

L’adopció és àmplia i creixent: molts serveis web populars ofereixen login amb passkeys, i els principals sistemes operatius l’integren de forma nativa. Els navegadors moderns suporten conditional UI (o passkey autofill), que mostra passkeys disponibles directament en el camp d’usuari, proporcionant una experiència similar a l’autocompletat de contrasenyes. L’autenticació cross-device permet utilitzar el telèfon per autenticar-se en un ordinador escanejant un codi QR, útil quan els passkeys no estan sincronitzats entre dispositius.

La validació del servidor és la part més delicada i no s’ha d’implementar des de zero. Cal verificar la signatura, el challenge, l’origen i el RPID; qualsevol omissió crea vulnerabilitats difícils de detectar. Utilitza una biblioteca testada per al teu llenguatge (SimpleWebAuthn, webauthn4j, etc.) per a tota la lògica del costat del servidor.

Seguretat Web

Cross-Origin Resource Sharing (CORS)

La Same-Origin Policy és una restricció fonamental dels navegadors: el JavaScript d’una pàgina només pot fer peticions a la mateixa origen (protocol, domini i port). Això protegeix contra molts atacs on un site maliciós intenta llegir dades d’un altre site on l’usuari ha iniciat sessió.

CORS és una peça que tot desenvolupador web trobarà aviat o tard: qualsevol SPA que consumeixi una API en un domini diferent (per exemple, app.example.com cridant api.example.com) necessita CORS configurat correctament. CORS relaxa aquesta restricció de manera controlada, permetent als servidors especificar quins origens poden accedir als seus recursos. Quan el JavaScript fa una petició cross-origin, el navegador primer pot enviar una petició OPTIONS (preflight) preguntant al servidor si permet aquesta petició. El servidor respon amb headers indicant què està permès.

L’header Access-Control-Allow-Origin especifica quins origens poden accedir al recurs. Pot ser un origen específic o el wildcard *. Tanmateix, si la petició inclou credencials (cookies), no es pot utilitzar *: s’ha d’especificar l’origen exacte. Access-Control-Allow-Methods i Access-Control-Allow-Headers controlen quins mètodes HTTP i headers personalitzats estan permesos.

Access-Control-Allow-Credentials: true permet que les peticions incloguin cookies i headers d’autorització. Això és perillós si es combina amb origens massa permissius. Un error comú és reflectir automàticament l’origen de la petició al header Access-Control-Allow-Origin quan s’accepten credencials, permetent efectivament qualsevol origin autenticar-se.

Les peticions simples (GET, HEAD, POST amb content-types específics i sense headers personalitzats) no requereixen preflight. Això pot ser sorprenent per a desenvolupadors que esperen que totes les peticions cross-origin es verifiquin primer. És important dissenyar APIs assumint que aquestes peticions simples poden arribar de qualsevol origen.

Cross-Site Scripting (XSS)

XSS és una de les vulnerabilitats web més freqüents, present de manera recurrent al OWASP Top 10. Consisteix en l’injecció de JavaScript maliciós en pàgines web vistes per altres usuaris. Hi ha tres tipus principals. El Reflected XSS injecta script en paràmetres de URL o formularis que es reflecteixen immediatament a la pàgina. El Stored XSS emmagatzema el script a la base de dades (per exemple, en comentaris) i l’executa cada vegada que es visualitza. El DOM-based XSS manipula el DOM del client sense involucrar el servidor.

La Content Security Policy (CSP) és la defensa més potent. És un header que especifica quines fonts de contingut són de confiança. script-src 'self' només permet scripts del mateix origen. script-src https://trusted-cdn.com permet scripts d’un CDN específic. Però el millor enfocament modern són els nonces: el servidor genera un valor aleatori per cada resposta i només executa scripts amb aquest nonce.

Content-Security-Policy: script-src 'nonce-r4nd0m123' <script nonce="r4nd0m123">...</script>

Això invalida completament XSS injectat perquè l’atacant no pot conèixer el nonce. La directiva strict-dynamic permet que scripts amb nonces creïn nous scripts dinàmicament, facilitant l’ús de frameworks moderns.

Els frameworks frontend moderns (React, Vue, Angular, Svelte) proporcionen escapament automàtic per defecte. Les expressions en templates s’escapen automàticament convertint caràcters perillosos com < en entitats HTML. Tanmateix, funcions que permeten inserir HTML “raw” desactiven aquesta protecció i només s’han d’utilitzar amb contingut de confiança.

La Trusted Types API força que l’assignació a sinks perillosos (innerHTML, eval, etc.) només accepti objectes Trusted Type. Això converteix XSS d’un problema de revisar tot el codi a un problema de revisar els punts específics on es creen aquests objectes de confiança.

La sanitització és necessària quan has de permetre HTML limitat (per exemple, formatació en comentaris). Les llibreries de sanitització eliminen elements i atributs perillosos mentre preserven el formatatge segur. Mai intentis implementar el teu propi sanititzador: és extremadament difícil fer-ho correctament.

Cross-Site Request Forgery (CSRF)

CSRF explota que els navegadors adjunten automàticament cookies a les peticions. Un site maliciós pot fer que el teu navegador enviï una petició al teu banc (amb les teves cookies de sessió) transferint diners a l’atacant. L’usuari està autenticat, la petició és vàlida tècnicament, però no intencionada.

SameSite cookies són la defensa més simple i efectiva. SameSite=Strict impedeix completament que la cookie s’enviï en peticions cross-site. SameSite=Lax permet-la en navegacions top-level (seguir enllaços) però no en formularis POST o fetch requests cross-site. Per a la majoria d’aplicacions, Lax proporciona un bon equilibri entre seguretat i usabilitat. De fet, des de 2020, Chrome, Firefox i Edge apliquen SameSite=Lax per defecte a les cookies que no especifiquen el valor, cosa que ha reduït dràsticament la superfície d’atac CSRF a la web.

Els tokens CSRF són un enfocament més tradicional. El servidor inclou un token aleatori en formularis o el proporciona a l’aplicació. Quan el formulari es submits, el token s’inclou (típicament com a camp ocult o header). El servidor verifica que el token coincideix amb el de la sessió. Com que l’atacant no pot llegir el token (per Same-Origin Policy), no pot crear peticions vàlides.

El pattern Double Submit Cookie emmagatzema el token tant en una cookie com en un lloc accessible a JavaScript (localStorage o el DOM). Quan es fa una petició, JavaScript llegeix el token i l’envia com a header o paràmetre. El servidor verifica que coincideixin. Això funciona perquè un atacant no pot establir cookies per al teu domini ni llegir-les.

Per a APIs JSON, un header personalitzat com X-Requested-With: XMLHttpRequest pot ser suficient. Els navegadors no permeten que sites arbitraris afegeixin headers personalitzats en peticions cross-origin (sense CORS), així que la presència d’aquest header demostra que la petició es va originar des del JavaScript del teu site.

Injection Attacks

La SQL injection insereix SQL maliciós en queries, potencialment llegint, modificant o eliminant dades. El input ' OR '1'='1 en un camp d’usuari podria convertir SELECT * FROM users WHERE username='$input' en SELECT * FROM users WHERE username='' OR '1'='1', retornant tots els usuaris.

Les prepared statements (també anomenades parameterized queries) són la solució definitiva. En lloc de concatenar strings per construir SQL, utilitzes placeholders que el driver de base de dades tracta com a dades, mai com a codi:

cursor.execute("SELECT * FROM users WHERE username = ?", (user_input,))

El driver automàticament escapa el input apropiadament per al dialecte SQL específic. És impossible injectar SQL perquè el input mai s’interpreta com a codi.

Els ORMs (Object-Relational Mappers) construeixen queries utilitzant mètodes que internament usen prepared statements. Tanmateix, ofereixen “escape hatches” per a raw SQL que poden ser vulnerables si no s’utilitzen amb cura. Les funcions de l’ORM són segures, les raw queries requereixen la mateixa atenció que el SQL directe.

El principi de mínim privilegi s’aplica a les credencials de base de dades. L’aplicació no hauria de connectar-se amb un usuari que té permisos DROP TABLE o CREATE USER. Si l’aplicació només llegeix i escriu dades normals, les seves credencials haurien de reflectir només aquests permisos.

Command injection executa comandes del sistema operatiu. Si l’aplicació utilitza input d’usuari per construir comandes (per exemple, executant ping $userInput), un atacant pot injectar ;rm -rf /. La millor defensa és evitar completament l’execució de comandes del shell amb input d’usuari. Si és absolutament necessari, utilitza llibreries que accepten arguments com a llista en lloc de string, i valida estrictament l’input contra una whitelist.

LDAP, XML i NoSQL injection són anàlegs a SQL injection en altres llenguatges de query. Els principis són els mateixos: mai construir queries concatenant strings, utilitzar sempre APIs que separen codi de dades, i validar l’input.

Security Headers

El navegador és molt permissiu per defecte: executa qualsevol script que trobi a la pàgina, carrega recursos de qualsevol origen, permet que la teva pàgina es mostri dins d’un iframe en qualsevol altre site, i intenta endevinar el tipus de contingut si no està clar. Els headers de seguretat són instruccions explícites amb les quals el servidor li diu al navegador quins comportaments restringir en les teves pàgines.

HTTP Strict Transport Security (HSTS) obliga els navegadors a utilitzar sempre HTTPS amb el teu site:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Després de rebre aquest header, el navegador converteix automàticament totes les peticions HTTP en HTTPS durant un any. includeSubDomains aplica això també als subdominis. preload permet incloure el teu domini en llistes hardcoded dels navegadors, protegint fins i tot la primera visita.

Content Security Policy ja s’ha discutit en XSS, però també controla altres recursos: img-src, style-src, connect-src, etc. upgrade-insecure-requests instrueix el navegador a carregar tots els recursos per HTTPS fins i tot si l’HTML especifica HTTP.

X-Frame-Options: DENY impedeix que la teva pàgina es carregui en un iframe, protegint contra clickjacking. SAMEORIGIN permet iframes del mateix origen. La directiva CSP frame-ancestors és més flexible i hauria de preferir-se si ja utilitzes CSP.

X-Content-Type-Options: nosniff impedeix que els navegadors “endevinin” el tipus MIME del contingut. Sense això, un navegador podria interpretar un arxiu JSON com a HTML si sembla contingut HTML, potencialment executant scripts.

Referrer-Policy controla quanta informació de la URL anterior s’inclou en la petició següent. strict-origin-when-cross-origin envia la URL completa per a peticions same-origin però només l’origen per a cross-origin, i res si baixa de HTTPS a HTTP.

Permissions-Policy (anteriorment Feature-Policy) controla l’accés a funcionalitats del navegador com la càmera, micròfon, geolocalització, etc. Permissions-Policy: camera=(), microphone=() desactiva completament aquestes funcionalitats.

Configurar els headers no és suficient: cal verificar que arriben al navegador en producció. Les configuracions de proxy, CDN o balancejador de càrrega poden eliminar-los silenciosament. Eines com securityheaders.com permeten comprovar-ho en un minut. Per a CSP en particular, és recomanable activar-lo primer en mode Content-Security-Policy-Report-Only per detectar violacions sense bloquejar res, i passar a mode d’aplicació un cop validat.

Clickjacking i UI Redressing

El clickjacking superimposa contingut invisible sobre la teva pàgina, enganyant els usuaris perquè facin clic en coses que no veuen. Per exemple, un iframe invisible amb el teu site col·locat sobre un joc. L’usuari creu que està jugant però està fent clic en botons del teu site.

X-Frame-Options i la directiva CSP frame-ancestors són les defenses primàries. frame-ancestors 'none' és equivalent a X-Frame-Options: DENY. frame-ancestors 'self' https://trusted.com permet específicament iframes del teu site i un site de confiança.

Les tècniques JavaScript de “frame busting” (detectar si estàs en un iframe i sortir-ne) són menys fiables. Els atacants han trobat maneres de desactivar-les utilitzant l’atribut sandbox dels iframes o altres tècniques. Els headers del servidor són més robusts perquè el navegador els respecta abans de carregar qualsevol JavaScript.

Web Application Firewalls (WAF)

Un Web Application Firewall (WAF) és una capa de protecció que filtra, monitoritza i bloqueja tràfic HTTP maliciós entre internet i l’aplicació web. Opera a nivell 7 (capa d’aplicació) del model OSI, permetent inspeccionar el contingut de les peticions. La majoria de sites web amb tràfic significatiu utilitzen algun tipus de WAF avui dia.

Els WAFs poden desplegar-se a l’edge (CDN/cloud) o a nivell d’aplicació. Els proveïdors de CDN ofereixen WAFs gestionats que filtren tràfic abans que arribi a l’origen. En el món open source, ModSecurity (integrable amb Nginx o Apache) és la referència històrica.

L’OWASP Core Rule Set (CRS) és un conjunt de regles genèriques que protegeix contra els atacs més comuns: SQL injection, XSS, LFI/RFI, i altres. Proporciona un punt de partida sòlid, però sovint requereix tuning per evitar falsos positius en aplicacions específiques.

Els WAFs moderns van més enllà de regles estàtiques, incorporant machine learning per detectar anomalies i rate limiting integrat. Tanmateix, no són una solució màgica: són una capa de defensa adicional, no un substitut de codi segur. Atacants sofisticats poden trobar maneres d’evadir regles WAF, especialment si el WAF no està correctament configurat.

Seguretat en la Càrrega de Fitxers

La càrrega de fitxers és una de les funcionalitats web amb més superfície d’atac. Un fitxer pujat pot ser un webshell (script executable al servidor), explotar vulnerabilitats en parsers d’imatge o PDF, o contenir contingut que travessi el sistema de fitxers del servidor.

El path traversal via nom de fitxer és un dels atacs més immediats: si un atacant puja un fitxer amb el nom ../../config/database.yml i el servidor el guarda utilitzant el nom original, pot sobreescriure fitxers arbitraris. La defensa és simple però crítica: mai s’ha d’utilitzar el nom de fitxer original per a l’emmagatzematge. Cal extreure únicament el nom base (sense components de directori) i, millor encara, substituir-lo completament per un nom generat al servidor (UUID).

La validació del tipus de fitxer no pot basar-se en la capçalera Content-Type enviada pel client, que és completament controlada per l’atacant. Cal inspeccionar el contingut real del fitxer (els primers bytes o magic bytes), ja que cada format té una signatura característica. Existeixen llibreries per a aquesta tasca en tots els llenguatges principals; no s’ha d’implementar manualment.

L’emmagatzematge fora del web root és fonamental. Si els fitxers pujats es guarden en un directori servit directament pel servidor web, un atacant pot pujar un script i executar-lo simplement accedint a la seva URL. Els fitxers han d’emmagatzemar-se en un directori no accessible directament (fora del document root) o en un servei d’objectes extern (S3, Azure Blob). Quan cal servir-los, s’ha de fer a través d’un endpoint controlat que estableixi la capçalera Content-Disposition: attachment per forçar la descàrrega en lloc de l’execució.

El límit de mida s’ha d’imposar al servidor abans de llegir el payload complet, no al client. Un atacant pot enviar una petició sense les restriccions del navegador.

Si l’aplicació accepta arxius comprimits (ZIP, tar), cal limitar la mida descomprimida i el nombre d’entrades abans d’extreure res: un arxiu zip bomb pot tenir 42 KB comprimits que s’expandeixen a centenars de GB. En entorns de producció crítics, els fitxers pujats haurien de passar per un escàner antivirus (ClamAV o un servei cloud) abans de quedar disponibles per a altres usuaris.

Subresource Integrity

Els CDNs permeten carregar llibreries populars ràpidament des de servidors optimitzats. Tanmateix, això introdueix un punt de confiança extern. Si el CDN és compromès o alterat, scripts maliciosos es carreguen en el teu site.

Subresource Integrity (SRI) proporciona protecció calculant un hash criptogràfic del script o stylesheet esperat i incloent-lo en el tag:

<script src="https://cdn.example.com/library.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" crossorigin="anonymous"></script>

El navegador descarrega l’arxiu, calcula el seu hash, i només l’executa si coincideix. Si el CDN serveix un arxiu diferent (maliciós o simplement una versió diferent), el navegador el bloqueja i llença un error. L’atribut crossorigin="anonymous" és necessari per permetre que el navegador llegeixi el contingut per validar-lo.

SRI és especialment important per a scripts crítics com frameworks. Un React compromès podria capturar totes les interaccions de l’usuari. CDNs populars com cdnjs i jsDelivr proporcionen hashes SRI automàticament al costat de cada URL de recurs.

La limitació d’SRI és que requereix saber el hash exacte per endavant. Si el CDN actualitza l’arxiu (fins i tot per fixes de seguretat), el hash canvia i la teva pàgina es trenca. Això requereix processos per actualitzar hashes quan actualitzes dependències.

Third-Party Scripts

Els scripts de tercers (analytics, ads, chat widgets) s’executen amb els mateixos privilegis que el teu propi codi. Poden accedir a localStorage, fer peticions, modificar el DOM, i capturar inputs de l’usuari. Cada script de tercer és un risc potencial.

Content Security Policy ajuda limitant d’on es poden carregar scripts. Si només permets scripts del teu domini i CDNs específics de confiança, scripts arbitraris no poden injectar-se. Tanmateix, scripts permesos encara tenen accés complet.

Els sandbox iframes proporcionen aïllament més fort. Carregant un script de tercer en un iframe amb l’atribut sandbox, pots limitar què pot fer: sandbox="allow-scripts" permet scripts però impedeix navegació, formularis, modals, etc. Els iframes sandbox no poden accedir a localStorage del parent ni llegir cookies.

Tanmateix, molts scripts de tercers requereixen accés al DOM principal per funcionar (per exemple, un widget de chat), fent sandbox impracticable. En aquests casos, l’auditoria regular és essencial. Què fa realment aquest script? Envia dades a servidors externs? Compleix amb GDPR?

Les solucions de server-side tagging mouen l’execució de scripts de tercers al servidor. El client envia dades event al teu servidor, que després les forwarda als serveis de tercers. Això proporciona control sobre quines dades es comparteixen i elimina scripts de tercers del navegador.

Seguretat d’APIs

Autenticació d’APIs

L’autenticació basada en formularis i redireccionaments cap a una pàgina de login funciona per a humans en un navegador, però no per a programes que es criden entre ells. Una API consumida per un servei backend, una app mòbil o un script no té usuari interactiu i no pot seguir un flux de login. Cal un mecanisme diferent.

Les APIs RESTful utilitzen Bearer tokens, típicament JWTs, inclosos a l’header Authorization: Bearer <token> de cada petició. El servidor valida el token criptogràficament sense consultar cap base de dades de sessions, fent el sistema stateless.

Les API keys identifiquen el client o projecte que fa peticions. A diferència dels tokens d’usuari, no representen un usuari específic sinó una aplicació. S’han de transmetre sempre en headers (com X-API-Key), mai en la URL, perquè les URLs es poden guardar en logs, historial de navegador, etc. Les API keys haurien de rotar-se periòdicament, i els sistemes haurien de permetre múltiples keys actives simultànies per facilitar la rotació sense downtime.

Mutual TLS (mTLS) proporciona autenticació bidireccional amb certificats. No només el client verifica la identitat del servidor (com en TLS normal), sinó que el servidor també verifica la identitat del client mitjançant el seu certificat. Això és comú en comunicació microserveis dins d’un clúster on cada servei té el seu propi certificat, sovint gestionat per una service mesh com Istio o Linkerd.

OAuth Client Credentials Flow és ideal per a autenticació màquina-a-màquina. El client (servei) s’autentica directament amb l’authorization server utilitzant les seves credencials i obté un token d’accés. Aquest flow no involucra cap usuari; el token representa el servei mateix.

Mai s’han d’incloure credencials a la URL. Les URLs apareixen en logs de servidor, historials de navegador, capçaleres Referer i eines de monitoratge, sovint en text pla. Tant API keys com tokens han de viatjar sempre en headers HTTP, no com a query parameters.

Rate Limiting i Throttling

El rate limiting protegeix les APIs contra abús, atacs DoS i usage excessiu que pot afectar altres usuaris. Els límits es poden aplicar per IP, per usuari autenticat, per API key, o combinacions d’aquests. Els límits típics són peticions per segon, per minut o per hora.

L’algoritme token bucket és popular: cada client té un “cubell” amb una capacitat màxima de tokens. Els tokens es regeneren a una taxa constant. Cada petició consumeix un token. Si el cubell està buit, la petició es rebutja o es posa en cua. Això permet bursts curts mentre manté una taxa promig.

L’algoritme sliding window manté un comptador per cada finestra de temps (per exemple, cada minut). Quan una petició arriba, es comprova el comptador de la finestra actual i possiblement la anterior. Això proporciona un límit més suau que fixed windows, que poden permetre el doble de peticions al voltant de límits de finestra.

Els headers de resposta comuniquen l’estat dels límits al client:

  • X-RateLimit-Limit: Màxim permès
  • X-RateLimit-Remaining: Quantes queden
  • X-RateLimit-Reset: Quan es restableix el límit
  • Retry-After: Quan tornar a intentar-ho després d’un 429

La implementació pot ser en middleware de l’aplicació, en un API gateway, o utilitzant un store compartit en memòria per a arquitectures distribuïdes. Redis és l’elecció més habitual perquè les seves operacions atòmiques i expiració automàtica de claus s’adapten perfectament a rate limiting.

L’error de disseny més freqüent és aplicar rate limiting per IP en lloc de per usuari o per API key. Un atacant distribuït pot enviar peticions des de milers d’IPs sense activar el límit; alhora, un usuari legítim darrere d’un NAT corporatiu pot ser bloquejat per compartir IP amb desenes de companys.

Input Validation

La validació d’input és la primera línia de defensa contra molts atacs. El principi de whitelist és fonamental: defineix què és acceptable en lloc d’intentar detectar què és maliciós. Per exemple, si esperes un codi postal espanyol, valida que sigui exactament 5 dígits, no intentes detectar patterns d’atac.

JSON Schema i OpenAPI permeten definir l’estructura esperada de les peticions declarativament. Llibreries de validació com Zod (TypeScript), Joi (Node.js) o ajv (JSON Schema genèric) poden automàticament rebutjar peticions que no compleixin l’schema. Això centralitza la validació i la fa consistent entre endpoints.

La verificació de tipus és especialment important en llenguatges dinàmics. Si esperes un número, converteix-lo explícitament i gestiona errors de conversió. No assumeixis que perquè un camp es diu age contindrà un número.

Establir límits és crucial: longitud màxima de strings (prevé buffer overflows i DoS), ranges numèrics, tamany màxim de payload (prevé DoS de memòria). Els valors fora d’aquests límits haurien de ser rebutjats immediatament amb errors 400 clars.

La validació hauria d’ocórrer el més aviat possible en el pipeline de petició, idealment en middleware abans d’arribar a la lògica de negoci. Això manté el codi de negoci net i assegura que la validació no s’oblidi accidentalment.

Un cas especial de validació d’input és la prevenció de Server-Side Request Forgery (SSRF): quan l’aplicació fa peticions HTTP a URLs proporcionades per l’usuari (per exemple, per obtenir una imatge o un webhook), un atacant pot apuntar a serveis interns (http://localhost, http://169.254.169.254 per a metadades cloud, rangs privats com 10.0.0.0/8 o 192.168.0.0/16). Les defenses principals són: mantenir una whitelist de dominis o IPs permesos, rebutjar adreces que resolguin a rangs privats o localhost, no seguir redireccions cegament (una URL pública pot redirigir a una interna), i limitar els protocols acceptats (només HTTP/HTTPS, mai file:// o gopher://).

IDOR i Falles de Lògica de Negoci

La validació d’entrades comprova la forma de les dades. Però un cop les dades són vàlides, cal comprovar si l’usuari té dret a accedir a l’objecte concret que identifiquen.

L’IDOR (Insecure Direct Object Reference) és una vulnerabilitat d’autorització: el servidor utilitza un identificador controlat per l’usuari (un paràmetre de URL, un camp del cos de la petició) per accedir a un recurs, sense verificar que l’usuari autenticat té dret sobre aquell recurs específic. L’exemple canònic és:

GET /api/comandes/1234 → retorna la comanda de l'usuari actual ✓ GET /api/comandes/1235 → retorna la comanda d'un altre usuari ✗

Un atacant simplement incrementa l’identificador i accedeix a dades d’altres usuaris. Això és escalació horitzontal de privilegis: un usuari amb els mateixos permisos que un altre accedeix a les seves dades.

L’error de fons és confondre autenticació amb autorització a nivell d’objecte: saber qui ets no implica saber si pots tocar aquest recurs concret. La correcció és sempre filtrar la consulta per l’identitat de l’usuari autenticat:

-- Insegur: qualsevol usuari autenticat pot accedir a qualsevol comanda SELECT * FROM comandes WHERE id = ? -- Segur: la consulta retorna el recurs només si pertany a l'usuari actual SELECT * FROM comandes WHERE id = ? AND usuari_id = ?

Mai s’ha de fer un fetch i després comprovar: si la consulta retorna dades d’un altre usuari, la informació ja ha estat llegida. El filtre ha de ser part de la consulta.

L’ús d’identificadors no seqüencials (UUIDs) dificulta l’enumeració, però no és una solució: un atacant pot obtenir IDs legítims d’altres maneres. La comprovació d’autorització és sempre necessària.

La mass assignment és una variant relacionada: si el servidor accepta tots els camps del cos de la petició i els assigna directament al model de dades, un atacant pot enviar camps que l’API mai hauria d’acceptar d’un client, com is_admin: true o preu: 0. La solució és whitelistar explícitament quins camps pot modificar un usuari en cada context.

Més àmpliament, les falles de lògica de negoci són situacions en què l’aplicació no fa complir les seves pròpies regles en tots els camins d’execució. Exemples: aplicar un codi de descompte dues vegades, saltar-se un pas obligatori d’un flux, o accedir a un recurs en estat “esborrany” via una crida directa a l’API que la interfície d’usuari mai exposa. Aquestes falles no les detecten les eines automàtiques i requereixen revisió manual i proves amb múltiples comptes d’usuari que intentin accedir als recursos dels altres.

API Gateways

Un API gateway és un reverse proxy que centralitza funcionalitat transversal: autenticació de tokens, rate limiting, validació de peticions contra un esquema OpenAPI, i logging de tot el tràfic. Des de la perspectiva de seguretat, permet aplicar aquests controls una sola vegada en lloc de duplicar-los a cada microservei. Exemples àmpliament adoptats inclouen Kong, Envoy (que també és la base de service meshes com Istio) i Traefik, tots open source. Pràcticament qualsevol arquitectura de microserveis en producció utilitza algun tipus de gateway.

Seguretat en el Desenvolupament Modern

Secrets Management

El problema dels secrets (contrasenyes, API keys, claus de xifratge) és que el codi s’ha de poder executar però no ha de contenir els secrets directament. Fer commit de secrets al repositori és perillós: el git history manté els secrets fins i tot si es eliminen després, i els repositoris privats poden esdevenir públics accidentalment o ser compromesos.

Per a desenvolupament local, les variables d’entorn són un primer pas raonable. Un arxiu .env (que està al .gitignore) conté els secrets, i l’aplicació els llegeix a l’inici. Això separa secrets del codi, però encara requereix distribuir els secrets als desenvolupadors de manera segura.

Els vaults (gestors de secrets centralitzats) van més enllà. HashiCorp Vault (open source) és la solució de referència, i els cloud providers ofereixen les seves pròpies alternatives gestionades. En lloc d’emmagatzemar secrets en arxius o variables d’entorn, l’aplicació consulta el vault en runtime. Això proporciona diversos avantatges: auditoria completa de qui accedeix quins secrets, rotació automàtica de secrets sense redesplegar aplicacions, i control d’accés granular.

En entorns Kubernetes, els Secrets nadius xifren dades at-rest utilitzant la clau de xifratge del clúster. Tanmateix, per defecte aquesta clau està emmagatzemada en etcd sense xifratge addicional. La configuració d’encryption at rest és essencial per a clústers de producció. Solucions d’External Secrets permeten fer commit de secrets xifrats al repositori, que només poden ser desxifrats pel clúster.

Per xifrar arxius de configuració directament en git, eines com SOPS (de Mozilla) utilitzen claus de proveïdors cloud o xifratge modern amb age (substitut lleuger de GPG). Això permet versionar configuració sensible de manera segura.

Les bones pràctiques inclouen mai fer commit de secrets (utilitzar hooks de git per detectar-los), rotar secrets regularment i automàticament quan és possible, i implementar accés basat en rols on només els serveis que necessiten un secret específic poden accedir-hi. Els secrets haurien de tenir scopes mínims: una API key per a un servei específic, no una master key que fa tot.

El risc no desapareix eliminant el secret del repositori. Git manté l’historial complet: un secret que va existir en un commit passat segueix accessible per a qualsevol que cloni el repositori. Si un secret s’ha exposat, la resposta correcta és revocar-lo i generar-ne un de nou immediatament, no simplement eliminar-lo del codi.

Les 12 factors i la seguretat

La metodologia dels 12 factors és la referència dominant per al disseny d’aplicacions cloud-native. Diversos factors tenen implicacions directes de seguretat que els desenvolupadors sovint passen per alt en aplicar-la.

Factor III — Configuració: la configuració específica de l’entorn (credencials, URLs de serveis externs, secrets) ha d’emmagatzemar-se en variables d’entorn, mai al codi font. Un arxiu application.properties o config.yml amb credencials de producció al repositori és una de les filtracions més freqüents. La secció anterior de gestió de secrets implementa aquest principi en detall.

Factor VI — Processos sense estat: l’aplicació ha de ser stateless i no compartir res entre peticions. Les sessions no poden viure a la memòria d’una instància concreta: han d’emmagatzemar-se en serveis de suport (Redis, base de dades). A més de facilitar l’escalat horitzontal, això té un benefici de seguretat directe: si una instància és compromesa i reiniciada, no arrossega sessions actives en memòria.

Factor X — Paritat dev/prod: l’entorn de desenvolupament ha de ser el més similar possible a producció. Moltes vulnerabilitats es descobreixen únicament en producció perquè els entorns de desenvolupament i staging desactiven controls de seguretat per comoditat: bases de dades sense TLS, HTTPS desactivat, middlewares d’autenticació comentats, o configuració CORS permissiva (*). Cada diferència entre entorns és una oportunitat perquè un problema passi desapercebut fins a producció.

Factor XI — Logs com a fluxos d’esdeveniments: l’aplicació ha d’escriure logs a stdout i deixar que la infraestructura els reculli i encamini. Això facilita l’agregació centralitzada de logs descrita a la secció de Logging i Monitoring: si cada servei decideix on escriure els seus logs, coordinar-los per a anàlisi de seguretat és molt més difícil.

Factor XII — Processos d’administració: les tasques puntuals (migracions de base de dades, scripts d’inicialització) han d’executar-se en el mateix entorn i amb el mateix codi que l’aplicació. Executar scripts d’administració localment amb credencials elevades que bypassen el model d’autorització de l’aplicació és un vector d’escalació de privilegis freqüent: si el script fa el que l’aplicació no permetria, s’està saltant un control de seguretat deliberat.

Dependency Security

El programari modern depèn de centenars o milers de paquets de tercers. Cada dependència és codi que no has escrit però que s’executa amb els mateixos privilegis que el teu. Els supply chain attacks exploten això comprometent dependències populars. L’atac a event-stream (2018) o ua-parser-js (2021) van injectar codi maliciós en paquets amb milions de descàrregues.

Les eines d’audit de dependències (npm audit, pip audit, cargo audit) escanegen les dependències contra bases de dades de vulnerabilitats conegudes. Haurien d’executar-se regularment en CI/CD i bloquejar deployments si es troben vulnerabilitats crítiques. Tanmateix, aquestes eines només detecten vulnerabilitats publicades, no codi maliciós encara no descobert.

Eines d’actualització automàtica de dependències com Renovate van més enllà, creant automàticament pull requests per actualitzar dependències quan es publiquen fixes de seguretat. Això redueix el window de vulnerabilitat però requereix processos de test robusts per assegurar que les actualitzacions no trenquin funcionalitat.

Els lock files (package-lock.json, yarn.lock, pnpm-lock.json) són essencials per a seguretat reproducible. Sense ells, npm install podria instal·lar versions diferents cada vegada, potencialment incloent una versió compromesa publicada després del teu últim test. Els lock files asseguren que tothom (desenvolupadors, CI, producció) utilitza exactament les mateixes versions.

La verificació d’integritat va un pas més enllà. npm utilitza checksums SHA-512 per verificar que el paquet descarregat no ha estat alterat. Algunes organitzacions van més lluny, verificant signatures criptogràfiques de paquets quan estan disponibles.

El Software Bill of Materials (SBOM) és una llista completa de tots els components del teu programari, incloent dependències transitives. Formats com SPDX o CycloneDX permeten generar SBOMs automàticament. Això és cada vegada més un requisit de compliance i facilita respondre ràpidament quan es descobreix una vulnerabilitat: pots saber immediatament si estàs afectat.

El framework SLSA (Supply chain Levels for Software Artifacts) defineix nivells de maduresa per a la seguretat de la cadena de subministrament. Des del nivell 1 (documentació bàsica del build) fins al nivell 4 (builds hermètics amb verificació completa), proporciona un camí clar per millorar. L’ecosistema Sigstore implementa SLSA amb components per signar artefactes, emetre certificats efímers, i mantenir registres de transparència. Això permet verificar la procedència d’artefactes sense gestionar claus de llarga durada.

Actualitzar quan es publica una vulnerabilitat no és suficient si no s’han revisat les dependències transitives. La majoria de vulnerabilitats crítiques en producció provenen de paquets que no has escollit directament, sinó d’una dependència d’una dependència. Les eines d’audit modernes mostren l’arbre complet i indiquen exactament quin paquet directe arrossega el paquet vulnerable.

Container Security

Els containers empaqueten aplicacions amb les seves dependències, però això significa que també empaqueten vulnerabilitats. La imatge base és crítica: una imatge amb un sistema operatiu complet pot contenir centenars de paquets que no necessites, cada un una possible font de vulnerabilitats. Imatges mínimes com Alpine Linux o Distroless contenen només el mínim necessari per executar l’aplicació. Per a llenguatges compilats com Go o Rust, fins i tot es poden utilitzar imatges scratch (completament buides) amb només el binari estàtic.

L’escaneig de vulnerabilitats d’imatges hauria de ser part del pipeline CI/CD. Eines com Trivy analitzen imatges per vulnerabilitats conegudes en paquets del sistema i dependències de llenguatge. Poden configurar-se per bloquejar el build si es troben vulnerabilitats crítiques.

Els multi-stage builds milloren la seguretat minimitzant el que va a la imatge final. La primera stage compila el codi amb totes les build tools necessàries. L’stage final només copia el binari compilat i les dependencies runtime a una imatge mínima. Això elimina compiladors, build tools i codi font de la imatge de producció.

Executar containers com a root és perillós. Si un atacant aconsegueix escapar del container, tindrà privilegis de root a l’host. Els Dockerfiles haurien d’incloure USER nonroot o crear un usuari específic. Kubernetes permet enforçar això amb PodSecurityStandards que rebutgen pods que intenten executar com a root.

Els secrets mai haurien de formar part de la imatge (vegeu Secrets Management). Les capes de Docker no s’eliminen mai, fins i tot si s’esborren en passos posteriors: si un RUN afegeix un secret (una clau SSH per clonar un repositori privat, per exemple) i una capa posterior l’elimina, el secret continua present en la capa original i és recuperable per qualsevol amb accés a la imatge. Els secrets han d’arribar en temps d’execució via variables d’entorn o mounts, mai com a pas del build.

Les imatges haurien d’actualitzar-se regularment, no només quan es canvia l’aplicació. Una imatge construïda fa sis mesos pot contenir vulnerabilitats que s’han descobert des de llavors. Rebuilds periòdics utilitzant la imatge base més recent asseguren que els patches de seguretat s’apliquen.

TLS/HTTPS

TLS (Transport Layer Security) xifra les comunicacions entre client i servidor, protegint contra espionatge i man-in-the-middle attacks. TLS 1.3 és la versió actual, llançada el 2018. Elimina cipher suites i opcions insegures, redueix la latència del handshake, i xifra més parts de la negociació. TLS 1.2 és el mínim acceptable en 2025; TLS 1.0 i 1.1 estan deprecated i han estat eliminats dels navegadors principals des de 2020.

Let’s Encrypt va revolucionar l’adopció d’HTTPS proporcionant certificats gratuïts i automatitzats. El protocol ACME permet als servidors demanar i renovar certificats automàticament sense intervenció manual, gestionant tot el cicle de vida dels certificats.

La configuració del servidor TLS és crucial. Cipher suites modernes com TLS_AES_128_GCM_SHA256 proporcionen xifratge fort eficient. Suites antigues amb RC4 o DES són vulnerables i haurien de desactivar-se. Perfect Forward Secrecy (PFS) utilitza claus efímeres per a cada sessió, assegurant que fins i tot si la clau privada del servidor es compromet en el futur, sessions passades no es poden desxifrar.

OCSP Stapling millora performance i privacitat. Sense ell, el client ha de contactar l’autoritat de certificació per verificar que el certificat no ha estat revocat, afegint latència i permetent a la CA trackejar quins sites visita l’usuari. Amb stapling, el servidor consulta l’estat de revocació i l’inclou en el handshake TLS, eliminant aquesta round-trip.

Certificate pinning en aplicacions mòbils significa hardcodejar la clau pública esperada del servidor. Això protegeix contra CAs compromeses que podrien emetre certificats falsos per al teu domini. Tanmateix, és perillós: si necessites rotar certificats i no has planejat adequadament (incloent backup pins), pots deixar usuaris sense poder connectar. Per això, moltes organitzacions pinen claus intermèdies en lloc de leaf certificates, donant més flexibilitat.

CI/CD Security

El pipeline CI/CD té accés a codi font, secrets, i permisos de deploy. És un objectiu d’alt valor per atacants. El SolarWinds hack (2020) va comprometre el build system per injectar malware en actualitzacions de programari, afectant milers d’organitzacions.

SAST (Static Application Security Testing) analitza codi font buscant patrons vulnerables, detectant SQL injection, XSS, i altres vulnerabilitats comunes. Eines open source com Semgrep o CodeQL són àmpliament utilitzades. Aquestes eines haurien d’executar-se en cada pull request, bloquejant merge si es troben vulnerabilitats crítiques.

DAST (Dynamic Application Security Testing) testeja l’aplicació en execució. Eines com OWASP ZAP (open source) escanegen l’aplicació desplegada i envien inputs maliciosos per detectar vulnerabilitats. DAST detecta problemes que SAST pot perdre (configuracions incorrectes, vulnerabilitats en dependències binàries) però requereix un entorn de test funcional.

El secrets scanning en commits impedeix que secrets arribin al repositori. Eines open source com truffleHog o Gitleaks escanegen cada commit buscant patrons que semblen API keys, contrasenyes, o claus privades. Les principals plataformes de gestió de codi ofereixen secret scanning integrat. Alguns s’integren com a pre-commit hooks, bloquejant el commit localment. Altres escanegen el repositori sencer periòdicament, detectant secrets que podrien haver-se esmunyit.

L’anàlisi de dependències s’ha discutit anteriorment, però val la pena destacar que hauria d’executar-se en cada build, no només periòdicament. Una dependència segura ahir pot tenir una vulnerabilitat publicada avui.

El container scanning verifica imatges abans de desplegar-les. Fins i tot si vas escannejar la imatge quan es va construir, val la pena tornar-ho a fer abans del deploy per detectar vulnerabilitats descobertes mentrestant.

Logging i Monitoring

Els logs de seguretat són essencials per detectar i investigar incidents. Haurien de capturar tots els esdeveniments rellevants per a seguretat: intents d’autenticació (especialment fallits), canvis en permisos o configuració, accés a recursos sensibles, i errors que podrien indicar atacs.

Els intents d’autenticació fallits poden indicar password spraying o credential stuffing. Múltiples fallades des de la mateixa IP o contra el mateix compte són sospitosos. Tanmateix, cal equilibri: bloquejar accounts després de poques fallades facilita denial of service contra usuaris legítims.

Els canvis de permisos són crítics. Si un usuari regular adquireix de sobte permisos d’admin, podria ser escalació de privilegis. Els canvis en configuració de seguretat (security groups, firewall rules) haurien de triggerejar alertes per a review.

L’accés a recursos sensibles hauria de loguejar-se amb detall suficient per auditoria: qui va accedir què i quan. Això és sovint un requisit de compliance (GDPR, HIPAA) i essencial per investigar data breaches.

La centralització de logs és necessària en arquitectures distribuïdes. Stacks com ELK (Elasticsearch, Logstash, Kibana) o Grafana Loki permeten cerques potents i visualitzen tendències, convertint terabytes de logs crus en insights accionables.

La detecció d’anomalies utilitza machine learning per aprendre el comportament normal i alertar sobre desviacions. Un usuari que normalment accedeix des d’Espanya i de sobte accedeix des de Xina és sospitós. Un servidor que normalment serveix 100 requests/minut i de sobte en serveix 10.000 podria estar sota atac.

La retenció de logs equilibra costos d’emmagatzematge amb necessitats d’auditoria i investigació. Les regulacions sovint requereixen períodes mínims de retenció. Una estratègia comuna és mantenir logs detallats per 30-90 dies en hot storage per investigació ràpida, després arxivar-logs en cold storage més econòmic per al període de retenció complet.

Gestió Segura d’Errors

La tensió fonamental dels missatges d’error és que el detall que ajuda un desenvolupador a depurar és exactament el que ajuda un atacant a explotar el sistema. La solució és una separació estricta: el detall complet va als logs del servidor; l’usuari rep un missatge genèric més un ID de correlació que li permet contactar suport sense exposar informació interna.

Els stack traces en les respostes revelen noms de classes, versions de llibreries, camins de fitxers i números de línia — tot el que un atacant necessita per construir un exploit dirigit. Això s’aplica tant a respostes JSON d’API com a pàgines d’error HTML. Els frameworks solen mostrar-los per defecte en mode de desenvolupament; és essencial desactivar-los explícitament en producció.

// Insegur: exposa informació interna { "error": "NullPointerException at com.example.UserService.findById(UserService.java:143)" } // Segur: missatge genèric + ID de correlació per als logs { "error": "S'ha produït un error intern. Si us plau, contacteu suport.", "correlacioId": "f3a2-8b91-4dc0" }

L’ID de correlació és un UUID generat per petició que s’adjunta a totes les entrades de log relacionades. Quan un usuari informa d’un problema, el seu ID de correlació permet localitzar immediatament el context complet als logs del servidor, sense que l’usuari hagi vist res d’allò.

Els missatges d’autenticació mereixen atenció especial: mai s’ha de diferenciar entre “usuari no trobat” i “contrasenya incorrecta”. Tots dos han de retornar exactament el mateix missatge ("Credencials incorrectes") i el mateix temps de resposta (per evitar atacs de temporització). Missatges distints permeten als atacants enumerar quins usuaris existeixen al sistema.

Pel que fa a les categories d’error HTTP: els errors 4xx (error del client) poden acompanyar-se d’un missatge sanititzat que ajudi l’usuari a corregir la petició. Els errors 5xx (error del servidor) han de retornar sempre un missatge genèric i un ID de correlació, mai detalls de la implementació. Retornar un codi 200 amb un cos d’error és un antipatró que trenca el monitoring i hauria d’evitar-se.

La gestió d’excepcions s’ha de centralitzar a la capa de frontera (controladors, gestors de peticions): és aquí on es captura l’excepció, es registra amb tot el context als logs, i es tradueix a una resposta segura. Deixar que les excepcions es propaguin fins al framework sense capturar-les és arriscat, perquè el comportament per defecte dels frameworks sovint inclou informació diagnòstica. Totes les pàgines d’error per defecte del framework han d’estar substituïdes per pàgines personalitzades en entorns de producció.

Cloud Security

Els cloud providers ofereixen infraestructura segura, però la seguretat de les aplicacions és responsabilitat compartida: el provider assegura la infraestructura física, hypervisors i xarxa base; el client és responsable del sistema operatiu, aplicacions, dades i configuració.

IAM (Identity and Access Management) és l’àrea més rellevant per a un desenvolupador. El principi de mínim privilegi s’aplica estrictament: cada usuari, servei o rol només hauria de tenir els permisos mínims necessaris. Els rols són preferibles a les credencials estàtiques per a serveis: una instància o funció serverless rep un rol IAM i els SDKs obtenen automàticament credencials temporals, eliminant el risc de credencials filtrades. MFA hauria de ser obligatori per a tots els usuaris humans amb accés a la consola.

El xifratge hauria d’estar activat a tot arreu. En repòs, els serveis de xifratge gestionats del provider gestionen la rotació de claus i l’auditoria d’accés. En trànsit, TLS entre tots els serveis. Molts serveis cloud l’ofereixen per defecte, però cal verificar-ho i configurar-ho explícitament.

Seguretat en Aplicacions Mòbils

Secure Storage

Les aplicacions mòbils no poden confiar en que l’entorn és segur. Els dispositius poden ser rooted/jailbroken, permetent accés directe al filesystem. Les aplicacions necessiten protegir dades sensibles fins i tot en aquests escenaris.

En iOS, els Keychain Services són el mecanisme recomanat per emmagatzemar secrets. Està integrat amb el Secure Enclave del dispositiu, un coprocessador criptogràfic dedicat. Les dades al Keychain estan xifrades amb claus que mai surten del Secure Enclave. L’accés pot requerir autenticació biomètrica, assegurant que només el propietari del dispositiu pot accedir als secrets.

En Android, EncryptedSharedPreferences i Android Keystore proporcionen funcionalitat similar. EncryptedSharedPreferences xifra automàticament les dades utilitzant claus del Android Keystore. El Keystore hardware-backed (disponible en dispositius moderns) genera i emmagatzema claus en hardware dedicat, fent-les inaccessibles fins i tot amb root access.

Per a aplicacions cross-platform, llibreries com flutter_secure_storage o react-native-keychain proporcionen abstraccions sobre aquests mecanismes natius. Utilitzen Keychain en iOS i Keystore en Android, oferint una API consistent.

El que mai s’hauria de fer és emmagatzemar secrets en SharedPreferences normal (Android) o UserDefaults (iOS). Aquests arxius són plaintext (o fàcilment desxifrables) i accessibles amb root/jailbreak. Tampoc hauria d’emmagatzemar-se informació sensible en la base de dades SQLite de l’app sense xifratge, ja que aquests arxius són igualment accessibles.

Certificate Pinning

En connexions HTTPS normals, el dispositiu confia en certificats signats per qualsevol CA reconeguda. Si un atacant pot comprometre una CA o afegir una CA pròpia al trust store del dispositiu (comú en dispositius corporatius), poden executar man-in-the-middle attacks interceptant tràfic HTTPS.

Certificate pinning hardcodeja quins certificats o claus públiques són vàlides per al teu servidor. Durant el handshake TLS, l’app compara el certificat rebut amb els pins hardcodejats. Si no coincideix, la connexió és rebutjada, fins i tot si el certificat està correctament signat.

Hi ha dues aproximacions: pinar el leaf certificate (el certificat específic del teu servidor) o pinar la clau pública intermèdia. Pinar el leaf requereix actualitzar l’app cada vegada que renovis el certificat (normalment cada 3 mesos amb Let’s Encrypt). Pinar la clau intermèdia dona més flexibilitat ja que pots rotar leaf certificates mentre mantinguis la mateixa CA.

És crucial incloure backup pins. Si el teu certificat principal es compromet o necessites rotar-lo d’emergència, un backup pin permet la transició sense deixar usuaris amb versions antigues de l’app sense poder connectar. Aquests backups poden ser claus de CAs alternatives o certificats futurs que planeje utilitzar.

Cal saber que el certificate pinning és una tècnica en declivi: Android va eliminar el suport natiu a partir d’Android 7 a favor de la Network Security Configuration, i la indústria tendeix cap a Certificate Transparency (logs públics de tots els certificats emesos) com a alternativa menys fràgil. El risc amb pinning és que un error pot bloquejar tots els usuaris. Si el certificat expira i no has actualitzat l’app a temps, o si perds accés a les claus privades i has de canviar completament de CA, usuaris amb l’app instal·lada no podran actualitzar (ja que no poden connectar per descarregar l’update). Per això, algunes organitzacions eviten pinning o utilitzen periods de grace.

Code Obfuscation

El codi de les aplicacions mòbils resideix al dispositiu de l’usuari. Un atacant amb temps i eines pot fer reverse engineering per entendre com funciona l’app, extreure secrets hardcodejats, o trobar vulnerabilitats.

Eines d’ofuscació per Android com R8 (successor de ProGuard, integrat al build system d’Android per defecte) minifiquen i ofusquen el codi Java/Kotlin. Renombren classes, mètodes i variables a noms sense sentit, eliminen codi no utilitzat, i apliquen optimitzacions que fan el bytecode més difícil de seguir. Tanmateix, el bytecode de la JVM és relativament fàcil de decompile, així que ofuscació no és una protecció completa.

En iOS, el compilador genera codi natiu (no bytecode interpretable), fent-lo inherentment més difícil de fer reverse engineering. Tanmateix, eines de desassemblat encara poden analitzar binaris i, amb esforç, reconstruir la lògica.

És important entendre que l’ofuscació només fa el reverse engineering més difícil i costós, no impossible. No és una substitució per a disseny segur. Secrets crítics (claus d’API, encryption keys) mai haurien d’estar hardcodejats en l’app, independentment de l’ofuscació. Aquests han de venir del backend o generar-se dinàmicament.

L’ofuscació també pot complicar el debugging de crashes en producció. Els stack traces contenen noms ofuscats, fent difícil identificar on va ocórrer l’error. Les mapping files (que relacionen noms ofuscats amb originals) han de mantenir-se segures i disponibles per desofuscar crash reports.

Biometric Authentication

L’autenticació biomètrica proporciona seguretat i usabilitat: és més difícil robar una empremta o cara que una contrasenya, i és més convenient per a l’usuari.

En iOS, el LocalAuthentication framework proporciona una API senzilla per sol·licitar autenticació. L’app mai accedeix a les dades biomètriques reals; simplement pregunta al sistema “aquest usuari s’ha autenticat correctament?” i rep un sí o no. Les dades biomètriques romanen al Secure Enclave i mai són accessibles a aplicacions.

Android ofereix la BiometricPrompt API, que proporciona una experiència consistent independentment del mètode biomètric disponible (empremta, face unlock, iris scan). Com iOS, les dades biomètriques estan protegides pel sistema i no accessibles a l’app.

Els fallbacks són essencials. No tots els dispositius tenen capacitats biomètriques, i les biomètriques poden fallar temporalment (mans mullades per empremtes, màscares per reconeixement facial). L’app hauria de permetre autenticació amb PIN o patró quan la biometria no està disponible o falla.

La biometria no hauria de ser l’única capa de seguretat per operacions crítiques. Per transaccions financeres o canvis de configuració sensibles, combinar biometria amb un segon factor (com OTP) proporciona defensa en profunditat.