· Nacho Coll · Guides  · 10 min di lettura

Token di Upload IPFS: Upload Sicuri Lato Client senza Esporre le Chiavi API

Scopri come i token di upload firmati ti permettono di caricare file su IPFS in sicurezza da browser e app mobile senza esporre la tua chiave API.

Scopri come i token di upload firmati ti permettono di caricare file su IPFS in sicurezza da browser e app mobile senza esporre la tua chiave API.

Costruire applicazioni web moderne richiede spesso il caricamento di file direttamente dai browser degli utenti verso lo storage cloud. Tuttavia, quando si tratta di sicurezza degli upload IPFS, gli sviluppatori si trovano di fronte a un dilemma impegnativo: come permettere upload lato client senza esporre le proprie preziose chiavi API a un potenziale uso improprio?

La maggior parte dei servizi di pinning IPFS ti costringe a una scelta scomoda: gestire tutti gli upload lato server (creando colli di bottiglia e complessità) o incorporare la tua chiave API nel codice client (un incubo di sicurezza). IPFS.NINJA risolve questo problema con una funzionalità unica che nessun altro servizio di pinning offre: i token di upload firmati.

IPFS Ninja

Il Problema con le Chiavi API IPFS Tradizionali

Quando si costruiscono applicazioni lato client che devono caricare su IPFS, gli sviluppatori incontrano tipicamente diverse sfide di sicurezza:

Rischio di Esposizione della Chiave API

Incorporare chiavi API direttamente nel JavaScript del browser significa che chiunque può visualizzare il codice sorgente ed estrarre le credenziali. Questo potrebbe portare a:

  • Upload non autorizzati che consumano la quota di storage
  • Potenziale abuso dell’account del servizio di pinning
  • Violazioni della conformità di sicurezza in ambienti aziendali

Colli di Bottiglia Lato Server

L’alternativa --- instradare tutti gli upload attraverso il backend --- crea diversi problemi:

  • Costi di banda del server più elevati
  • Latenza maggiore per gli utenti
  • Requisiti infrastrutturali più complessi
  • Potenziali punti singoli di guasto

Sicurezza delle App Mobile

Le applicazioni mobile affrontano sfide simili, dove le chiavi API memorizzate nei pacchetti dell’app possono essere estratte tramite reverse engineering.

Introduzione ai Token di Upload IPFS

I token di upload firmati di IPFS.NINJA forniscono un compromesso sicuro. Ecco come funzionano:

  1. Il server genera il token: Il tuo backend crea un token firmato a tempo limitato usando la tua chiave API
  2. Il client riceve il token: Il token viene trasmesso in sicurezza alla tua applicazione frontend
  3. Upload diretto: I client caricano direttamente su IPFS.NINJA usando il token firmato
  4. Scadenza automatica: I token scadono dopo una durata prestabilita, limitando la finestra di esposizione

Questo approccio combina la sicurezza dell’autenticazione lato server con i vantaggi prestazionali degli upload diretti lato client.

Comprendere la Sicurezza dei Token di Upload

I token di upload firmati utilizzano firme crittografiche per garantire l’autenticità senza esporre la chiave API principale. Ogni token contiene:

  • Timestamp di scadenza: Invalidazione automatica dopo la durata specificata
  • Vincoli di utilizzo: Limiti opzionali sul numero di file o dimensione totale
  • Firma crittografica: Previene manomissioni o contraffazioni
  • Verifica dell’emittente: Si collega al tuo account autenticato

A differenza delle chiavi API, i token di upload sono progettati per essere incorporati in sicurezza nel codice lato client. Anche se estratti, forniscono un accesso limitato che scade automaticamente.

Implementazione Backend: Esempio con Express.js

Costruiamo un esempio completo che mostra come implementare upload IPFS sicuri lato client. Prima, ecco il backend Express.js che genera i token di upload:

// server.js
const express = require('express');
const cors = require('cors');
const app = express();

app.use(express.json());
app.use(cors());

// Your IPFS.NINJA API key (keep this secure on server-side only)
const IPFS_API_KEY = 'bws_1234567890abcdef1234567890abcdef12345678';

