· Nacho Coll · Guides  · 8 min lasīšanas

IPFS augšupielādes tokeni: droša klienta puses augšupielāde, neatklājot API atslēgas

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.

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.

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

Problēma ar tradicionālajām IPFS API atslēgām

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

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.

Atpakaļ uz Blogu

Saistītie raksti

Skatīt visus rakstus »