IPFS augšupielādes tokeni: Droša klienta augšupielāde

Uzziniet, kā parakstītie augšupielādes tokeni ļauj droši augšupielādēt IPFS no pārlūkprogrammām un mobilajām lietotnēm, neatklājot jūsu API atslēgu.

Nacho Collautors Atjaunināts: 11 min lasīšanas
Uzziniet, kā parakstītie augšupielādes tokeni ļauj droši augšupielādēt IPFS no pārlūkprogrammām un mobilajām lietotnēm, neatklājot jūsu API atslēgu.
TL;DR
  • Ģenerējiet parakstītus augšupielādes tokenus servera pusē, lai pārlūkprogrammas un mobilās lietotnes nekad neredzētu jūsu neapstrādāto API atslēgu.
  • Tokeniem ir derīguma termiņa laika zīmogs un lietošanas ierobežojumi, kas mazina kaitējumu, ja tāds kādreiz tiek iegūts.
  • Tiešas augšupielādes no klienta uz IPFS ar tokenu palīdzību pilnībā novērš servera joslas platuma sastrēgumus.
  • Kriptogrāfiski paraksti katram tokenam novērš manipulācijas vai viltota augšupielādes akreditācijas datu izveidi.

Modernu tīmekļa lietojumprogrammu izveide bieži prasa failu augšupielādi tieši no lietotāju pārlūkprogrammām mākoņkrātuvē. Tomēr attiecībā uz IPFS augšupielādes drošību izstrādātāji saskaras ar sarežģītu dilemmu: kā atļaut klienta puses augšupielādi, neatklājot savas vērtīgās API atslēgas iespējamai ļaunprātīgai izmantošanai?

Lielākā daļa IPFS piespraušanas pakalpojumu liek jums izdarīt neērtu izvēli: apstrādāt visas augšupielādes servera pusē (radot vājos posmus un sarežģītību) vai iegult savu API atslēgu klienta kodā (drošības murgs). IPFS.NINJA atrisina to ar unikālu funkciju, ko nepiedāvā neviens cits piespraušanas pakalpojums: parakstītie augšupielādes tokeni.

IPFS Ninja

Kā IPFS augšupielādes tokeni nodrošina drošu klienta puses piekļuvi#

Veidojot klienta puses lietojumprogrammas, kurām nepieciešams augšupielādēt IPFS, izstrādātāji parasti saskaras ar vairākiem drošības izaicinājumiem:

API atslēgas atklāšanas risks#

API atslēgu tieša iegulšana pārlūkprogrammas JavaScript nozīmē, ka ikviens var apskatīt jūsu pirmkodu un iegūt jūsu akreditācijas datus. Tas var izraisīt:

  • Nesankcionētas augšupielādes, kas patērē jūsu krātuves kvotu
  • Iespējamu jūsu piespraušanas pakalpojuma konta ļaunprātīgu izmantošanu
  • Drošības atbilstības pārkāpumus uzņēmumu vidēs

Servera puses vājie posmi#

Alternatīva --- visu augšupielāžu maršrutēšana caur jūsu aizmugursistēmu --- rada vairākas problēmas:

  • Palielinātas servera joslas platuma izmaksas
  • Lielāks latentums lietotājiem
  • Sarežģītākas infrastruktūras prasības
  • Iespējami vieni kļūmju punkti

Mobilo lietotņu drošība#

Mobilās lietojumprogrammas saskaras ar līdzīgiem izaicinājumiem, kur lietotņu pakotnēs saglabātās API atslēgas var iegūt, veicot reverso inženieriju.

IPFS augšupielādes tokenu iepazīšana#

