Tokens de Upload IPFS: Uploads Seguros do Cliente
Aprende como os tokens de upload assinados permitem envios directos ao IPFS a partir de browsers e aplicações móveis sem expor a tua chave 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.

- Gere tokens de upload assinados no servidor para que navegadores e aplicações móveis nunca vejam a sua chave API em bruto.
- Os tokens têm um timestamp de expiração e restrições de utilização, limitando os danos caso algum seja alguma vez extraído.
- Os uploads diretos do cliente para o IPFS via tokens eliminam por completo os estrangulamentos de largura de banda do servidor.
- As assinaturas criptográficas em cada token impedem a adulteração ou a falsificação de uma credencial de upload.
Construir aplicações web modernas requer frequentemente o upload de ficheiros diretamente dos navegadores dos utilizadores para o armazenamento na nuvem. No entanto, quando se trata da segurança de uploads IPFS, os programadores enfrentam um dilema desafiante: como permitir uploads do lado do cliente sem expor as suas valiosas chaves API a possíveis abusos?
A maioria dos serviços de pinning IPFS obriga-o a uma escolha desconfortável: tratar todos os uploads do lado do servidor (criando estrangulamentos e complexidade) ou incorporar a sua chave API no código do cliente (um pesadelo de segurança). O IPFS.NINJA resolve isto com uma funcionalidade exclusiva que nenhum outro serviço de pinning oferece: tokens de upload assinados.

