Quando un gateway di pagamento con carta distribuisce le sue librerie solo per ASP, PHP e Java, integrarlo da Python o da Zope non è una questione di binding: si riscrive l’intero handshake con il server della banca. Il protocollo, però, non cambia con il linguaggio. È un round-trip cifrato fra negozio e gateway protetto da un segreto condiviso, e questa è la parte che va capita prima di scrivere una riga di codice.
Il problema
Nel 2003 chi vende online in Italia appoggia l’incasso a un gateway bancario: il negozio non vede mai il numero di carta, che viene digitato su una pagina ospitata dalla banca. Il sistema GestPay di Banca Sella è uno di questi. Il merchant prepara la transazione, la banda raccoglie i dati di carta sul proprio dominio, autorizza con il circuito e restituisce l’esito al negozio.
Il punto debole non è la crittografia in sé — sotto c’è TLS 1.0 (RFC 2246) e il modello a tre domini di Verified by Visa, attivo dal 2001, che sposta l’autenticazione del titolare sul dominio dell’emittente. Il punto debole è il confine fra l’applicazione del merchant e il gateway: come il negozio dichiara quale importo incassare e quale transazione sta autorizzando, in modo che la banca non accetti parametri manomessi dal browser dell’utente.
GestPay risolve questo confine con un segreto condiviso e una doppia cifratura. E le librerie ufficiali per farlo, all’epoca, esistono per i linguaggi mainstream del web aziendale: ASP, PHP, Java. Per chi costruisce su Python e su Zope — l’application server da cui nel 2003 nasce Plone 1.0, appoggiato a CMF 1.3 — non c’è nulla. Il codice va scritto.
Architettura del round-trip
Il flusso ha tre momenti, e nessuno dei tre passa il numero di carta dal negozio.
- Cifratura. Il merchant compone i parametri della transazione — codice esercente, importo, valuta, identificativo di transazione, dati dell’acquirente — e li manda al server GestPay con una chiamata
crypt. Il gateway risponde con una stringa cifrata: nel formato proprietario è racchiusa fra tag#cryptstring#. Questa stringa, non i parametri in chiaro, è ciò che il browser dell’utente porta al dominio della banca. - Pagamento. Sul dominio del gateway l’utente inserisce i dati di carta. Autenticazione del titolare, autorizzazione sul circuito e raccolta del PAN avvengono fuori dal negozio.
- Decifratura. A esito ottenuto il gateway rimanda al merchant una stringa cifrata, racchiusa fra tag
#decryptstring#. Il negozio la passa a una chiamatadecryptverso il server GestPay e ne estrae esito, codice di autorizzazione, eventuali errori e alert.
Il negozio non cifra e non decifra in proprio: delega le due operazioni al server della banca, che è l’unico a possedere la chiave associata al codice esercente. Il segreto condiviso non è una chiave che gira nel codice del merchant; è la relazione fra codice esercente e gateway. Il trasporto delle due chiamate è HTTP GET verso i server ecomm.sella.it.
Una libreria Python che incapsula tutto questo — la classe che chiama crypt, quella che chiama decrypt, il parsing della risposta in attributi dell’oggetto (importo, stato, codice errore, dati carta mascherati) — è esattamente ciò che noze rilasciò nel 2003 come open source con GestPayCrypt e la variante GestPayCryptHS, fino al pacchetto Zope/CMF per innestare il pagamento nei portali Plone: https://www.noze.it/open-source/pygestpaycrypt/.
Il punto critico: il GET
La scelta del trasporto su HTTP GET non è neutra. Un parametro in query string finisce nei log del web server, nei log dei proxy intermedi, nella cronologia del browser e, se la pagina contiene link esterni, nell’header Referer inviato a terzi. La sezione 15 di RFC 2616 segnala proprio questi canali: una URL non è un posto dove mettere dati sensibili.
Nel modello GestPay questo rischio è contenuto da come è disegnato il flusso, non dal trasporto. Sulla query string del browser viaggia la stringa già cifrata dal gateway, non i parametri in chiaro: chi legge i log vede #cryptstring#…, non l’importo né i dati dell’acquirente. Le chiamate crypt e decrypt con i parametri in chiaro avvengono server-to-server, dal merchant al gateway, su TLS — non dal browser. E il dato che la normativa protegge davvero, il PAN, non transita mai dal negozio: lo raccoglie il dominio della banca. La separazione dei domini fa sì che il merchant non sia mai depositario del numero di carta — la condizione che nel 2001 il programma Visa CISP, poi confluito nel PCI Data Security Standard 1.0 del dicembre 2001, pone al centro degli obblighi del venditore.
Resta che il GET è una scelta da maneggiare con cautela: ogni parametro aggiuntivo che un’integrazione mettesse in query string oltre alla stringa cifrata — un identificativo di sessione, un riferimento d’ordine in chiaro — rientrerebbe nei canali di RFC 2616. La libreria deve far passare per la cifratura tutto ciò che è sensibile, e lasciare in chiaro solo ciò che è già pubblico.
Implicazioni
La prima implicazione è che il linguaggio dell’SDK non descrive il modello di sicurezza. Riscrivere il client in Python non indebolisce nulla finché si rispettano i tre invarianti: cifratura lato gateway, PAN fuori dal negozio, parametri sensibili mai in chiaro sulla query string. Una libreria di terze parti che li mantiene è equivalente, sul piano della sicurezza, all’SDK ufficiale; una che li violasse — per esempio cifrando lato client con una chiave hardcoded — sarebbe pericolosa anche se scritta in Java.
La seconda è che l’open source qui ha un valore operativo, non ideologico. Una libreria di pagamento ispezionabile permette di verificare che la chiave non sia nel codice, che i parametri sensibili passino dalla cifratura, che la risposta sia validata prima di marcare un ordine come pagato. Su un componente che decide se incassare o no, poter leggere il codice è una garanzia di audit. La libreria fu distribuita sotto LGPL — nella forma della GNU Lesser General Public License 2.1 del febbraio 1999, che ne consente l’uso anche in applicazioni proprietarie purché le modifiche alla libreria restino aperte.
Limiti
Il modello descrive il confine fra negozio e gateway, non l’intera superficie d’attacco di un e-commerce. Restano fuori la validazione dell’esito lato merchant — un negozio che si fidasse della sola redirezione di ritorno, senza ricontrollare la stringa decrypt, sarebbe aggirabile manipolando il browser — la gestione delle transazioni duplicate, la riconciliazione contabile e la protezione della pagina che invoca la cifratura. E il formato proprietario a tag #cryptstring# lega l’integrazione a uno specifico gateway: un cambio di banca significa riscrivere il client, non riconfigurarlo. La libreria rende GestPay raggiungibile da Python; non rende il pagamento online un problema risolto.
- https://www.rfc-editor.org/rfc/rfc2246
- https://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html
- https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
- https://en.wikipedia.org/wiki/3-D_Secure
Immagine di copertina: logo del progetto PyGestPayCrypt (noze).