IPFS.NINJA parakstītie augšupielādes tokeni nodrošina drošu vidusceļu. Lūk, kā tie darbojas:

  1. Serveris ģenerē tokenu: jūsu aizmugursistēma izveido laika ierobežotu, parakstītu tokenu, izmantojot jūsu API atslēgu
  2. Klients saņem tokenu: tokens droši tiek pārsūtīts jūsu priekšgala lietojumprogrammai
  3. Tieša augšupielāde: klienti augšupielādē tieši uz IPFS.NINJA, izmantojot parakstīto tokenu
  4. Automātiska derīguma termiņa beigšanās: tokenu derīgums beidzas pēc noteikta ilguma, ierobežojot iedarbības logu

Šī pieeja apvieno servera puses autentifikācijas drošību ar tiešas klienta augšupielādes veiktspējas priekšrocībām.

Augšupielādes tokenu drošības izpratne#

Parakstītie augšupielādes tokeni izmanto kriptogrāfiskus parakstus, lai nodrošinātu autentiskumu, neatklājot jūsu galveno API atslēgu. Katrs tokens satur:

  • Derīguma termiņa laika zīmogu: automātiska atzīšana par nederīgu pēc norādītā ilguma
  • Lietošanas ierobežojumus: izvēles ierobežojumus failu skaitam vai kopējam izmēram
  • Kriptogrāfisku parakstu: novērš manipulāciju vai viltošanu
  • Izdevēja verifikāciju: saista atpakaļ ar jūsu autentificēto kontu

Atšķirībā no API atslēgām augšupielādes tokeni ir izstrādāti drošai iegulšanai klienta puses kodā. Pat ja tie tiek iegūti, tie nodrošina ierobežotu piekļuvi, kuras derīgums automātiski beidzas.

Aizmugursistēmas implementācija: Express.js piemērs#

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

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

const IPFS_API_KEY = 'bws_1234567890abcdef1234567890abcdef12345678';

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', maxUploads: 10, maxSizeMB: 50
      })
    });
    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' });
  }
});

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'); });

Priekšgala implementācija: droša klienta augšupielāde#

<!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>

Papildu drošības apsvērumi#

Tokena tvēruma ierobežojumi#

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']
  })
});

Satura validācija#

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' }); }
});

Ātruma ierobežošana#

const rateLimit = require('express-rate-limit');
const tokenLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, max: 10,
  message: 'Too many token requests, please try again later'
});
app.use('/api/generate-upload-token', tokenLimiter);

Priekšrocības salīdzinājumā ar tradicionālajām pieejām#

vs. servera puses starpniekserveris#

  • Veiktspēja: tiešas augšupielādes novērš servera joslas platuma izmantošanu
  • Mērogojamība: nav servera vājo posmu augšupielādes pīķu laikā
  • Izmaksas: samazinātas joslas platuma un apstrādes izmaksas
  • Lietotāja pieredze: labāks augšupielādes ātrums un progresa izsekošana

vs. klienta puses API atslēga#

  • Drošība: nav API atslēgas iegūšanas vai ļaunprātīgas izmantošanas riska
  • Atbilstība: atbilst drošības audita prasībām
  • Piekļuves kontrole: detalizētas atļaujas un automātiska derīguma beigšanās

vs. citi piespraušanas pakalpojumi#

IPFS.NINJA pašlaik ir vienīgais lielākais piespraušanas pakalpojums, kas piedāvā parakstītus augšupielādes tokenus.

Papildu informācijai skatiet mūsu visaptverošo salīdzinājuma ceļvedi.

Ražošanas izvietošanas padomi#

Vides konfigurācija#

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']
};

Uzraudzība un reģistrēšana#

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 });

Kļūdu apstrāde#

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.' });
});

Integrācija ar populāriem ietvariem#

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(); } 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; };
  return { uploading: readonly(uploading), uploadProgress: readonly(uploadProgress), uploadFile };
}

Pieeju salīdzinājums#

PieejaAPI atslēga klientā?Automātisks termiņš?Detalizēti ierobežojumi?
Pilnas API atslēgas iegulšanaJā (nedroši)
Servera puses starpnieksNavNav
IPFS.NINJA augšupielādes tokens

Biežāk uzdotie jautājumi#

