· Nacho Coll · Guides · 10 Min. Lesezeit
IPFS Upload-Tokens: Sichere Client-seitige Uploads ohne API-Schlüssel preiszugeben
Erfahren Sie, wie signierte Upload-Tokens es Ihnen ermöglichen, sicher aus Browsern und mobilen Apps auf IPFS hochzuladen, ohne Ihren API-Schlüssel preiszugeben.

Moderne Webanwendungen erfordern häufig das Hochladen von Dateien direkt aus den Browsern der Benutzer in Cloud-Speicher. Wenn es jedoch um die Sicherheit von IPFS-Uploads geht, stehen Entwickler vor einem herausfordernden Dilemma: Wie ermöglicht man Client-seitige Uploads, ohne seine wertvollen API-Schlüssel einem möglichen Missbrauch auszusetzen?
Die meisten IPFS-Pinning-Dienste zwingen Sie zu einer unbequemen Wahl: Entweder alle Uploads Server-seitig verarbeiten (was Engpässe und Komplexität erzeugt) oder Ihren API-Schlüssel im Client-Code einbetten (ein Sicherheitsalptraum). IPFS.NINJA löst dieses Problem mit einem einzigartigen Feature, das kein anderer Pinning-Dienst bietet: signierte Upload-Tokens.

Das Problem mit traditionellen IPFS-API-Schlüsseln
Beim Erstellen von Client-seitigen Anwendungen, die auf IPFS hochladen müssen, stoßen Entwickler typischerweise auf mehrere Sicherheitsherausforderungen:
Risiko der API-Schlüssel-Offenlegung
Das Einbetten von API-Schlüsseln direkt in Browser-JavaScript bedeutet, dass jeder Ihren Quellcode einsehen und Ihre Zugangsdaten extrahieren kann. Dies könnte führen zu:
- Unautorisierte Uploads, die Ihr Speicherkontingent verbrauchen
- Potenzieller Missbrauch Ihres Pinning-Dienst-Kontos
- Verstöße gegen Sicherheits-Compliance in Unternehmensumgebungen
Server-seitige Engpässe
Die Alternative --- alle Uploads über Ihr Backend zu leiten --- erzeugt mehrere Probleme:
- Höhere Server-Bandbreitenkosten
- Höhere Latenz für Benutzer
- Komplexere Infrastrukturanforderungen
- Mögliche Single Points of Failure
Sicherheit mobiler Apps
Mobile Anwendungen stehen vor ähnlichen Herausforderungen, bei denen API-Schlüssel in App-Paketen durch Reverse Engineering extrahiert werden können.
Einführung der IPFS Upload-Tokens
Die signierten Upload-Tokens von IPFS.NINJA bieten einen sicheren Mittelweg. So funktionieren sie:
- Server generiert den Token: Ihr Backend erstellt einen zeitlich begrenzten, signierten Token mit Ihrem API-Schlüssel
- Client empfängt den Token: Der Token wird sicher an Ihre Frontend-Anwendung übertragen
- Direkter Upload: Clients laden direkt zu IPFS.NINJA hoch, wobei sie den signierten Token verwenden
- Automatischer Ablauf: Tokens laufen nach einer festgelegten Dauer ab und begrenzen das Expositionsfenster
Dieser Ansatz kombiniert die Sicherheit der Server-seitigen Authentifizierung mit den Leistungsvorteilen direkter Client-Uploads.
Upload-Token-Sicherheit verstehen
Signierte Upload-Tokens verwenden kryptographische Signaturen, um die Authentizität zu gewährleisten, ohne Ihren Haupt-API-Schlüssel preiszugeben. Jeder Token enthält:
- Ablauf-Zeitstempel: Automatische Ungültigmachung nach der angegebenen Dauer
- Nutzungsbeschränkungen: Optionale Limits für Dateianzahl oder Gesamtgröße
- Kryptographische Signatur: Verhindert Manipulation oder Fälschung
- Aussteller-Verifizierung: Verknüpfung mit Ihrem authentifizierten Konto
Im Gegensatz zu API-Schlüsseln sind Upload-Tokens so konzipiert, dass sie sicher in Client-seitigem Code eingebettet werden können. Selbst wenn sie extrahiert werden, bieten sie eingeschränkten Zugriff, der automatisch abläuft.
Backend-Implementierung: Express.js-Beispiel
Lassen Sie uns ein vollständiges Beispiel erstellen, das zeigt, wie sichere Client-seitige IPFS-Uploads implementiert werden. Zunächst das Express.js-Backend, das Upload-Tokens generiert:
// 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 {
// Verify the file was pinned successfully
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');
});Frontend-Implementierung: Sicherer Client-Upload
Hier ist nun der Frontend-Code, der Dateien sicher mit dem signierten Token hochlädt:
<!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 statusDiv = document.getElementById('status');
statusDiv.className = `status ${type}`;
statusDiv.textContent = message;
}
displayResults(results) {
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = '<h3>Upload Results:</h3>' +
results.map(result => `
<div class="status ${result.success ? 'success' : 'error'}" style="margin: 10px 0;">
<strong>${result.filename}</strong><br>
${result.success ?
`✅ CID: ${result.cid}<br>📊 Size: ${result.size} MB<br>🔗 URL: <a href="${result.httpUrl}" target="_blank">${result.httpUrl}</a>` :
`❌ Error: ${result.error}`}
</div>
`).join('');
}
clearResults() { document.getElementById('results').innerHTML = ''; }
}
const uploader = new SecureIPFSUploader();
</script>
</body>
</html>Erweiterte Sicherheitsüberlegungen
Bei der Implementierung von IPFS-Upload-Sicherheit mit signierten Tokens sollten Sie diese zusätzlichen Sicherheitsmaßnahmen berücksichtigen:
Token-Bereichsbeschränkungen
Konfigurieren Sie Tokens mit angemessenen Einschränkungen:
// Generate token with specific constraints
const restrictedToken = await fetch('https://api.ipfs.ninja/upload-tokens', {
method: 'POST',
headers: {
'X-Api-Key': IPFS_API_KEY
},
body: JSON.stringify({
expiresIn: '30m', // Short expiration
maxUploads: 5, // Limited upload count
maxSizeMB: 10, // Size restriction
allowedMimeTypes: ['image/jpeg', 'image/png'], // File type restrictions
ipWhitelist: ['192.168.1.0/24'] // IP-based access control
})
});Inhaltsvalidierung
Validieren Sie hochgeladene Inhalte immer auf Ihrem 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' });
}
});Ratenbegrenzung
Implementieren Sie eine zusätzliche Ratenbegrenzung auf Ihrem Token-Generierungs-Endpoint:
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);Vorteile gegenüber traditionellen Ansätzen
Signierte Upload-Tokens bieten mehrere Vorteile gegenüber alternativen IPFS-Upload-Sicherheitsmethoden:
vs. Server-seitiges Proxying
- Leistung: Direkte Uploads eliminieren die Server-Bandbreitennutzung
- Skalierbarkeit: Keine Server-Engpässe bei hohem Upload-Aufkommen
- Kosten: Reduzierte Bandbreiten- und Verarbeitungskosten
- Benutzererfahrung: Bessere Upload-Geschwindigkeiten und Fortschrittsverfolgung
vs. Client-seitige API-Schlüssel
- Sicherheit: Kein Risiko der API-Schlüssel-Extraktion oder des Missbrauchs
- Compliance: Erfüllt Sicherheitsaudit-Anforderungen
- Zugriffskontrolle: Feingranulare Berechtigungen und automatischer Ablauf
- Überwachung: Bessere Verfolgung von Upload-Quellen und -Mustern
vs. Andere Pinning-Dienste
IPFS.NINJA ist derzeit der einzige große Pinning-Dienst, der signierte Upload-Tokens anbietet. Wettbewerber wie Pinata erfordern entweder Server-seitiges Proxying oder Client-seitige API-Schlüssel-Offenlegung, was dies zu einem einzigartigen Differenzierungsmerkmal macht.
Weitere Details zum Vergleich von IPFS.NINJA mit anderen Diensten finden Sie in unserem umfassenden Vergleichsleitfaden.
Tipps für den Produktionseinsatz
Beim Einsatz signierter Upload-Tokens in der Produktion:
Umgebungskonfiguration
Speichern Sie sensible Konfigurationen sicher:
// Use environment variables for production
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']
};Überwachung und Protokollierung
Implementieren Sie umfassende Protokollierung für die Sicherheitsüberwachung:
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
});Fehlerbehandlung
Implementieren Sie eine robuste Fehlerbehandlung, die keine sensiblen Informationen preisgibt:
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.'
});
});Integration mit populären Frameworks
Signierte Upload-Tokens funktionieren nahtlos mit modernen Web-Frameworks. Hier sind schnelle Integrationsbeispiele:
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
};
}Fazit
Signierte Upload-Tokens lösen eine kritische Sicherheitsherausforderung in der Entwicklung dezentraler Anwendungen. Indem sie einen sicheren Weg bieten, direkte Client-seitige Uploads zu IPFS zu ermöglichen, ohne API-Schlüssel preiszugeben, eröffnen sie neue architektonische Möglichkeiten für moderne Webanwendungen.
Ob Sie ein Content-Management-System, einen NFT-Marktplatz oder eine andere Anwendung bauen, die sichere Datei-Uploads erfordert --- die Upload-Tokens von IPFS.NINJA bieten die Sicherheit und Flexibilität, die Sie benötigen. Die Implementierung ist unkompliziert, die Sicherheitsvorteile sind erheblich und die Leistungsgewinne sind substanziell.
Um mehr über die Grundlagen von IPFS zu erfahren, lesen Sie unseren Leitfaden zu Was ist IPFS-Pinning oder erkunden Sie unser vollständiges API-Tutorial. Für Entwickler, die verschiedene Optionen evaluieren, bietet unser Vergleich der besten IPFS-Pinning-Dienste umfassende Einblicke.
Bereit, sichere Client-seitige IPFS-Uploads in Ihrer Anwendung zu implementieren? Die Kombination aus modernen Sicherheitspraktiken und dezentralem Speicher macht diesen Ansatz ideal für Produktionsanwendungen, die skalieren müssen und gleichzeitig Sicherheitsstandards einhalten.
Bereit mit dem Pinning zu starten? Erstellen Sie ein kostenloses Konto --- 500 Dateien, 1 GB Speicher, dediziertes Gateway. Keine Kreditkarte erforderlich.