// Generate a signed upload token
app.post('/api/generate-upload-token', async (req, res) => {
  try {
    const response = await fetch('https://api.ipfs.ninja/upload-tokens', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Api-Key': IPFS_API_KEY
      },
      body: JSON.stringify({
        expiresIn: '1h', // Token valid for 1 hour
        maxUploads: 10,  // Optional: limit number of uploads
        maxSizeMB: 50    // Optional: limit total upload size
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const tokenData = await response.json();
    
    res.json({
      success: true,
      uploadToken: tokenData.token,
      expiresAt: tokenData.expiresAt
    });
  } catch (error) {
    console.error('Token generation failed:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to generate upload token'
    });
  }
});

// Optional: Endpoint to verify uploads completed successfully
app.post('/api/verify-upload', async (req, res) => {
  const { cid } = req.body;
  
  try {
    const response = await fetch(`https://api.ipfs.ninja/pins/${cid}`, {
      headers: { 'X-Api-Key': IPFS_API_KEY }
    });
    const pinData = await response.json();
    res.json({ success: true, verified: pinData.pinned, metadata: pinData });
  } catch (error) {
    res.status(500).json({ success: false, error: 'Verification failed' });
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Implementazione Frontend: Upload Client Sicuro

Ecco ora il codice frontend che carica file in sicurezza usando il token firmato:

<!DOCTYPE html>
<html>
<head>
    <title>Secure IPFS Upload Demo</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
        .upload-area { border: 2px dashed #ccc; padding: 20px; text-align: center; margin: 20px 0; }
        .upload-area.dragover { border-color: #007cba; background: #f0f8ff; }
        button { background: #007cba; color: white; border: none; padding: 10px 20px; cursor: pointer; }
        .status { margin: 10px 0; padding: 10px; border-radius: 4px; }
        .success { background: #d4edda; color: #155724; }
        .error { background: #f8d7da; color: #721c24; }
        .info { background: #d1ecf1; color: #0c5460; }
    </style>
</head>
<body>
    <h1>Secure IPFS Upload with Signed Tokens</h1>
    <div class="upload-area" id="uploadArea">
        <p>Drag & drop files here or click to select</p>
        <input type="file" id="fileInput" multiple style="display: none;">
        <button onclick="document.getElementById('fileInput').click()">Select Files</button>
    </div>
    <div id="status"></div>
    <div id="results"></div>
    <script>
        class SecureIPFSUploader {
            constructor() {
                this.uploadToken = null;
                this.tokenExpiry = null;
                this.setupEventListeners();
            }
            setupEventListeners() {
                const uploadArea = document.getElementById('uploadArea');
                const fileInput = document.getElementById('fileInput');
                uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover'); });
                uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('dragover'); });
                uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('dragover'); this.handleFiles(Array.from(e.dataTransfer.files)); });
                fileInput.addEventListener('change', (e) => { this.handleFiles(Array.from(e.target.files)); });
            }
            async getUploadToken() {
                if (this.uploadToken && this.tokenExpiry && new Date() < new Date(this.tokenExpiry)) return this.uploadToken;
                try {
                    this.showStatus('Generating secure upload token...', 'info');
                    const response = await fetch('/api/generate-upload-token', { method: 'POST', headers: { 'Content-Type': 'application/json' } });
                    if (!response.ok) throw new Error(`Failed to generate token: ${response.statusText}`);
                    const data = await response.json();
                    if (!data.success) throw new Error(data.error || 'Token generation failed');
                    this.uploadToken = data.uploadToken;
                    this.tokenExpiry = data.expiresAt;
                    return this.uploadToken;
                } catch (error) { this.showStatus(`Token generation failed: ${error.message}`, 'error'); throw error; }
            }
            async uploadFile(file) {
                try {
                    const token = await this.getUploadToken();
                    const fileBase64 = await this.fileToBase64(file);
                    this.showStatus(`Uploading ${file.name} to IPFS...`, 'info');
                    const response = await fetch('https://api.ipfs.ninja/upload/new', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json', 'Authorization': `Signed ${token}` },
                        body: JSON.stringify({ content: fileBase64, description: `File uploaded via secure token: ${file.name}`, metadata: { filename: file.name, fileType: file.type, uploadedAt: new Date().toISOString(), uploadMethod: 'signed-token' } })
                    });
                    if (!response.ok) { const errorText = await response.text(); throw new Error(`Upload failed: ${response.status} ${errorText}`); }
                    const result = await response.json();
                    return { success: true, filename: file.name, cid: result.cid, size: result.sizeMB, ipfsUri: result.uris.ipfs, httpUrl: result.uris.url };
                } catch (error) { return { success: false, filename: file.name, error: error.message }; }
            }
            async handleFiles(files) {
                if (files.length === 0) return;
                this.clearResults();
                try {
                    const results = await Promise.all(files.map(file => this.uploadFile(file)));
                    this.displayResults(results);
                    const successful = results.filter(r => r.success).length;
                    if (successful === results.length) this.showStatus(`✅ Successfully uploaded ${successful} file(s) to IPFS!`, 'success');
                    else this.showStatus(`⚠️ Uploaded ${successful}/${results.length} files. Check results below.`, 'error');
                } catch (error) { this.showStatus(`Upload failed: ${error.message}`, 'error'); }
            }
            fileToBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result.split(',')[1]); reader.onerror = error => reject(error); }); }
            showStatus(message, type) { const s = document.getElementById('status'); s.className = `status ${type}`; s.textContent = message; }
            displayResults(results) {
                document.getElementById('results').innerHTML = '<h3>Upload Results:</h3>' + results.map(r => `<div class="status ${r.success ? 'success' : 'error'}" style="margin: 10px 0;"><strong>${r.filename}</strong><br>${r.success ? `✅ CID: ${r.cid}<br>📊 Size: ${r.size} MB<br>🔗 URL: <a href="${r.httpUrl}" target="_blank">${r.httpUrl}</a>` : `❌ Error: ${r.error}`}</div>`).join('');
            }
            clearResults() { document.getElementById('results').innerHTML = ''; }
        }
        const uploader = new SecureIPFSUploader();
    </script>
</body>
</html>

Considerazioni Avanzate sulla Sicurezza

Nell’implementare la sicurezza degli upload IPFS con token firmati, considera queste misure di sicurezza aggiuntive:

Limitazioni dell’Ambito del Token

Configura i token con restrizioni appropriate:

const restrictedToken = await fetch('https://api.ipfs.ninja/upload-tokens', {
  method: 'POST',
  headers: { 'X-Api-Key': IPFS_API_KEY },
  body: JSON.stringify({
    expiresIn: '30m', maxUploads: 5, maxSizeMB: 10,
    allowedMimeTypes: ['image/jpeg', 'image/png'],
    ipWhitelist: ['192.168.1.0/24']
  })
});

Validazione del Contenuto

Valida sempre il contenuto caricato sul tuo backend:

app.post('/api/validate-upload', async (req, res) => {
  const { cid } = req.body;
  try {
    const response = await fetch(`https://ipfs.ninja/ipfs/${cid}`);
    const contentType = response.headers.get('content-type');
    if (!isValidContentType(contentType)) {
      await deleteFromIPFS(cid);
      return res.status(400).json({ error: 'Invalid content type' });
    }
    res.json({ success: true, validated: true });
  } catch (error) { res.status(500).json({ error: 'Validation failed' }); }
});

Limitazione della Frequenza

Implementa una limitazione della frequenza aggiuntiva sull’endpoint di generazione dei token:

const rateLimit = require('express-rate-limit');

const tokenLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 10, // Limit each IP to 10 token requests per windowMs
  message: 'Too many token requests, please try again later'
});

