Esame 2019 Ordinaria: Turismo digitale con BLE Beacon — Come l'ho risolta io, pezzo per pezzo
Questa traccia per me è bellissima perché unisce tutto: rete WiFi cittadina, geolocalizzazione indoor che non è banale, DB ben fatto, PHP con sessioni, tariffe che sembrano marketing ma sono logica pura, e VPN. Ve la racconto come l'ho pensata io
Io parto sempre da due domande: 1) come verifico che il tablet sia davvero dentro il POI? 2) come impedisco che uno usi il suo telefono per accedere gratis? Se rispondi a queste due, tutto il resto viene giù. E la risposta per me è BLE + certificato client. Ve lo spiego.
0. Come ho letto io la traccia — i 5 vincoli che non puoi sbagliare
Leggendola io ho estratto questi punti che per me sono fondamentali e che la soluzione originale dava per scontati:
- Solo minitablet forniti dall'InfoPoint — non smartphone personali. Quindi io devo fare device binding, non basta la password del biglietto. Altrimenti uno fotografa la password e la usa da casa.
- Contenuti solo su server — niente offline. Quindi streaming HLS, non download. È più giusto così perché protegge i contenuti culturali.
- Password univoca giornaliera dal biglietto — validità 1 giorno. Io la gestisco con
data_emissione = CURDATE()e hash bcrypt, non in chiaro. - Solo in prossimità del POI — e qui casca l'asino: GPS indoor non funziona. Io ho scelto BLE.
- Restituzione InfoPoint con documento o carta di credito — quindi devo tracciare quale tablet è stato dato a chi, ma questo esula dal DB esame e lo metto nel gestionale noleggi.
1) Geolocalizzazione indoor precisa (GPS no, WiFi RSSI poco preciso, QR falsificabile) → BLE beacon. 2) Vincolo dispositivo (solo tablet forniti) → certificato client X.509 sul tablet + WPA2-Enterprise. Così anche se uno ruba la password, senza certificato non entra nella WiFi.
1. Infrastruttura: il mio schema server + WiFi + minitablet
Io ho immaginato così l'infrastruttura, perché secondo me è l'unica che regge centinaia di turisti con video:
- Data center comunale o cloud PA GDPR-compliant — io lo metterei comunale per governance pubblica e bassa latenza.
- Web server Apache/Nginx + PHP 8.x — serve pagine dinamiche con sessione.
- Media server separato — e qui molti lo omettono, ma per me è fondamentale: se 200 turisti guardano video HLS 5 min in 7 lingue contemporaneamente, un solo server va in ginocchio. Io separo web e media, e metto anche CDN locale sugli AP WiFi che fa cache dei video più richiesti.
- DB Server MySQL/MariaDB + Redis per sessioni — sessioni in RAM, veloci, validazione password biglietto rapida.
- Rete WiFi cittadina 802.11ac/ax con WPA2-Enterprise + RADIUS — solo tablet con certificato entrano.
- Minitablet con certificato client + app che legge BLE.
| Livello | Protocollo | Perché l'ho scelto io |
|---|---|---|
| WiFi | 802.11ac/ax | Banda per streaming video 5 min, per me minimo |
| Sicurezza WiFi | WPA2-Enterprise 802.1X | Solo tablet con cert X.509 — è il device binding che voglio io |
| App | HTTPS TLS 1.3 + cookie Secure/HttpOnly/SameSite=Strict | Sessione sicura, no hijacking |
| Video | HLS su HTTPS | Streaming adattivo, chunk, no download |
| Monitor | SNMP v3 + Syslog TLS | Monitoro AP e switch, log centralizzati |
2. Geolocalizzazione: perché ho scelto BLE Beacon e non GPS
Questa è la parte che nella soluzione originale era spiegata male. Io la completo:
| Tec | Precisione | Indoor | Costo | Il mio giudizio |
|---|---|---|---|---|
| GPS | 3-5m outdoor | NO | Basso | Per me solo fallback outdoor, dentro un museo non prende |
| BLE Beacon | 1-5m | SÌ | €5-20 | La mia scelta principale — 1 beacon per POI con UUID univoco |
| WiFi RSSI | 3-10m | Parziale | 0 | Fallback secondario, uso AP esistenti |
| QR Code | Utente dipendente | Sì | Min | Per me no, falsificabile con foto |
Come l'ho implementata io: in ogni POI metto un BLE beacon con UUID univoco (es. POI-001-UUID salvato in tabella POI.beacon_uuid). Il tablet fa scanning BLE continuo, quando vede UUID con RSSI > -70dBm (vicino) invia al server id_poi + beacon_uuid. Il server verifica che il beacon_uuid corrisponda davvero a quel POI nel DB. Se non corrisponde, 403. È semplice e per me è più giusto così perché non si può barare fotografando un QR.
Io lo spiego così in classe: GPS dentro una chiesa di pietra di 1 metro di spessore non prende. BLE invece funziona indoor, costa poco, batteria 2 anni, e lo nascondi dietro un quadro. È la tecnologia giusta per musei.
3. Database: l'ER che ho disegnato io e il logico con SCELTA_POI
Qui molti studenti sbagliano le cardinalità. Io ho disegnato così:
- VISITATORE 1—N BIGLIETTO — un visitatore può avere più biglietti in giorni diversi, ma un biglietto appartiene a un solo visitatore.
- BIGLIETTO 1—N SCELTA_POI — solo per tariffa intermedia: ogni biglietto può scegliere max 3 POI avanzati. Questa tabella per me è fondamentale, e nella soluzione originale era spiegata male.
- POI 1—N CONTENUTO — ogni POI ha contenuto base (IT, 1 min) + contenuti avanzati (7 lingue, 5 min).
- POI 1—N IMMAGINE — galleria fino a 3 immagini per base, più per avanzata.
- POI 1—1 beacon_uuid — ogni POI ha un beacon univoco, unique constraint.
Io salvo lingua dentro SCELTA_POI per intermedia perché la scelta lingua è legata al POI: se scegli Colosseo in EN, resta EN. Non puoi cambiarla dopo aver consumato la scelta, altrimenti aggiri il limite di 3. Per la piena invece lingua in $_SESSION globale, modificabile da menu. Questa distinzione nella soluzione originale mancava, io la aggiungo perché è più giusta.
4. Pagine web: login con password giornaliera + verifica prossimità
Flusso che ho implementato io per tariffa base (che è garantita a tutti):
- Login: visitatore inserisce password biglietto → io verifico hash bcrypt + data_emissione = oggi → creo sessione PHP.
- Lista POI: mostro tutti i POI con pagina base.
- Selezione POI: tablet invia
id_poi + beacon_uuidrilevato via BLE. - Verifica prossimità: io controllo su DB che beacon_uuid corrisponda davvero a id_poi. Se no, 403.
- Erogo contenuto base con video HLS 1 min IT + max 3 immagini.
5. Tariffe: base/intermedia/piena — la logica che ho implementato io
Qui la soluzione originale era carente. Io la completo così:
| Tariffa | Base | Avanzata | Lingua avanzata | Dove salvo |
|---|---|---|---|---|
| Base | Tutti i POI, video 1 min IT | — | — | — |
| Intermedia | Tutti base | 3 POI a scelta, video 5 min | 1 delle 7, scelta al 1° accesso a quel POI | SCELTA_POI.lingua — legata al POI, non modificabile dopo |
| Piena | Tutti base | Tutti i POI, video 5 min | 1 delle 7, modificabile da menu globale | $_SESSION['lingua'] globale |
Funzione che ho scritto io per decidere cosa mostrare:
6. Quesito I — Commenti e voti: tabella RECENSIONE e media che ho aggiunto io
Qui aggiungo tabella che mancava di vincoli:
Pagina media voti: io faccio SELECT AVG(voto), COUNT(*) FROM RECENSIONE WHERE id_poi=? — semplice, efficace, e mostro stelle.
7. Quesito II — Dispositivi personali: perché per me non si può aprire a smartphone
Qui io sono netto: se apro a smartphone personali, perdo controllo. Ecco perché dico no:
- Sicurezza contenuti: su tablet managed ho certificato client + kiosk mode, non possono fare screenshot o registrare video 5 min facilmente. Su smartphone personale sì.
- Esperienza: tablet con app dedicata + BLE scanning continuo — su smartphone dovrei fare app per iOS/Android, permessi BLE, batteria, ecc. Complicato.
- Business model: noleggio tablet è parte del biglietto (documento o carta di credito a garanzia). Se usi il tuo telefono, come giustifico il noleggio?
- Geolocalizzazione: su mio tablet controllo che app BLE sia attiva. Su telefono personale utente può disattivare Bluetooth e aggirare verifica prossimità.
Se proprio volessi aprire a personali, io metterei: app dedicata con certificate pinning + login con SPID + verifica BLE obbligatoria + watermark su video. Ma per me resta sconsigliato.
8. Quesito III — Sicurezza DB e RBAC: SQL injection, password_hash e ruoli
Qui completo quello che mancava:
- SQL injection: io uso solo prepared statement
$pdo->prepare()con placeholder?, mai concatenazione stringhe. È la base. - Password: mai in chiaro, solo
password_hash($pwd, PASSWORD_BCRYPT)e verifica conpassword_verify(). Validità giornaliera condata_emissione. - RBAC che ho aggiunto io: tabella RUOLO (admin, operatore InfoPoint, visitatore) + UTENTE con ruolo. Admin vede tutto, operatore gestisce noleggi, visitatore solo contenuti POI. Sessione con controllo ruolo su ogni pagina.
- XSS: sempre
htmlspecialchars()su output, cookie HttpOnly/Secure/SameSite=Strict. - HTTPS ovunque: TLS 1.3, HSTS, no HTTP.
9. Quesito IV — VPN site-to-site + roadwarrior: la differenza che spiego sempre
La traccia chiede VPN tra le due sedi (Roma-Milano) e accesso agenti commerciali. Io la spiego così perché molti confondono:
Site-to-site (sede-sede): due router/firewall che fanno tunnel IPSec permanente in modalità tunnel. IKE Phase 1 (AES-256, SHA-256, DH group 14, cert X.509) + Phase 2 (traffico 192.168.1.0/24 ↔ 192.168.2.0/24, ESP AES-256 HMAC). Trasparente agli utenti: PC Roma vede server Milano come LAN locale. Si alza all'avvio, sempre on.
Remote Access (roadwarrior agenti): ogni agente ha client OpenVPN/IPSec che si connette esplicitamente alla VPN gateway della sede principale da 4G/WiFi hotel. Non è permanente, è on-demand. Autenticazione con certificato + MFA. Una volta dentro, gli assegno IP da pool 10.8.0.0/24 e vede rete interna.
Site-to-site = gateway parlano tra loro automaticamente, utenti non sanno di usare VPN, è trasparente e permanente. Roadwarrior = ogni utente ha client e deve connettersi esplicitamente, è esplicito e on-demand. Per me è fondamentale dirlo all'orale.
10. Riepilogo che porto io all'orale
📚 Come la presento io all'orale
Io parto dai vincoli “solo tablet + solo vicino al POI” e da lì derivo BLE + cert client. Poi dico perché ho separato media server, perché ho messo lingua in SCELTA_POI, perché RAID non è backup (anche se qui non c'è ma lo dico lo stesso), e chiudo con site-to-site vs roadwarrior. È il mio filo logico e per me funziona.