Ko nozīmē IPFS augšupielādes tokeni un kā tie atšķiras no API atslēgām? IPFS augšupielādes tokeni ir īslaicīgi, kriptogrāfiski parakstīti akreditācijas dati, kurus jūsu serveris ģenerē, izmantojot jūsu API atslēgu. Atšķirībā no API atslēgām, kas piešķir pilnu piekļuvi kontam, augšupielādes tokeni ir iebūvēts termiņš, izvēles failu skaita un izmēra ierobežojumi, un tie ir paredzēti drošai izmantošanai klienta pusē.

Vai es varu ierobežot to, ko augšupielādes tokens var darīt? Jā. Ģenerējot tokenu, varat iestatīt expiresIn (piem., ‘1h’), maxUploads (piem., 10 faili), maxSizeMB (piem., 50 MB), allowedMimeTypes (piem., [‘image/jpeg’]) un ipWhitelist (piem., CIDR diapazoni). Pat ja tokens tiek izvilkts no klienta koda, uzbrucējs var augšupielādēt tikai šo ierobežojumu ietvaros, pirms tokens beidzas.

Kā ģenerēt augšupielādes tokenu Node.js / Express vidē? Sūtiet POST pieprasījumu uz https://api.ipfs.ninja/upload-tokens ar jūsu API atslēgu galvenē X-Api-Key un JSON pamattekstu, kurā norādīti expiresIn, maxUploads un maxSizeMB. Atbilde ietver parakstītu tokena virkni un expiresAt laika zīmogu. Nododiet tokenu savam frontenda kodam; klients pēc tam to sūta kā Authorization: Signed <token>, zvanot uz augšupielādes galapunktu.

Vai IPFS.NINJA ir vienīgais pinēšanas pakalpojums, kas atbalsta parakstītus augšupielādes tokenus? Jā, 2026. gadā IPFS.NINJA ir vienīgais lielākais IPFS pinēšanas pakalpojums, kas piedāvā parakstītus, ierobežotus augšupielādes tokenus. Citi pakalpojumi prasa vai nu novirzīt visas augšupielādes caur jūsu backend, vai iegult pilnu API atslēgu klienta kodā.

Kas notiek, ja augšupielādes tokens tiek nozagts vai nopludināts? Iedarbība ir ierobežota ar tokena iebūvētajiem ierobežojumiem: tas automātiski beidzas pēc konfigurētā laika, var izmantot tikai ierobežotam augšupielāžu skaitam, nedrīkst pārsniegt konfigurēto izmēra ierobežojumu un nevar izmantot, lai uzskaitītu, dzēstu vai pārvaldītu jūsu pinētos failus — šīm darbībām joprojām nepieciešama jūsu galvenā API atslēga.

Secinājums#

Parakstītie augšupielādes tokeni atrisina kritisku drošības izaicinājumu decentralizētu lietojumprogrammu izstrādē. Nodrošinot drošu veidu, kā atļaut tiešas klienta puses augšupielādes IPFS, neatklājot API atslēgas, tie paver jaunas arhitektūras iespējas modernām tīmekļa lietojumprogrammām.

Neatkarīgi no tā, vai veidojat satura pārvaldības sistēmu, NFT tirgu vai jebkuru lietojumprogrammu, kas prasa drošas failu augšupielādes, IPFS.NINJA augšupielādes tokeni nodrošina nepieciešamo drošību un elastību.

Lai uzzinātu vairāk par IPFS pamatiem, skatiet mūsu ceļvedi par kas ir IPFS piespraušana vai izpētiet mūsu pilno API pamācību. Izstrādātājiem, kas novērtē dažādas iespējas, mūsu labāko IPFS piespraušanas pakalpojumu salīdzinājums sniedz visaptverošu ieskatu.

Gatavs sākt piespraušanu? Izveidojiet bezmaksas kontu --- 500 faili, 1 GB krātuve, īpašs vārteja. Nav nepieciešama kredītkarte.

Nacho Coll

Par autoru

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.

Atpakaļ uz Blogu

Saistītie raksti