Token Muat Naik IPFS: Upload Klien yang Selamat
Ketahui bagaimana signed upload token membolehkan anda memuat naik ke IPFS dengan selamat dari pelayar dan aplikasi mudah alih tanpa mendedahkan API key anda.
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.

- Jana token muat naik bertandatangan di sisi pelayan supaya pelayar dan aplikasi mudah alih tidak pernah melihat kunci API mentah anda.
- Token membawa cap masa tamat tempoh dan sekatan penggunaan, mengehadkan kerosakan jika satu token pernah diekstrak.
- Muat naik langsung dari klien ke IPFS melalui token menghapuskan sepenuhnya kesesakan lebar jalur pelayan.
- Tandatangan kriptografi pada setiap token menghalang gangguan atau pemalsuan kelayakan muat naik palsu.
Membina aplikasi web moden sering memerlukan muat naik fail secara langsung dari pelayar pengguna ke storan awan. Walau bagaimanapun, dalam hal keselamatan muat naik IPFS, pembangun menghadapi dilema yang mencabar: bagaimana membenarkan muat naik sisi klien tanpa mendedahkan API key anda yang berharga kepada potensi penyalahgunaan?
Kebanyakan perkhidmatan pinning IPFS memaksa anda membuat pilihan yang tidak selesa: mengendalikan semua muat naik di sisi pelayan (mewujudkan kesesakan dan kerumitan) atau membenamkan API key anda dalam kod klien (mimpi ngeri keselamatan). IPFS.NINJA menyelesaikan masalah ini dengan ciri unik yang tiada perkhidmatan pinning lain tawarkan: signed upload token.

