Ci sono tanti modi per poter mettere al sicuro le funzioni. Questo argomento non è chiarissimo dal primo momento in cui si utilizzano le funzioni in Cloud ed è inoltre in continuo cambiamento. Io ho deciso di implementare un mio metodo che utilizza la blasonata e più semplice autenticazione: la Basic Auth. Sicuramente non è la più sicura ma bastava al mio scopo. Unica pecca è che la funzione deve essere aggiunta e implementata in ogni funzione Cloud che andiamo a creare. In questo modo, possiamo chiamare la funzione implementata da dove vogliamo semplicemente aggiungendo come parametro della request user e password criptati nella Basic Auth.
Per semplificare la verifica, ho creato, una funzione che inserisco all’interno di ogni Cloud Function. Questa funzione si occupa di fare il parsing dei dati della request, verificare se presente una basic auth e se presente verifica se i dati di user e password corrispondono. Di seguito il codice utilizzato:
var compare = require('tsscmp')
const checkAuth = async() => {
// parse login and password from headers
const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
const strauth = new Buffer(b64auth, 'base64').toString()
const splitIndex = strauth.indexOf(':')
const login = strauth.substring(0, splitIndex)
const password = strauth.substring(splitIndex + 1)
//function to validate credentials using https://www.npmjs.com/package/tsscmp
//Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison
function check (name, pass) {
var valid = true
// Simple method to prevent short-circut and use timing-safe compare
valid = compare(name, 'user') && valid
valid = compare(pass,'password') && valid
return valid
}
if (!check(login, password)) {
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="example"')
res.end('Access denied')
}
}
Successivamente possiamo utilizzare la funzione all’interno del metodo chiamato della nostra Cloud Function nel seguente modo:
exports.helloWorld = async (req, res) => {
await checkAuth();
let message = req.query.message || req.body.message || 'Hello World!';
res.status(200).send(message);
};
Ed il gioco è fatto, le nostre funzioni adesso sono al sicuro. Mica tanto! ma è un inizio. Tanto alla fine se dovessero superare la nostra autenticazione, non sarebbe un grosso problema. In questa funzione restituiamo solo Hello World!
Come facciamo invece a richiamare la nostra funzione con Flutter? Dobbiamo semplicemente creare la Basic Auth con user e password e aggiungerla alla request come segue:
var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password'));
http.Response response = await http.get(apiUrl,
headers: <String, String>{'authorization': basicAuth});
print(response.statusCode); //200
print(response.body ); //Hello world!
Questo metodo non è uno dei migliori però è molto rapido implementarlo e inoltre ci dà un vantaggio perché può essere utilizzato allo stesso modo per tutti i provider cloud.
Infine, possiamo richiamarlo dappertutto utilizzando qualsiasi linguaggio, e non costringe a doversi portare dietro chiavi json che potrebbero essere esposte.
L’unico problema che ho avuto è stato quello di avere nel codice scolpiti user e password che non trovavo gradevoli. Ho risolto questo problema mettendo questi parametri all’interno delle configurazioni di firebase. Se siete interessati potete leggere questo articolo Flutter 2 plugin indispensabili dalle grandi potenzialità