00 / Arquitectura
Com està construït aquest web
Treballo en un SOC, així que aquest web és també una declaració d'intencions: si no fos capaç d'assegurar el meu propi lloc, poc tindria a aportar assegurant el de ningú. A sota hi ha les decisions que hi ha al darrere i el motiu de cadascuna, no una llista de tecnologies.
Resum del stack
- Frontend
- Astro 7 (SSG) · Tailwind CSS 4 · TypeScript
- Backend
- PHP 8 (PDO) · MySQL / MariaDB · sin framework
- Infraestructura
- CDMON · Apache · Cloudflare · TLS
- Idiomes
- Español · English · Català
01 / Superfície d'atac del frontend
El lloc es compila a HTML pla, de manera que la major part del risc clàssic de servidor desapareix. El que queda es tanca per capçalera.
-
CSP amb hash recalculats a cada compilació
script-src porta el SHA-256 de cada script del lloc i no inclou ‘unsafe-inline’. Un <script> injectat no s'executa: no pot portar un hash que la política no conegui. Els hash es generen sols en compilar, no es mantenen a mà.
astro.config.mjs -
HSTS a un any, deliberadament sense preload
includeSubDomains actiu. Es deixa fora de la llista de precàrrega expressament: entrar-hi és fàcil i sortir-ne triga mesos, i per a un portfolio aquest compromís no compensa.
server/.htaccess -
Aïllament del document
X-Frame-Options DENY contra el clickjacking, Cross-Origin-Opener-Policy i Cross-Origin-Resource-Policy en same-origin, i Permissions-Policy que apaga geolocalització, micròfon i càmera encara que res no les demani.
server/.htaccess -
Res de tercers
Sense CDN, sense Google Fonts, sense etiquetes d'analítica externa. Les tipografies van autoallotjades amb subset llatí. Cada domini que es treu és un tercer menys que pot caure, canviar el fitxer que serveix o registrar la IP del visitant.
-
Filtratge de projectes interactiu en client sense estat
El filtratge de projectes per etiquetes s’executa íntegrament al navegador mitjançant manipulació de classes CSS. Evita desar estats en sessió o fer consultes HTTP addicionals per re-renderitzar, minimitzant la transferència de dades i la càrrega del servidor.
src/components/Projects.astro
02 / Consola d'administració
La part amb accés a la base de dades i a les pujades. Aquí és on el detall importa de debò.
-
Consultes preparades reals, no emulades
PDO amb ATTR_EMULATE_PREPARES a false. Amb l'emulació activada el driver interpola els valors al client abans d'enviar-los, i aquesta interpolació és justament on han viscut històricament els salts d'entrecometat en certs jocs de caràcters.
server/db.php -
Login que no revela si l’usuari existeix
Contra un usuari inexistent es verifica igualment un hash fictici. Sense aquest pas, la resposta torna abans quan l'usuari no existeix i el temps permet enumerar comptes vàlids. Les contrasenyes van amb bcrypt i es rehashegen soles si puja el cost.
server/admin/auth.php -
CSRF en temps constant, amb el cas buit cobert
El token es compara amb hash_equals. A més s'exigeix que el de la sessió existeixi i no estigui buit: sense aquesta condició, una petició sense galeta creava una sessió nova amb el token a cadena buida i la comparació de dues cadenes buides retornava cert, deixant passar la petició.
server/admin/auth.php -
Sessions endurides i amb caducitat doble
Regeneració de l'identificador després del login (fixació de sessió), galetes HttpOnly, Secure i SameSite=Lax, use_strict_mode perquè ningú no imposi un identificador propi, i tancament tant per inactivitat com per durada màxima absoluta.
server/admin/auth.php -
Bloqueig de força bruta per IP
Els intents fallits es compten en una finestra temporal i bloquegen la IP en superar el llindar. La IP es pren de REMOTE_ADDR llevat que es declari explícitament un proxy de confiança: fiar-se de X-Forwarded-For sense proxy al davant permet falsejar la IP i saltar-se el límit sencer.
server/lib/http.php -
Markdown segur al bàner superior
El text del bàner superior s’edita en Markdown i es parseja al navegador. Abans de la conversió a HTML (enllaços, negretes, codi), la cadena s’escapa completament contra XSS, impedint que una potencial injecció en base de dades executi codi script.
src/components/Announcement.astro -
Controlador preventiu de migracions de base de dades
El script d’inicialització verifica les taules i columnes necessàries en carregar la consola d’administració. Si falta alguna columna (per exemple, després de desplegar canvis de l’esquema), mostra una pantalla de manteniment amb instruccions de migració segures en comptes d’una fallada fatal de SQL.
server/lib/bootstrap.php
03 / Contingut que no em pertany
Imatges pujades a la consola i missatges escrits per desconeguts: tot el que entra de fora es tracta com a hostil fins que es demostri el contrari.
-
Les imatges es validen per contingut, no per extensió
Primer el tipus MIME real amb finfo, i després getimagesize(), que només reconeix imatges de debò. Un polyglot amb capçalera GIF falsificada passa el primer filtre però no el segon. Hi ha a mes un topall de dimensions perquè ningú no tombi el servidor amb una imatge de 30000 px de costat.
server/lib/upload.php -
La carpeta de pujades no executa codi
Defensa en profunditat: encara que la validació fallés i s'hi colés un .php, el .htaccess d'aquella carpeta retira els handlers i apaga el motor, de manera que es serviria com a text pla. Els fitxers es desen amb nom aleatori, mai amb el que envia el client.
server/uploads/.htaccess -
El formulari no pot convertir-se en un relay de spam
S'eliminen salts de línia i caràcters de control abans que cap valor toqui una capçalera de correu. Sense aquest sanejament, un nom amb CRLF permet injectar capçaleres (un Bcc:, per exemple) i fer servir el formulari per enviar correu a tercers.
server/api/contact.php -
Exportacions CSV sense injecció de formules
Excel i LibreOffice executen com a formula qualsevol cel·la que comenci per =, +, - o @. Com que la safata exporta text escrit per desconeguts, aquestes cel·les es prefixen perquè es tractin com a text i no com a codi en obrir el fitxer.
server/admin/partials/layout.php
04 / Analítica pròpia i privacitat
Saber quanta gent hi entra no exigeix muntar el perfil de ningú. L'analítica és pròpia i desa el mínim.
-
L'adreça IP no s'emmagatzema
Es desa un SHA-256 de la IP amb una sal del servidor. Serveix per comptar visitants únics aproximats i per frenar floods, però no permet reconstruir l'adreça ni creuar-la amb cap altra base de dades.
server/api/visit.php -
Sense galetes de seguiment i amb Do Not Track respectat
No hi ha bàner de consentiment perquè no hi ha res a consentir. Si el navegador envia el senyal Do Not Track, la visita ni tan sols es registra.
-
La sessió viu i mor amb la pestanya
Per saber si dues pàgines vistes són la mateixa visita, el navegador desa un identificador aleatori a sessionStorage (no localStorage, no una galeta). Tècnicament segueix sent "emmagatzematge a l'equip" sota l'ePrivacy, però en morir en tancar la pestanya i no combinar-se mai amb una altra visita, encaixa en l'excepció de mesura d'audiència que reconeixen la majoria d'autoritats de protecció de dades de la UE: per això no canvia la resposta d'aquí dalt.
src/scripts/analytics.ts -
Del referrer es descarta la query
Només es conserva esquema, host i ruta. Els paràmetres d'una URL aliena solen portar identificadors de sessió o de campanya, i no hi ha cap raó perquè acabin a la meva base de dades.
server/api/visit.php -
Retenció acotada i purga automàtica
Les visites s'esborren als 400 dies, els intents de login als 90 i el registre d'auditoria a l'any. La purga va incorporada al mateix flux, sense dependre d'un cron que ningú no recorda revisar.
server/api/visit.php
05 / SEO tècnic
Un portfolio que no apareix no serveix de res. El posicionament aquí és una qüestió d'estructura, no de repetir paraules clau.
-
Un únic graf de dades estructurades
Person, WebSite, ProfilePage, BreadcrumbList i FAQPage viatgen en un sol bloc JSON-LD enllaçat per @id, en comptes de blocs solts que poden contradir-se entre ells. Cada article del blog hi afegeix el seu propi BlogPosting.
src/components/Seo.astro -
410 Gone en lloc de 404 per al que s’ha retirat
Dos projectes es van retirar del lloc amb les URL ja indexades. Un 404 diu al cercador que la pàgina podria tornar, i la reintenta durant setmanes; un 410 li diu que la retirada és deliberada i definitiva, i la treu de l'índex força abans.
server/.htaccess -
hreflang reciproc, i només on hi ha traducció
Cada pàgina traduïda declara totes les seves germanes i un x-default. Les que existeixen en un sol idioma no en declaren cap: anunciar un alternatiu que no existeix trenca el grup sencer, i Google llavors el descarta del tot.
src/pages/sitemap.xml.ts -
SEO local amb senyals coherents
Badalona i Barcelona apareixen al títol, a la descripció, a les etiquetes geo i dins del bloc Person amb address, homeLocation i areaServed. El senyal importa quan és el mateix a tot arreu, no quan es repeteix en un sol lloc.
src/config.ts
06 / Llegible per a models de llenguatge
Cada cop es pregunta més a un assistent i menys a un cercador. Si la resposta sobre mi l'ha de donar una IA, prefereixo que llegeixi dades ordenades i no que improvisi.
-
llms.txt generat a cada compilació
Un resum en text pla seguint la convenció de llmstxt.org, sense HTML ni soroll. Surt de les mateixes fonts que alimenten el web, així que no pot quedar desfasat respecte del que es veu a la pantalla.
src/pages/llms.txt.ts -
Política explicita per a rastrejadors d’IA
GPTBot, ClaudeBot, PerplexityBot, Google-Extended i Applebot tenen permís escrit. Els rastrejadors de SEO comercial, que només consumeixen amplada de banda de l'allotjament compartit, estan bloquejats. És una decisió presa, no el resultat de no haver tocat el fitxer.
public/robots.txt -
Divulgació de vulnerabilitats per RFC 9116
Un /.well-known/security.txt amb contacte, idiomes i data de caducitat. És el primer que busca qui troba una fallada i la vol reportar bé; no tenir-lo és la raó habitual que un avis acabi en un formulari generic o enlloc.
public/.well-known/security.txt -
Anàlisi estàtica a cada push
CodeQL cobreix el frontend en TypeScript i JavaScript. Com que CodeQL no analitza PHP, el backend el revisa Semgrep amb regles d'injecció, XSS i secrets, i tots dos resultats arriben en format SARIF a la mateixa pestanya. Dependabot vigila les dependencies cada setmana.
.github/workflows/codeql.yml
Cada punt d'aquesta pàgina correspon a codi que pots obrir al repositori. Si trobes res que no quadri, el canal per avisar-me és a /.well-known/security.txt
Mètriques de rendiment (PageSpeed Insights)
* Vista Mòbil, mesurada el 23/08/2026 amb Lighthouse contra producció (CDMON, HTTPS i TLS actius). És una foto d'un moment concret, no una garantia permanent: comprova-ho tu mateix si t'interessa.