app.use('/api/generate-upload-token', tokenLimiter);

Vantaggi Rispetto agli Approcci Tradizionali

I token di upload firmati offrono diversi vantaggi rispetto ai metodi alternativi di sicurezza degli upload IPFS:

vs. Proxy Lato Server

  • Prestazioni: Gli upload diretti eliminano l’uso della banda del server
  • Scalabilità: Nessun collo di bottiglia del server durante i periodi di alto carico
  • Costo: Riduzione dei costi di banda e di elaborazione
  • Esperienza Utente: Migliori velocità di upload e tracciamento del progresso

vs. Chiavi API Lato Client

  • Sicurezza: Nessun rischio di estrazione o uso improprio delle chiavi API
  • Conformità: Soddisfa i requisiti di audit di sicurezza
  • Controllo degli Accessi: Permessi granulari e scadenza automatica
  • Monitoraggio: Migliore tracciamento delle fonti e dei pattern di upload

vs. Altri Servizi di Pinning

IPFS.NINJA è attualmente l’unico grande servizio di pinning che offre token di upload firmati. Concorrenti come Pinata richiedono proxy lato server o esposizione delle chiavi API lato client, rendendo questo un differenziatore unico.

Per maggiori dettagli sul confronto di IPFS.NINJA con altri servizi, consulta la nostra guida comparativa completa.