Cara Token Muat Naik IPFS Membolehkan Akses Sisi Klien yang Selamat#
Apabila membina aplikasi sisi klien yang perlu memuat naik ke IPFS, pembangun biasanya menghadapi beberapa cabaran keselamatan:
Risiko Pendedahan API Key#
Membenamkan API key secara langsung dalam JavaScript pelayar bermakna sesiapa sahaja boleh melihat kod sumber anda dan mengekstrak kelayakan anda. Ini boleh menyebabkan:
- Muat naik tanpa kebenaran yang menghabiskan kuota storan anda
- Potensi penyalahgunaan akaun perkhidmatan pinning anda
- Pelanggaran pematuhan keselamatan dalam persekitaran enterprise
Kesesakan Sisi Pelayan#
Alternatifnya—mengalirkan semua muat naik melalui backend anda—mewujudkan beberapa isu:
- Peningkatan kos lebar jalur pelayan
- Kependaman yang lebih tinggi untuk pengguna
- Keperluan infrastruktur yang lebih kompleks
- Potensi titik kegagalan tunggal
Keselamatan Aplikasi Mudah Alih#
Aplikasi mudah alih menghadapi cabaran yang sama, di mana API key yang disimpan dalam bundle aplikasi boleh diekstrak melalui kejuruteraan terbalik.
Memperkenalkan Token Muat Naik IPFS#
Signed upload token IPFS.NINJA menyediakan jalan tengah yang selamat. Begini cara ia berfungsi:
- Pelayan menjana token: Backend anda mencipta token bertandatangan dengan had masa menggunakan API key anda
- Klien menerima token: Token dihantar dengan selamat ke aplikasi frontend anda
- Muat naik langsung: Klien memuat naik terus ke IPFS.NINJA menggunakan signed token
- Tamat tempoh automatik: Token tamat tempoh selepas tempoh yang ditetapkan, mengehadkan tetingkap pendedahan
Pendekatan ini menggabungkan keselamatan pengesahan sisi pelayan dengan manfaat prestasi muat naik langsung dari klien.
Memahami Keselamatan Upload Token#
Signed upload token menggunakan tandatangan kriptografi untuk memastikan kesahihan tanpa mendedahkan API key utama anda. Setiap token mengandungi:
- Cap masa tamat tempoh: Pembatalan automatik selepas tempoh yang ditentukan
- Kekangan penggunaan: Had pilihan pada bilangan fail atau saiz keseluruhan
- Tandatangan kriptografi: Menghalang pengubahsuaian atau pemalsuan
- Pengesahan penerbit: Menghubungkan kembali ke akaun anda yang disahkan
Tidak seperti API key, upload token direka untuk dibenamkan dengan selamat dalam kod sisi klien. Walaupun diekstrak, ia menyediakan akses terhad yang tamat tempoh secara automatik.
Pelaksanaan Backend: Contoh Express.js#
Mari kita bina contoh lengkap yang menunjukkan cara melaksanakan muat naik IPFS sisi klien yang selamat. Pertama, berikut backend Express.js yang menjana upload token:
// 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');
});Pelaksanaan Frontend: Muat Naik Klien yang Selamat#
Sekarang, berikut kod frontend yang memuat naik fail dengan selamat menggunakan signed token:
<!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>Pertimbangan Keselamatan Lanjutan#
Apabila melaksanakan keselamatan muat naik IPFS dengan signed token, pertimbangkan langkah-langkah keselamatan tambahan ini:
Had Skop Token#
Konfigurasikan token dengan sekatan yang sesuai:
// 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
})
});Pengesahan Kandungan#
Sentiasa sahkan kandungan yang dimuat naik pada backend anda:
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' });
}
});Pengehadan Kadar#
Laksanakan pengehadan kadar tambahan pada endpoint penjanaan token anda:
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);Kelebihan Berbanding Pendekatan Tradisional#
Signed upload token menyediakan beberapa kelebihan berbanding kaedah keselamatan muat naik IPFS alternatif:
vs. Proksi Sisi Pelayan#
- Prestasi: Muat naik langsung menghapuskan penggunaan lebar jalur pelayan
- Kebolehskalaan: Tiada kesesakan pelayan semasa tempoh muat naik yang tinggi
- Kos: Pengurangan kos lebar jalur dan pemprosesan
- Pengalaman Pengguna: Kelajuan muat naik dan penjejakan kemajuan yang lebih baik
vs. API Key Sisi Klien#
- Keselamatan: Tiada risiko pengekstrakan atau penyalahgunaan API key
- Pematuhan: Memenuhi keperluan audit keselamatan
- Kawalan Akses: Kebenaran terperinci dan tamat tempoh automatik
- Pemantauan: Penjejakan sumber dan corak muat naik yang lebih baik
vs. Perkhidmatan Pinning Lain#
IPFS.NINJA pada masa ini merupakan satu-satunya perkhidmatan pinning utama yang menawarkan signed upload token. Pesaing seperti Pinata memerlukan sama ada proksi sisi pelayan atau pendedahan API key sisi klien, menjadikan ini pembeza yang unik.
Untuk butiran lanjut tentang perbandingan IPFS.NINJA dengan perkhidmatan lain, lihat panduan perbandingan komprehensif kami.
Petua Penerapan Pengeluaran#
Apabila menerapkan signed upload token dalam pengeluaran:
Konfigurasi Persekitaran#
Simpan konfigurasi sensitif dengan selamat:
// 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']
};Pemantauan dan Pengelogan#
Laksanakan pengelogan komprehensif untuk pemantauan keselamatan:
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
});Pengendalian Ralat#
Laksanakan pengendalian ralat yang kukuh dan tidak membocorkan maklumat sensitif:
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.'
});
});Integrasi dengan Rangka Kerja Popular#
Signed upload token berfungsi dengan lancar bersama rangka kerja web moden. Berikut contoh integrasi pantas:
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
};
}Kesimpulan#
Signed upload token menyelesaikan cabaran keselamatan kritikal dalam pembangunan aplikasi terdesentralisasi. Dengan menyediakan cara selamat untuk membolehkan muat naik langsung sisi klien ke IPFS tanpa mendedahkan API key, ia membuka kemungkinan seni bina baharu untuk aplikasi web moden.
Sama ada anda membina sistem pengurusan kandungan, pasaran NFT, atau sebarang aplikasi yang memerlukan muat naik fail yang selamat, upload token IPFS.NINJA menyediakan keselamatan dan fleksibiliti yang anda perlukan. Pelaksanaannya mudah, manfaat keselamatannya signifikan, dan peningkatan prestasinya besar.
Untuk mengetahui lebih lanjut tentang asas IPFS, lihat panduan kami tentang apa itu IPFS pinning atau terokai tutorial API lengkap kami. Untuk pembangun yang menilai pelbagai pilihan, perbandingan perkhidmatan pinning IPFS terbaik kami menyediakan pandangan komprehensif.
Bersedia untuk melaksanakan muat naik IPFS sisi klien yang selamat dalam aplikasi anda? Gabungan amalan keselamatan moden dan storan terdesentralisasi menjadikan pendekatan ini ideal untuk aplikasi pengeluaran yang perlu berskala sambil mengekalkan standard keselamatan.
Bersedia untuk mula pinning? Cipta akaun percuma — 500 fail, storan 1 GB, gateway khusus. Tiada kad kredit diperlukan.
| Pendekatan | Kunci API dalam Klien? | Tamat Tempoh Automatik? | Had Terperinci? |
|---|---|---|---|
| Benamkan kunci API penuh | Ya (tidak selamat) | Tidak | Tidak |
| Proksi sisi pelayan | Tidak | N/A | N/A |
| Token muat naik IPFS.NINJA | Tidak | Ya | Ya |
Soalan Lazim#
Apakah token muat naik IPFS dan bagaimana ia berbeza daripada kunci API? Token muat naik IPFS ialah kelayakan bertanda kriptografi jangka pendek yang dijana oleh pelayan anda menggunakan kunci API anda. Tidak seperti kunci API yang memberikan akses penuh ke akaun, token muat naik mempunyai tamat tempoh terbina dalam, had bilangan fail dan saiz yang pilihan, dan direka bentuk untuk digunakan dengan selamat dalam kod sisi klien.
Bolehkah saya mengehadkan apa yang boleh dilakukan oleh token muat naik? Ya. Semasa menjana token, anda boleh menetapkan expiresIn (cth. ‘1h’), maxUploads (cth. 10 fail), maxSizeMB (cth. 50 MB), allowedMimeTypes (cth. [‘image/jpeg’]), dan ipWhitelist (cth. julat CIDR). Walaupun token diekstrak daripada kod sisi klien, penyerang hanya boleh memuat naik dalam had tersebut sebelum token tamat tempoh.
Bagaimana cara menjana token muat naik dalam Node.js / Express? POST ke https://api.ipfs.ninja/upload-tokens dengan kunci API anda dalam pengepala X-Api-Key dan badan JSON yang mengandungi expiresIn, maxUploads, dan maxSizeMB. Respons merangkumi rentetan token bertanda dan cap masa expiresAt. Hantar token tersebut ke frontend anda; klien kemudian menghantar sebagai Authorization: Signed <token> semasa memanggil titik akhir muat naik.
Adakah IPFS.NINJA satu-satunya perkhidmatan penyematan yang menyokong token muat naik bertanda? Ya, sehingga 2026, IPFS.NINJA ialah satu-satunya perkhidmatan penyematan IPFS utama yang menawarkan token muat naik bertanda dan berskop. Perkhidmatan lain memerlukan sama ada penghalaan semua muat naik melalui backend anda atau membenamkan kunci API penuh dalam kod klien.
Apa yang berlaku jika token muat naik dicuri atau bocor? Pendedahan dikawal oleh kekangan terbina dalam token: ia tamat tempoh secara automatik selepas tempoh yang dikonfigurasi, hanya boleh digunakan untuk bilangan muat naik yang terhad, tidak boleh melebihi had saiz yang dikonfigurasi, dan tidak boleh digunakan untuk menyenaraikan, memadam, atau mengurus fail yang disematkan — operasi tersebut masih memerlukan kunci API utama anda.
Mengenai artikel ini
Artikel ini dibantu AI, disemak oleh manusia dan disahkan terhadap platform IPFS.NINJA sebenar sebelum diterbitkan. Ketahui cara kami menggunakan AI dalam kandungan kami .

Mengenai penulis
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.
