Tokeny IPFS: Bezpieczne przesyłanie po stronie klienta
Dowiedz się, jak podpisane tokeny przesyłania pozwalają bezpiecznie przesyłać pliki do IPFS z przeglądarek i aplikacji mobilnych bez ujawniania klucza API.
Nacho founded IPFS.NINJA to make content-addressed storage feel as simple as an S3 PUT — a single API call, a permanent CID, no wallets or peer discovery to reason about.

- Generuj podpisane tokeny przesyłania po stronie serwera, aby przeglądarki i aplikacje mobilne nigdy nie widziały surowego klucza API.
- Tokeny zawierają znacznik czasu wygaśnięcia i ograniczenia użycia, co minimalizuje szkody, jeśli kiedykolwiek zostaną wydobyte.
- Bezpośrednie przesyłanie z klienta do IPFS za pomocą tokenów całkowicie eliminuje wąskie gardła przepustowości serwera.
- Podpisy kryptograficzne na każdym tokenie zapobiegają manipulacji lub sfałszowaniu fałszywych danych uwierzytelniających do przesyłania.
Tworzenie nowoczesnych aplikacji internetowych często wymaga przesyłania plików bezpośrednio z przeglądarek użytkowników do chmury. Jednak jeśli chodzi o bezpieczeństwo przesyłania IPFS, programiści stają przed trudnym dylematem: jak umożliwić przesyłanie po stronie klienta bez ujawniania cennych kluczy API potencjalnym nadużyciom?
Większość usług pinningu IPFS zmusza do niewygodnego wyboru: albo obsługujesz wszystkie przesyłania po stronie serwera (tworząc wąskie gardła i komplikacje), albo osadzasz klucz API w kodzie klienta (koszmar bezpieczeństwa). IPFS.NINJA rozwiązuje ten problem unikalną funkcją, której nie oferuje żadna inna usługa pinningu: podpisane tokeny przesyłania.