Como os Tokens de Upload IPFS Permitem Acesso Seguro do Lado do Cliente#
Ao construir aplicações do lado do cliente que precisam de fazer upload para o IPFS, os programadores tipicamente encontram vários desafios de segurança:
Risco de Exposição da Chave API#
Incorporar chaves API diretamente no JavaScript do navegador significa que qualquer pessoa pode ver o seu código-fonte e extrair as suas credenciais. Isto pode levar a:
- Uploads não autorizados a consumir a sua quota de armazenamento
- Potencial abuso da sua conta no serviço de pinning
- Violações de conformidade de segurança em ambientes empresariais
Estrangulamentos do Lado do Servidor#
A alternativa --- encaminhar todos os uploads pelo seu backend --- cria vários problemas:
- Custos mais elevados de largura de banda do servidor
- Maior latência para os utilizadores
- Requisitos de infraestrutura mais complexos
- Possíveis pontos únicos de falha
Segurança em Aplicações Móveis#
As aplicações móveis enfrentam desafios semelhantes, onde chaves API armazenadas nos pacotes da aplicação podem ser extraídas por engenharia reversa.
Apresentamos os Tokens de Upload IPFS#
Os tokens de upload assinados do IPFS.NINJA proporcionam um meio-termo seguro. Eis como funcionam:
- O servidor gera o token: O seu backend cria um token assinado com tempo limitado utilizando a sua chave API
- O cliente recebe o token: O token é transmitido com segurança para a sua aplicação frontend
- Upload direto: Os clientes fazem upload diretamente para o IPFS.NINJA utilizando o token assinado
- Expiração automática: Os tokens expiram após uma duração definida, limitando a janela de exposição
Esta abordagem combina a segurança da autenticação do lado do servidor com os benefícios de desempenho dos uploads diretos do cliente.
Compreender a Segurança dos Tokens de Upload#
Os tokens de upload assinados utilizam assinaturas criptográficas para garantir a autenticidade sem expor a sua chave API principal. Cada token contém:
- Marca temporal de expiração: Invalidação automática após a duração especificada
- Restrições de utilização: Limites opcionais no número de ficheiros ou tamanho total
- Assinatura criptográfica: Previne adulteração ou falsificação
- Verificação do emissor: Liga-se à sua conta autenticada
Ao contrário das chaves API, os tokens de upload foram concebidos para serem incorporados com segurança no código do lado do cliente. Mesmo se extraídos, proporcionam acesso limitado que expira automaticamente.
Implementação do Backend: Exemplo com Express.js#
Vamos construir um exemplo completo que mostra como implementar uploads seguros para o IPFS do lado do cliente. Primeiro, eis o backend Express.js que gera tokens de 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 {
// 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');
});Implementação do Frontend: Upload Seguro do Cliente#
Agora, eis o código do frontend que faz upload de ficheiros com segurança utilizando o token assinado:
<!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>Considerações Avançadas de Segurança#
Ao implementar segurança de upload IPFS com tokens assinados, considere estas medidas de segurança adicionais:
Limitações de Âmbito do Token#
Configure tokens com restrições apropriadas:
// 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
})
});Validação de Conteúdo#
Valide sempre o conteúdo enviado no seu backend:
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' });
}
});Limitação de Taxa#
Implemente limitação de taxa adicional no seu endpoint de geração de tokens:
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);Benefícios Face às Abordagens Tradicionais#
Os tokens de upload assinados proporcionam várias vantagens sobre os métodos alternativos de segurança de upload IPFS:
vs. Proxy do Lado do Servidor#
- Desempenho: Uploads diretos eliminam a utilização de largura de banda do servidor
- Escalabilidade: Sem estrangulamentos do servidor durante períodos de alta procura de uploads
- Custo: Redução de custos de largura de banda e processamento
- Experiência do Utilizador: Melhores velocidades de upload e acompanhamento de progresso
vs. Chaves API no Cliente#
- Segurança: Sem risco de extração ou utilização indevida de chaves API
- Conformidade: Cumpre os requisitos de auditoria de segurança
- Controlo de Acesso: Permissões granulares e expiração automática
- Monitorização: Melhor rastreamento das fontes e padrões de upload
vs. Outros Serviços de Pinning#
O IPFS.NINJA é atualmente o único serviço de pinning importante que oferece tokens de upload assinados. Concorrentes como o Pinata exigem proxy do lado do servidor ou exposição de chaves API no cliente, tornando isto um diferencial único.
Para mais detalhes sobre como o IPFS.NINJA se compara a outros serviços, consulte o nosso guia de comparação completo.
Dicas para Deploy em Produção#
Ao fazer deploy de tokens de upload assinados em produção:
Configuração do Ambiente#
Armazene configurações sensíveis com segurança:
// 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']
};Monitorização e Registo#
Implemente registo abrangente para monitorização de segurança:
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
});Tratamento de Erros#
Implemente tratamento de erros robusto que não divulgue informações sensíveis:
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.'
});
});Integração com Frameworks Populares#
Os tokens de upload assinados funcionam perfeitamente com frameworks web modernos. Eis exemplos rápidos de integração:
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
};
}Conclusão#
Os tokens de upload assinados resolvem um desafio de segurança crítico no desenvolvimento de aplicações descentralizadas. Ao proporcionar uma forma segura de permitir uploads diretos do lado do cliente para o IPFS sem expor chaves API, abrem novas possibilidades arquiteturais para aplicações web modernas.
Quer esteja a construir um sistema de gestão de conteúdo, um marketplace de NFT ou qualquer aplicação que exija uploads seguros de ficheiros, os tokens de upload do IPFS.NINJA proporcionam a segurança e a flexibilidade de que necessita. A implementação é simples, os benefícios de segurança são significativos e os ganhos de desempenho são substanciais.
Para saber mais sobre os fundamentos do IPFS, consulte o nosso guia sobre o que é pinning no IPFS ou explore o nosso tutorial completo da API. Para programadores a avaliar diferentes opções, a nossa comparação dos melhores serviços de pinning IPFS oferece informações abrangentes.
Pronto para implementar uploads seguros do lado do cliente para o IPFS na sua aplicação? A combinação de práticas modernas de segurança e armazenamento descentralizado torna esta abordagem ideal para aplicações em produção que precisam de escalar mantendo padrões de segurança.
Pronto para começar a fazer pinning? Crie uma conta gratuita --- 500 ficheiros, 1 GB de armazenamento, gateway dedicado. Sem necessidade de cartão de crédito.
| Abordagem | Chave API no cliente? | Expira automaticamente? | Limites detalhados? |
|---|---|---|---|
| Incorporar chave API completa | Sim (inseguro) | Não | Não |
| Proxy do lado do servidor | Não | N/A | N/A |
| Token de upload IPFS.NINJA | Não | Sim | Sim |
Perguntas frequentes#
O que são tokens de upload IPFS e como diferem das chaves API? Os tokens de upload IPFS são credenciais de curta duração, assinadas criptograficamente, geradas pelo teu servidor usando a tua chave API. Ao contrário das chaves API, que concedem acesso total à conta, os tokens de upload têm expiração integrada, limites opcionais de contagem e tamanho de ficheiros, e são concebidos para uso seguro em código do lado do cliente.
Posso restringir o que um token de upload pode fazer? Sim. Ao gerar um token, podes definir expiresIn (ex.: ‘1h’), maxUploads (ex.: 10 ficheiros), maxSizeMB (ex.: 50 MB), allowedMimeTypes (ex.: [‘image/jpeg’]) e ipWhitelist (ex.: intervalos CIDR). Mesmo que o token seja extraído do código do lado do cliente, um atacante só pode fazer upload dentro dessas restrições antes que o token expire.
Como gero um token de upload em Node.js / Express? Faz um POST para https://api.ipfs.ninja/upload-tokens com a tua chave API no cabeçalho X-Api-Key e um corpo JSON com expiresIn, maxUploads e maxSizeMB. A resposta inclui uma string de token assinada e um timestamp expiresAt. Passa esse token ao teu frontend; o cliente envia-o então como Authorization: Signed <token> ao chamar o endpoint de upload.
O IPFS.NINJA é o único serviço de pinning que suporta tokens de upload assinados? Sim, a partir de 2026, o IPFS.NINJA é o único grande serviço de pinning IPFS a oferecer tokens de upload assinados e com âmbito. Outros serviços exigem que todos os uploads sejam encaminhados pelo teu backend ou que uma chave API completa seja incorporada no código do cliente.
O que acontece se um token de upload for roubado ou vazado? A exposição é contida pelas restrições integradas do token: expira automaticamente após a duração configurada, só pode ser usado para um número limitado de uploads, não pode exceder o limite de tamanho configurado e não pode ser usado para listar, eliminar ou gerir os teus ficheiros fixados --- essas operações ainda requerem a tua chave API principal.
Sobre este artigo
Este artigo foi assistido por IA, revisto por humanos e verificado na plataforma IPFS.NINJA em produção antes da publicação. Saiba como usamos IA nos nossos conteúdos .

Sobre o autor
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.
