¿Por qué asegurar sus claves API en el backend en lugar de dejarlas en el frontend?
Si necesita realizar solicitudes HTTP que requieran una clave API, dejarla en el código del frontend es una mala práctica por varias razones:
- Exposición pública: Aunque el usuario promedio no tenga interés en hacerlo, cualquiera puede inspeccionar su código y encontrar sus claves. Esto expone sus servicios a un riesgo de abuso, como superar los límites de uso o realizar llamadas maliciosas.
- Seguridad de los datos: Una clave API no asegurada puede servir como un punto de entrada para acceder a los datos almacenados en su backend.
- Gestión de accesos: Usando un backend, puede controlar con precisión los permisos y limitar el acceso a la clave según las necesidades reales.
¿La solución? Almacenar y gestionar sus claves API en un entorno seguro, como Firebase Functions, y exponer solo los datos que se mostrarán en el frontend.
¿Cómo almacenar su clave en Firebase utilizando firebase functions:secrets:accessor add API_KEY
?
Firebase ofrece un sistema de gestión de secretos que permite asegurar sus claves sensibles y almacenarlas en el backend, en lugar de en su código fuente. Aquí están los pasos a seguir:
- Si aún no lo ha hecho, inicialice Firebase Functions en su proyecto Flutter con el comando:
firebase init functions
Si necesita ayuda, describo más detalles sobre los pasos necesarios para instalar funciones en la nube en su proyecto Flutter.
2. Añada un «secreto» en Firebase con el siguiente comando:
firebase functions:secrets:set API_KEY
Puede reemplazar API_KEY
por otro nombre, siguiendo esta notación. Se le pedirá que ingrese el valor de su clave API. No se preocupe si el texto permanece invisible, esto es completamente normal.
3. Finalmente, podrá agregar una capa adicional de seguridad con el siguiente comando:
firebase functions:secrets:accessor add API_KEY
Esto asegura que solo las funciones autorizadas puedan acceder a la clave.
¿Cómo acceder a su clave para usarla?
Ahora que su clave está almacenada en el backend, podrá acceder a ella en Firebase para realizar sus solicitudes HTTP. El uso común es hacer la llamada desde Firebase, pero supongamos que quiero recuperarla para mostrarla en mi frontend. Aquí está cómo hacerlo:
Para comenzar, vamos a crear una función en la nube para acceder a la clave secreta que acabamos de crear:
import * as functions from "firebase-functions";
import {defineSecret} from "firebase-functions/params";
const OpenAIApiKey = defineSecret("API_KEY");
// Función de Firebase para enviar la clave API al frontend
export const getApiKey = functions.https.onRequest(
{secrets: [OpenAIApiKey]},
async (req, res) => {
try {
// Recuperar la clave API desde Firebase Secrets
const apiKey = await OpenAIApiKey.value();
console.log("API key retrieved:", apiKey);
if (!apiKey) {
res.status(500).send("Internal Server Error: API key is missing");
return;
}
// Enviar la clave API como respuesta (solo para fines educativos)
res.status(200).json({apiKey});
} catch (error) {
console.error("Error retrieving API key:", error);
res.status(500).send("Internal Server Error");
}
}
);
Aquí está el código para mostrar nuestra clave en el frontend (una vez más, esta no es la manera correcta de usar su clave, pero es solo para el ejemplo):
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ApiKeyDemo(),
);
}
}
class ApiKeyDemo extends StatefulWidget {
@override
_ApiKeyDemoState createState() => _ApiKeyDemoState();
}
class _ApiKeyDemoState extends State<ApiKeyDemo> {
String _apiKey = "Fetching...";
@override
void initState() {
super.initState();
_fetchApiKey();
}
Future<void> _fetchApiKey() async {
final url = "https://<your-cloud-function-url>"; // Reemplace por la URL de su función
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
setState(() {
_apiKey = json.decode(response.body)['apiKey'];
});
} else {
setState(() {
_apiKey = "Error fetching API Key";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Firebase API Key Demo")),
body: Center(
child: Text(
"API Key: $_apiKey",
style: TextStyle(fontSize: 18),
),
),
);
}
}