Jak tokeny przesyłania IPFS umożliwiają bezpieczny dostęp po stronie klienta#
Podczas tworzenia aplikacji po stronie klienta, które muszą przesyłać dane do IPFS, programiści napotykają kilka wyzwań związanych z bezpieczeństwem:
Ryzyko ujawnienia klucza API#
Osadzanie kluczy API bezpośrednio w JavaScript przeglądarki oznacza, że każdy może przeglądać kod źródłowy i wyodrębnić dane uwierzytelniające. Może to prowadzić do:
- Nieautoryzowanych przesyłań zużywających limit przestrzeni dyskowej
- Potencjalnego nadużycia konta usługi pinningu
- Naruszeń zgodności z wymogami bezpieczeństwa w środowiskach korporacyjnych
Wąskie gardła po stronie serwera#
Alternatywa — kierowanie wszystkich przesyłań przez backend — tworzy kilka problemów:
- Zwiększone koszty przepustowości serwera
- Większe opóźnienia dla użytkowników
- Bardziej złożone wymagania infrastrukturalne
- Potencjalne pojedyncze punkty awarii
Bezpieczeństwo aplikacji mobilnych#
Aplikacje mobilne napotykają podobne wyzwania — klucze API przechowywane w pakietach aplikacji mogą zostać wyodrębnione przez inżynierię wsteczną.
Przedstawiamy tokeny przesyłania IPFS#
Podpisane tokeny przesyłania IPFS.NINJA zapewniają bezpieczny kompromis. Oto jak działają:
- Serwer generuje token: Twój backend tworzy ograniczony czasowo, podpisany token przy użyciu klucza API
- Klient otrzymuje token: Token jest bezpiecznie przekazywany do Twojej aplikacji frontendowej
- Bezpośrednie przesyłanie: Klienci przesyłają bezpośrednio do IPFS.NINJA za pomocą podpisanego tokenu
- Automatyczne wygaśnięcie: Tokeny wygasają po określonym czasie, ograniczając okno ekspozycji
To podejście łączy bezpieczeństwo uwierzytelniania po stronie serwera z zaletami wydajności bezpośredniego przesyłania po stronie klienta.
Zrozumienie bezpieczeństwa tokenów przesyłania#
Podpisane tokeny przesyłania używają podpisów kryptograficznych, aby zapewnić autentyczność bez ujawniania głównego klucza API. Każdy token zawiera:
- Znacznik czasu wygaśnięcia: Automatyczne unieważnienie po określonym czasie
- Ograniczenia użycia: Opcjonalne limity liczby plików lub całkowitego rozmiaru
- Podpis kryptograficzny: Zapobiega manipulacji lub fałszerstwu
- Weryfikacja wydawcy: Powiązanie z Twoim uwierzytelnionym kontem
W przeciwieństwie do kluczy API, tokeny przesyłania są zaprojektowane tak, aby można je było bezpiecznie osadzać w kodzie po stronie klienta. Nawet jeśli zostaną wyodrębnione, zapewniają ograniczony dostęp, który automatycznie wygasa.
Implementacja backendu: przykład Express.js#
Zbudujmy kompletny przykład pokazujący, jak zaimplementować bezpieczne przesyłanie IPFS po stronie klienta. Najpierw backend Express.js, który generuje tokeny przesyłania:
// 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');
});Implementacja frontendu: bezpieczne przesyłanie po stronie klienta#
Teraz kod frontendowy, który bezpiecznie przesyła pliki za pomocą podpisanego tokenu:
<!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');
// Drag and drop handlers
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));
});
// File input handler
fileInput.addEventListener('change', (e) => {
this.handleFiles(Array.from(e.target.files));
});
}
async getUploadToken() {
// Check if we have a valid token
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();
// Convert file to base64 for JSON transport
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 {
// Upload files concurrently
const uploadPromises = files.map(file => this.uploadFile(file));
const results = await Promise.all(uploadPromises);
this.displayResults(results);
const successful = results.filter(r => r.success).length;
const total = results.length;
if (successful === total) {
this.showStatus(`✅ Successfully uploaded ${successful} file(s) to IPFS!`, 'success');
} else {
this.showStatus(`⚠️ Uploaded ${successful}/${total} 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 = () => {
// Remove the data:mime/type;base64, prefix
const base64 = reader.result.split(',')[1];
resolve(base64);
};
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 = '';
}
}
// Initialize the uploader
const uploader = new SecureIPFSUploader();
</script>
</body>
</html>Zaawansowane zagadnienia bezpieczeństwa#
Przy wdrażaniu bezpieczeństwa przesyłania IPFS z podpisanymi tokenami warto rozważyć następujące dodatkowe środki bezpieczeństwa:
Ograniczenia zakresu tokenu#
Konfiguruj tokeny z odpowiednimi restrykcjami:
// 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
})
});Walidacja zawartości#
Zawsze waliduj przesłaną zawartość na swoim backendzie:
app.post('/api/validate-upload', async (req, res) => {
const { cid } = req.body;
try {
// Fetch and validate the uploaded content
const response = await fetch(`https://ipfs.ninja/ipfs/${cid}`);
const contentType = response.headers.get('content-type');
// Implement your validation logic
if (!isValidContentType(contentType)) {
// Remove invalid content
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' });
}
});Ograniczanie częstotliwości zapytań#
Zaimplementuj dodatkowe ograniczanie częstotliwości zapytań na endpoincie generowania tokenów:
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);Zalety w porównaniu z tradycyjnymi podejściami#
Podpisane tokeny przesyłania oferują kilka zalet w porównaniu z alternatywnymi metodami bezpieczeństwa przesyłania IPFS:
W porównaniu z proxy po stronie serwera#
- Wydajność: Bezpośrednie przesyłanie eliminuje zużycie przepustowości serwera
- Skalowalność: Brak wąskich gardeł serwera w okresach intensywnego przesyłania
- Koszt: Zmniejszone koszty przepustowości i przetwarzania
- Doświadczenie użytkownika: Lepsza prędkość przesyłania i śledzenie postępu
W porównaniu z kluczami API po stronie klienta#
- Bezpieczeństwo: Brak ryzyka wyodrębnienia lub nadużycia klucza API
- Zgodność: Spełnia wymagania audytów bezpieczeństwa
- Kontrola dostępu: Szczegółowe uprawnienia i automatyczne wygaśnięcie
- Monitorowanie: Lepsze śledzenie źródeł i wzorców przesyłania
W porównaniu z innymi usługami pinningu#
IPFS.NINJA jest obecnie jedyną główną usługą pinningu oferującą podpisane tokeny przesyłania. Konkurenci tacy jak Pinata wymagają proxy po stronie serwera lub ujawnienia klucza API po stronie klienta, co czyni tę funkcję unikalnym wyróżnikiem.
Więcej szczegółów na temat porównania IPFS.NINJA z innymi usługami znajdziesz w naszym kompleksowym przewodniku porównawczym.
Wskazówki dotyczące wdrożenia produkcyjnego#
Podczas wdrażania podpisanych tokenów przesyłania w produkcji:
Konfiguracja środowiska#
Przechowuj poufną konfigurację w bezpieczny sposób:
// 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']
};Monitorowanie i logowanie#
Zaimplementuj kompleksowe logowanie do monitorowania bezpieczeństwa:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'upload-security.log' })
]
});
// Log token generation
logger.info('Upload token generated', {
userId: req.user.id,
clientIP: req.ip,
userAgent: req.get('User-Agent'),
expiresAt: tokenData.expiresAt
});Obsługa błędów#
Zaimplementuj solidną obsługę błędów, która nie ujawnia poufnych informacji:
app.use((error, req, res, next) => {
// Log full error details server-side
logger.error('Upload token error', {
error: error.message,
stack: error.stack,
userId: req.user?.id,
endpoint: req.path
});
// Send safe error message to client
res.status(500).json({
success: false,
error: 'An internal error occurred. Please try again.'
});
});Integracja z popularnymi frameworkami#
Podpisane tokeny przesyłania działają bezproblemowo z nowoczesnymi frameworkami webowymi. Oto szybkie przykłady integracji:
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
};
}Porównanie podejść do przesyłania#
| Podejście | Klucz API w kliencie? | Automatyczne wygaśnięcie? | Szczegółowe limity? |
|---|---|---|---|
| Osadzanie pełnego klucza API | Tak (niebezpieczne) | Nie | Nie |
| Proxy po stronie serwera | Nie | Nie dotyczy | Nie dotyczy |
| Token przesyłania IPFS.NINJA | Nie | Tak | Tak |
Najczęściej zadawane pytania#
Czym są tokeny przesyłania IPFS i czym różnią się od kluczy API? Tokeny przesyłania IPFS to krótkotrwałe, kryptograficznie podpisane dane uwierzytelniające generowane przez serwer przy użyciu klucza API. W przeciwieństwie do kluczy API, które zapewniają pełny dostęp do konta, tokeny przesyłania mają wbudowany czas wygaśnięcia, opcjonalne limity liczby i rozmiaru plików i są zaprojektowane do bezpiecznego użycia w kodzie po stronie klienta.
Czy mogę ograniczyć to, co token przesyłania może robić? Tak. Podczas generowania tokenu można ustawić expiresIn (np. ‘1h’), maxUploads (np. 10 plików), maxSizeMB (np. 50 MB), allowedMimeTypes (np. [‘image/jpeg’]) i ipWhitelist (np. zakresy CIDR). Nawet jeśli token zostanie wydobyty z kodu po stronie klienta, atakujący może przesyłać tylko w tych granicach, zanim token wygaśnie.
Jak wygenerować token przesyłania w Node.js / Express? Wyślij żądanie POST do https://api.ipfs.ninja/upload-tokens z kluczem API w nagłówku X-Api-Key i treścią JSON zawierającą expiresIn, maxUploads i maxSizeMB. Odpowiedź zawiera podpisany ciąg tokenu i znacznik czasu expiresAt. Przekaż ten token do frontendu; klient wysyła go następnie jako Authorization: Signed <token> podczas wywoływania punktu końcowego przesyłania.
Czy IPFS.NINJA jest jedyną usługą przypinania obsługującą podpisane tokeny przesyłania? Tak, od 2026 roku IPFS.NINJA jest jedyną dużą usługą przypinania IPFS oferującą podpisane, zakresowe tokeny przesyłania. Inne usługi wymagają albo kierowania wszystkich przesyłań przez backend, albo osadzania pełnego klucza API w kodzie klienta.
Co się stanie, jeśli token przesyłania zostanie skradziony lub wycieknie? Ekspozycja jest ograniczona przez wbudowane ograniczenia tokenu: wygasa automatycznie po skonfigurowanym czasie, może być użyty tylko do ograniczonej liczby przesyłań, nie może przekroczyć skonfigurowanego limitu rozmiaru i nie może być użyty do wyświetlania, usuwania ani zarządzania przypiętymi plikami — te operacje nadal wymagają głównego klucza API.
Podsumowanie#
Podpisane tokeny przesyłania rozwiązują krytyczny problem bezpieczeństwa w tworzeniu zdecentralizowanych aplikacji. Zapewniając bezpieczny sposób umożliwienia bezpośredniego przesyłania po stronie klienta do IPFS bez ujawniania kluczy API, otwierają nowe możliwości architektoniczne dla nowoczesnych aplikacji internetowych.
Niezależnie od tego, czy budujesz system zarządzania treścią, marketplace NFT, czy jakąkolwiek aplikację wymagającą bezpiecznego przesyłania plików, tokeny przesyłania IPFS.NINJA zapewniają potrzebne bezpieczeństwo i elastyczność. Implementacja jest prosta, korzyści bezpieczeństwa znaczące, a zyski wydajności istotne.
Aby dowiedzieć się więcej o podstawach IPFS, zapoznaj się z naszym przewodnikiem Czym jest pinning IPFS lub sprawdź nasz kompletny samouczek API. Dla programistów oceniających różne opcje, nasze porównanie najlepszych usług pinningu IPFS dostarcza kompleksowych informacji.
Gotowy do wdrożenia bezpiecznego przesyłania IPFS po stronie klienta w swojej aplikacji? Połączenie nowoczesnych praktyk bezpieczeństwa i zdecentralizowanego przechowywania danych czyni to podejście idealnym dla produkcyjnych aplikacji, które muszą się skalować przy zachowaniu standardów bezpieczeństwa.
Gotowy, aby zacząć pinning? Utwórz darmowe konto — 500 plików, 1 GB przestrzeni dyskowej, dedykowana brama. Karta kredytowa nie jest wymagana.
O tym artykule
Ten artykuł powstał z pomocą AI, został zweryfikowany przez człowieka i sprawdzony na żywej platformie IPFS.NINJA przed publikacją. Dowiedz się, jak wykorzystujemy AI w naszych treściach .

O autorze
Nacho Coll
Founder & Engineer at IPFS.NINJA
Nacho founded IPFS.NINJA to make content-addressed storage feel as simple as an S3 PUT — a single API call, a permanent CID, no wallets or peer discovery to reason about. Writes about IPFS internals, decentralized storage patterns, and the pinning-service landscape from the operator side of the wire.