Suggerimenti per il Deploy in Produzione

Configurazione dell’Ambiente

const config = {
  ipfsApiKey: process.env.IPFS_API_KEY,
  tokenExpiry: process.env.UPLOAD_TOKEN_EXPIRY || '1h',
  maxFileSize: process.env.MAX_FILE_SIZE_MB || 50,
  allowedOrigins: process.env.ALLOWED_ORIGINS?.split(',') || ['localhost:3000']
};

Monitoraggio e Logging

const winston = require('winston');
const logger = winston.createLogger({
  level: 'info', format: winston.format.json(),
  transports: [new winston.transports.File({ filename: 'upload-security.log' })]
});
logger.info('Upload token generated', {
  userId: req.user.id, clientIP: req.ip,
  userAgent: req.get('User-Agent'), expiresAt: tokenData.expiresAt
});

Gestione degli Errori

app.use((error, req, res, next) => {
  logger.error('Upload token error', {
    error: error.message, stack: error.stack, userId: req.user?.id, endpoint: req.path
  });
  res.status(500).json({ success: false, error: 'An internal error occurred. Please try again.' });
});

Integrazione con Framework Popolari

React Hook

import { useState, useCallback } from 'react';

export function useSecureIPFSUpload() {
  const [uploading, setUploading] = useState(false);
  const [uploadToken, setUploadToken] = useState(null);

  const getToken = useCallback(async () => {
    if (uploadToken?.expiresAt && new Date() < new Date(uploadToken.expiresAt)) return uploadToken.token;
    const response = await fetch('/api/generate-upload-token', { method: 'POST' });
    const data = await response.json();
    setUploadToken(data);
    return data.uploadToken;
  }, [uploadToken]);

  const uploadFile = useCallback(async (file) => {
    setUploading(true);
    try { const token = await getToken(); /* Upload logic here... */ }
    finally { setUploading(false); }
  }, [getToken]);

  return { uploadFile, uploading };
}

Vue.js Composable

import { ref } from 'vue';

export function useSecureUpload() {
  const uploading = ref(false);
  const uploadProgress = ref(0);
  const uploadFile = async (file) => { uploading.value = true; /* Implementation here... */ };
  return { uploading: readonly(uploading), uploadProgress: readonly(uploadProgress), uploadFile };
}

Conclusione

I token di upload firmati risolvono una sfida critica di sicurezza nello sviluppo di applicazioni decentralizzate. Fornendo un modo sicuro per abilitare upload diretti lato client verso IPFS senza esporre le chiavi API, aprono nuove possibilità architetturali per le applicazioni web moderne.

Che tu stia costruendo un sistema di gestione dei contenuti, un marketplace NFT o qualsiasi applicazione che richieda upload sicuri di file, i token di upload di IPFS.NINJA forniscono la sicurezza e la flessibilità di cui hai bisogno. L’implementazione è semplice, i benefici di sicurezza sono significativi e i guadagni prestazionali sono sostanziali.

Per saperne di più sui fondamenti di IPFS, consulta la nostra guida su cos’è il pinning IPFS o esplora il nostro tutorial completo dell’API. Per gli sviluppatori che valutano diverse opzioni, il nostro confronto dei migliori servizi di pinning IPFS fornisce approfondimenti completi.

Pronto a implementare upload IPFS sicuri lato client nella tua applicazione? La combinazione di pratiche di sicurezza moderne e storage decentralizzato rende questo approccio ideale per applicazioni in produzione che devono scalare mantenendo gli standard di sicurezza.

Pronto per iniziare il pinning? Crea un account gratuito --- 500 file, 1 GB di storage, gateway dedicato. Nessuna carta di credito richiesta.

Torna al Blog

Articoli Correlati

Vedi Tutti gli Articoli »