Un enllaç d'un sol ús que es destrueix amb un GET simple mor en el
moment en què Slack, Teams o una passarel·la de correu l'obren per
generar-ne una previsualització. La solució és tornar només metadades
al GET i destruir el secret amb una petició POST explícita en
mostrar-lo, amb una frase de contrasenya enviada per un segon canal
com a protecció addicional. Aquest article explica quins sistemes
obren enllaços sense demanar permís i com dissenyar el servei perquè
cap d'ells no el faci fallar.
El generador d'enllaços secrets d'un sol ús de LinkPilot funciona així: una petició de previsualització només rep l'estructura de la pàgina; el secret s'obté, i la visualització es compta, quan un navegador executa la pàgina.
La fallada per defecte
La majoria de clients de xat i de correu intenten mostrar una previsualització quan veuen un URL. El flux és aquest:
- Un usuari enganxa l'enllaç secret a Slack.
- El previsualitzador de Slack obre l'URL (fa un
GET; Slack documenta aquest comportament a link unfurling). - El vostre gestor de visualització tracta la petició com si fos una persona, destrueix el secret i en retorna el contingut.
- Slack rep el contingut ja destruït (o una pàgina genèrica) i el mostra.
- La persona fa clic a l'enllaç, veu «ja s'ha vist» i demana a qui l'ha enviat un enllaç nou.
El previsualitzador no tenia cap mala intenció. El vostre gestor ha confós «algú ha obert l'URL» amb «el destinatari ha llegit el secret». Són esdeveniments diferents, i tot el problema de disseny consisteix a mantenir-los separats.
Al pas 3 s'amaga una variant pitjor: si el GET del previsualitzador
ha rebut el contingut real, el secret ara és dins la infraestructura
de previsualitzacions d'un tercer. En el millor dels casos s'ha
descartat després de generar la previsualització, però no teniu cap
manera de confirmar-ho, ni cap esdeveniment d'auditoria que us digui
que ha passat.
Qui obre un enllaç abans que el destinatari?
Els previsualitzadors dels xats, les passarel·les de seguretat del correu, la protecció dels dispositius, els servidors intermediaris corporatius i les extensions de previsualització, i normalment més d'un alhora. Dir que «Slack genera previsualitzacions» es queda curt. Un URL enganxat en un entorn de treball actual l'obren normalment diversos sistemes abans que cap persona hi faci clic:
| Qui l'obre | Quan ho fa | Es pot identificar? |
|---|---|---|
| Previsualitzadors de xat (Slack, Teams, iMessage, WhatsApp, LinkedIn) | En el moment d'enviar el missatge | Normalment sí: user agents propis com Slackbot-LinkExpanding |
| Passarel·les de seguretat del correu (Mimecast, Proofpoint, Microsoft Defender) | En el lliurament, i sovint una altra vegada en el moment del clic | Sovint no: moltes es presenten amb el user agent d'un navegador real |
| Protecció de dispositius / antivirus | Quan l'URL arriba a l'ordinador del destinatari | Rarament |
| Servidors intermediaris web corporatius | En el moment del clic, abans de reenviar la petició de l'usuari | Rarament |
| Extensions de navegador que previsualitzen enllaços | En passar el ratolí per sobre o en carregar la pàgina | No: funcionen dins la sessió del navegador de l'usuari |
El comportament dels proveïdors d'aquesta taula canvia amb el temps:
preneu les afirmacions concretes sobre cada passarel·la com «segons
el que es publicava el juliol del 2026, comproveu-ho amb la
documentació actual». El que tenen en comú, en canvi, no canvia: tots
aquests sistemes fan un GET simple, perquè és l'única manera segura
i universal de mirar un URL que no controleu.
Com s'evita que una previsualització destrueixi un enllaç d'un sol ús?
Doneu metadades al GET i destruïu el secret només amb un POST
explícit. La semàntica d'HTTP ja ho preveu:
l'RFC 9110, secció 9.2.1
defineix GET com a mètode segur i idempotent, és a dir, una petició
que qualsevol pot fer lliurement perquè no hauria de canviar res.
POST és el verb d'acció. Per tant, tracteu la visualització com una
acció POST darrere d'un botó visible:
GET /s/abc123 → HTML page: "Click to reveal this secret"
POST /s/abc123 → burns the secret, returns the payload
Ara el GET de Slack retorna una pàgina neutra de metadades. El
secret només mor quan la persona fa clic al botó. Cap escàner que
respecti la semàntica d'HTTP no toca mai el contingut, i gairebé tots
la respecten, perquè enviar POSTs amb efectes a URL arbitraris
trencaria mig web.
Un recorregut pas a pas
Seguiu un enllaç de principi a fi. Creeu un secret que es destrueix després de la lectura, amb frase de contrasenya, i enganxeu l'URL en un missatge directe de Slack i en un correu.
- El previsualitzador de Slack fa
GET /s/abc123. Resposta: una petita pàgina de metadades (estat actiu, cal frase de contrasenya). No es registra cap visualització. El secret continua viu. - La passarel·la de correu del destinatari reescriu l'URL del
correu i l'obre en el moment del lliurament per analitzar-ne la
reputació. El mateix
GET, les mateixes metadades. Continua viu. - El destinatari fa clic a l'enllaç del correu. L'escàner de la
passarel·la l'obre una vegada més en el moment del clic (
GET) i després redirigeix el navegador. - El navegador carrega la pàgina per mostrar el secret, amb un
altre
GET. La pàgina mostra un botó i un camp per a la frase de contrasenya. Encara no s'ha registrat cap visualització. - El destinatari introdueix la frase de contrasenya i fa clic al
botó. El navegador envia
POST /s/abc123. Es registra la visualització, es retorna el contingut i el secret es destrueix. - Qualsevol que obri l'URL després rep una resposta explícita que diu que ja s'ha vist i s'ha destruït, en lloc del contingut.
Cinc peticions, una visualització. Aquesta proporció és normal en un entorn corporatiu. Un disseny que no l'aguanti fallarà el primer dia, i qui envia acabarà enganxant la contrasenya directament al xat.
Defenses en capes
La separació entre GET i POST
Resol el cas de tots els previsualitzadors i escàners que es comporten bé, que són la immensa majoria del trànsit automatitzat. Aquesta és la base; tota la resta afegeix profunditat.
Frase de contrasenya
Cobreix el que la separació no pot cobrir: els entorns aïllats que executen JavaScript i interactuen amb la pàgina, a més dels clics accidentals de persones i dels correus reenviats. Un entorn aïllat d'anàlisi pot fer clic a un botó; no pot proporcionar una frase de contrasenya que ha viatjat per un altre canal. Envieu l'enllaç per correu i la frase de contrasenya per un missatge directe de Slack o per SMS.
Filtratge de bots pel user agent
Descarteu els bots de previsualització coneguts (Slackbot,
LinkedInBot, Twitterbot) abans que arribin a la lògica de
visualització. Val la pena fer-ho com a precaució extra, però mai no
és suficient per si sol: els escàners de seguretat es presenten
habitualment amb el user agent d'un navegador real precisament perquè
les pàgines malicioses no es puguin amagar de l'anàlisi.
Caducitat curta
Limiteu el període de risc. Un enllaç que ha viscut quinze minuts deixa als escàners, als fitxers de registre i a l'historial del xat un URL que ja no porta enlloc.
Cache-Control: no-store
Poseu-lo a cada resposta de visualització perquè cap intermediari (servidor intermediari corporatiu, CDN, memòria cau del navegador) no conservi una còpia del contingut després de l'única lectura legítima.
El cas de les passarel·les de seguretat del correu
Les passarel·les corporatives mereixen una secció pròpia perquè obren l'enllaç dues vegades. Els productes d'aquesta mena (Microsoft Defender Safe Links n'és l'exemple documentat) reescriuen cada URL del correu entrant perquè passi pel seu propi domini d'anàlisi. Normalment l'URL s'obre una vegada en el lliurament, per analitzar-ne la reputació, i una altra en el moment del clic, abans de redirigir la persona a la destinació real.
Si qualsevol d'aquestes peticions destruís el secret, el destinatari
veuria sempre un enllaç mort: una fallada que als usuaris els
sembla aleatòria però que és del tot determinista. La separació entre
GET i POST absorbeix les dues peticions sense cap cost, perquè
totes dues són GETs simples. Després, la redirecció del moment del
clic porta la persona a la pàgina de visualització, on es fa el
POST.
Casos límit que encara fan mal
- Entorns aïllats que executen JavaScript. Alguns entorns
d'anàlisi renderitzen la pàgina i hi interactuen. En principi, un
entorn agressiu podria fer clic a un
POSTdarrere d'un botó sense frase de contrasenya. La frase de contrasenya és la protecció addicional. - Enllaç i frase de contrasenya pel mateix canal. Si viatgen tots dos en un mateix correu, totes les defenses anteriors queden reduïdes a «esperem que ningú no llegeixi aquest correu». Separeu sempre els canals.
- Reenviaments. Cada reenviament a un xat o un sistema de correu nou provoca una nova ronda de previsualitzacions. Amb la separació no fa cap mal, però cada salt és un altre conjunt de registres que conserven l'URL, i això és un argument a favor de la caducitat curta.
- Visualitzacions simultànies. Si dues persones fan clic al botó alhora, la lectura i la destrucció han de passar en un sol pas indivisible: exactament una petició guanya i l'altra rep «ja s'ha vist». Una implementació que les separi pot servir el contingut dues vegades.
Comproveu la vostra eina
Abans de confiar credencials reals a qualsevol servei per compartir secrets, feu aquesta comprovació de dos minuts:
- Creeu un secret de prova que es destrueixi després de la lectura.
- Obriu-lo des d'un terminal, que és exactament el que fa un
previsualitzador:
curl -s -o /dev/null -w "%{http_code}" "<url>" - Obriu l'enllaç en un navegador. Si veieu «ja s'ha vist», l'eina
destrueix el secret amb el
GETi no és segur enganxar-ne els enllaços en un xat o un correu. - Si ha sobreviscut, mostreu-lo correctament al navegador i torneu a
executar
curl: ara hauríeu de rebre una resposta explícita que indiqui que ja no existeix o s'ha destruït, no el contingut.
Com ho implementa LinkPilot
Perquè consti, aquest és el disseny que fan servir els enllaços
secrets de LinkPilot: un GET al punt de visualització només retorna
metadades (l'estat i si cal frase de contrasenya), i només es
registra una visualització amb un POST explícit. Les frases de
contrasenya es converteixen en un hash SHA-256 al client i tenen un
límit de cinc intents per IP, per minut i per secret. Cada resposta
de visualització porta Cache-Control: no-store, els secrets
destruïts o caducats retornen HTTP 410 amb el contingut desat
sobreescrit, i la cronologia d'auditoria registra cada esdeveniment
amb la IP convertida en hash amb una sal que canvia cada dia: la IP en
brut no es desa mai. Teniu tots els detalls a la pàgina
d'arquitectura de seguretat.
Proveu-la gratis, sense registre
Missatge que s'autodestrueix
Envieu una nota que desapareix després de llegir-la una vegada.
Límits d'aquest disseny
Els límits, amb franquesa: res de tot això no protegeix un secret després d'una visualització legítima, perquè el destinatari pot copiar el contingut, fer-ne una captura de pantalla o reenviar-lo. No serveix de res si la frase de contrasenya viatja amb l'enllaç. I no pot impedir que una persona que ha rebut l'enllaç per error l'obri abans que el destinatari previst. Per a això hi ha la cronologia d'auditoria: veieu la visualització inesperada i roteu la credencial.
Lectures relacionades
- Com funcionen els enllaços secrets d'un sol ús
- Per què els URL filtren informació confidencial
- Arquitectura de seguretat de LinkPilot
Referències
- IETF, RFC 9110: HTTP Semantics, section 9.2.1 Safe Methods.
- Slack API, Unfurling links in messages.
- Microsoft Learn, Safe Links in Microsoft Defender for Office 365.
