[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"navigation":3,"\u002Fblog\u002Fcomprehensive-guide-encrypting-images-uploading-to-cloudinary-and-serving-them-securely":58,"\u002Fblog\u002Fcomprehensive-guide-encrypting-images-uploading-to-cloudinary-and-serving-them-securely-surround":334},[4],{"title":5,"path":6,"stem":7,"children":8,"page":57},"Blog","\u002Fblog","blog",[9,13,17,21,25,29,33,37,41,45,49,53],{"title":10,"path":11,"stem":12},"A Guide to Server-Side Rendering (SSR) with Vite and React.js","\u002Fblog\u002Fa-guide-to-server-side-rendering-ssr-with-vite-and-reactjs","blog\u002Fa-guide-to-server-side-rendering-ssr-with-vite-and-reactjs",{"title":14,"path":15,"stem":16},"🔄 Background DNS Refresh in Node.js: Keeping Cached IPs Updated","\u002Fblog\u002Fbackground-dns-refresh-in-nodejs-keeping-cached-ips-updated","blog\u002Fbackground-dns-refresh-in-nodejs-keeping-cached-ips-updated",{"title":18,"path":19,"stem":20},"Comprehensive Guide: Encrypting Images, Uploading to Cloudinary, and Serving Them Securely","\u002Fblog\u002Fcomprehensive-guide-encrypting-images-uploading-to-cloudinary-and-serving-them-securely","blog\u002Fcomprehensive-guide-encrypting-images-uploading-to-cloudinary-and-serving-them-securely",{"title":22,"path":23,"stem":24},"Comprehensive Guide to Using Uploadthing with Express and Typescript","\u002Fblog\u002Fcomprehensive-guide-to-using-uploadthing-with-express-and-typescript","blog\u002Fcomprehensive-guide-to-using-uploadthing-with-express-and-typescript",{"title":26,"path":27,"stem":28},"🌐 DNS Caching in Node.js: Supercharge Your App's Speed & Reliability 🚀","\u002Fblog\u002Fdns-caching-in-nodejs-supercharge-your-apps-speed-and-reliability","blog\u002Fdns-caching-in-nodejs-supercharge-your-apps-speed-and-reliability",{"title":30,"path":31,"stem":32},"Enhancing Redis for Message Queues: Using External Databases for Payload Storage","\u002Fblog\u002Fenhancing-redis-for-message-queues-using-external-databases-for-payload-storage","blog\u002Fenhancing-redis-for-message-queues-using-external-databases-for-payload-storage",{"title":34,"path":35,"stem":36},"Full Stack Learning Management System (LMS) with MERN Stack: A Complete Guide","\u002Fblog\u002Ffull-stack-learning-management-system-lms-with-mern-stack-a-complete-guide","blog\u002Ffull-stack-learning-management-system-lms-with-mern-stack-a-complete-guide",{"title":38,"path":39,"stem":40},"Getting Started with Redis: Elevating Your Web Applications","\u002Fblog\u002Fgetting-started-with-redis-elevating-your-web-applications","blog\u002Fgetting-started-with-redis-elevating-your-web-applications",{"title":42,"path":43,"stem":44},"How Queues Enhance Your Application Development Process","\u002Fblog\u002Fhow-queues-enhance-your-application-development-process","blog\u002Fhow-queues-enhance-your-application-development-process",{"title":46,"path":47,"stem":48},"Next-Gen E-Learning: Transforming Education for Students and Creators","\u002Fblog\u002Fnext-gen-e-learning-transforming-education-for-students-and-creators","blog\u002Fnext-gen-e-learning-transforming-education-for-students-and-creators",{"title":50,"path":51,"stem":52},"Single Sign-On (SSO) Implementation Using Passport.js in TypeScript","\u002Fblog\u002Fsingle-sign-on-sso-implementation-using-passportjs-in-typescript","blog\u002Fsingle-sign-on-sso-implementation-using-passportjs-in-typescript",{"title":54,"path":55,"stem":56},"Using Multer for Local File Uploads: Benefits, Trade-offs, and Cloud Transfer Integration","\u002Fblog\u002Fusing-multer-for-local-file-uploads-benefits-trade-offs-and-cloud-transfer-integration","blog\u002Fusing-multer-for-local-file-uploads-benefits-trade-offs-and-cloud-transfer-integration",false,{"id":59,"title":18,"author":60,"body":66,"date":326,"description":327,"extension":328,"image":329,"meta":330,"minRead":303,"navigation":331,"path":19,"seo":332,"stem":20,"__hash__":333},"blog\u002Fblog\u002Fcomprehensive-guide-encrypting-images-uploading-to-cloudinary-and-serving-them-securely.md",{"name":61,"to":62,"username":63,"avatar":64},"Sachin Thapa","https:\u002F\u002Fsachinthapa.hashnode.dev\u002F","sachinthapa572",{"src":65,"alt":61},"https:\u002F\u002Fcdn.hashnode.com\u002Fres\u002Fhashnode\u002Fimage\u002Fupload\u002Fv1733546636355\u002Fa4ae8ce5-eb4c-4ed8-ab9d-15a4b8f32ea5.png",{"type":67,"value":68,"toc":302},"minimark",[69,81,84,92,101,108,119,122,124,130,141,147,153,159,165,172,174,180,191,197,203,206,208,214,220,226,232,238,244,250,253,255,261,267,273,276,282,288,291,293,299],[70,71,72,73,80],"p",{},"This guide explains how to implement a secure system for encrypting images, uploading them to a cloud storage solution (",[74,75,79],"a",{"href":76,"rel":77},"https:\u002F\u002Fcloudinary.com\u002F",[78],"nofollow","Cloudinary","), and serving them when needed. We’ll walk through each step in detail, from setting up key generation to encrypting and decrypting images, uploading to Cloudinary, and securely serving them. By the end, you'll understand how everything ties together to form a robust, secure image handling process.",[82,83],"hr",{},[85,86,88],"h2",{"id":87},"step-1-key-generation",[89,90,91],"strong",{},"Step 1: Key Generation",[70,93,94,95,100],{},"To secure the encryption and decryption process, we'll use ",[74,96,99],{"href":97,"rel":98},"https:\u002F\u002Fwww.geeksforgeeks.org\u002Frsa-algorithm-cryptography\u002F",[78],"RSA"," (asymmetric cryptography) to encrypt the AES (symmetric) key used for image encryption.",[102,103,105],"h3",{"id":104},"generating-rsa-keys",[89,106,107],{},"Generating RSA Keys",[109,110,115],"pre",{"className":111,"code":113,"language":114},[112],"language-text","import crypto from \"crypto\";\nimport fs from \"fs\";\n\n\u002F\u002F Generate RSA key pair\nconst { publicKey, privateKey } = crypto.generateKeyPairSync(\"rsa\", {\n  modulusLength: 2048, \u002F\u002F Key size in bits\n  publicKeyEncoding: {\n    type: \"spki\", \u002F\u002F Recommended format for public key\n    format: \"pem\",\n  },\n  privateKeyEncoding: {\n    type: \"pkcs8\", \u002F\u002F Recommended format for private key\n    format: \"pem\",\n  },\n});\n\n\u002F\u002F Save public key to a file\nfs.writeFileSync(\"public_key.pem\", publicKey);\nconsole.log(\"Public key saved to public_key.pem\");\n\n\u002F\u002F Save private key to a file\nfs.writeFileSync(\"private_key.pem\", privateKey);\nconsole.log(\"Private key saved to private_key.pem\");\n","text",[116,117,113],"code",{"__ignoreMap":118},"",[70,120,121],{},"This generates an RSA key pair and saves the keys to files for use in encryption and decryption.",[82,123],{},[85,125,127],{"id":126},"step-2-setting-up-image-uploads",[89,128,129],{},"Step 2: Setting Up Image Uploads",[70,131,132,133,140],{},"We’ll use ",[74,134,137],{"href":135,"rel":136},"https:\u002F\u002Fgithub.com\u002Fexpressjs\u002Fmulter#readme",[78],[116,138,139],{},"multer",", a middleware for handling file uploads in Node.js, to handle user-uploaded images.",[102,142,144],{"id":143},"installing-multer",[89,145,146],{},"Installing Multer",[109,148,151],{"className":149,"code":150,"language":114},[112],"npm install multer\n",[116,152,150],{"__ignoreMap":118},[102,154,156],{"id":155},"configuring-multer",[89,157,158],{},"Configuring Multer",[109,160,163],{"className":161,"code":162,"language":114},[112],"import multer from \"multer\";\nimport path from \"path\";\n\nconst storage = multer.diskStorage({\n  destination: (req, file, cb) => {\n    cb(null, \"uploads\u002F\"); \u002F\u002F Directory to save uploaded files\n  },\n  filename: (req, file, cb) => {\n    const uniqueSuffix = Date.now() + \"-\" + Math.round(Math.random() * 1E9);\n    cb(null, `${uniqueSuffix}-${file.originalname}`);\n  },\n});\n\nexport const upload = multer({ storage });\n",[116,164,162],{"__ignoreMap":118},[70,166,167,168,171],{},"This code configures Multer to save uploaded images to a directory called ",[116,169,170],{},"uploads\u002F",".",[82,173],{},[85,175,177],{"id":176},"step-3-encrypting-images",[89,178,179],{},"Step 3: Encrypting Images",[70,181,182,183,190],{},"We'll use ",[74,184,187],{"href":185,"rel":186},"https:\u002F\u002Fwww.sharesecure.link\u002Farticles\u002Fthe-ultimate-developers-guide-to-aes-gcm-encryption-with-web-cryptography-api#understanding-encryption-how-it-all-works",[78],[89,188,189],{},"AES-GCM"," (a symmetric encryption algorithm) to encrypt the image data and RSA to securely store the encryption key.",[102,192,194],{"id":193},"encryption-function",[89,195,196],{},"Encryption Function",[109,198,201],{"className":199,"code":200,"language":114},[112],"import crypto from \"crypto\";\nimport fs from \"fs\";\n\n\u002F\u002F Load RSA public key\nconst publicKey = fs.readFileSync(\"public_key.pem\", \"utf8\");\n\nexport const encryptImage = (imageFile) => {\n  const algorithm = \"aes-256-gcm\";\n  const key = crypto.randomBytes(32); \u002F\u002F AES key\n  const iv = crypto.randomBytes(16); \u002F\u002F Initialization vector\n  const cipher = crypto.createCipheriv(algorithm, key, iv);\n\n  \u002F\u002F Read the image file into a buffer\n  const imageBuffer = fs.readFileSync(imageFile.path);\n\n  let encrypted = cipher.update(imageBuffer);\n  encrypted = Buffer.concat([encrypted, cipher.final()]);\n  const authTag = cipher.getAuthTag();\n\n  \u002F\u002F Encrypt the AES key with RSA public key\n  const encryptedKey = crypto.publicEncrypt(publicKey, key);\n\n  return {\n    iv: iv.toString(\"hex\"),\n    encryptedData: encrypted.toString(\"base64\"),\n    authTag: authTag.toString(\"hex\"),\n    encryptedKey: encryptedKey.toString(\"base64\"),\n  };\n};\n",[116,202,200],{"__ignoreMap":118},[70,204,205],{},"This function encrypts the image file using AES-GCM and encrypts the AES key with the RSA public key.",[82,207],{},[85,209,211],{"id":210},"step-4-uploading-to-cloudinary",[89,212,213],{},"Step 4: Uploading to Cloudinary",[102,215,217],{"id":216},"installing-cloudinary-sdk",[89,218,219],{},"Installing Cloudinary SDK",[109,221,224],{"className":222,"code":223,"language":114},[112],"npm install cloudinary\n",[116,225,223],{"__ignoreMap":118},[102,227,229],{"id":228},"configuring-cloudinary",[89,230,231],{},"Configuring Cloudinary",[109,233,236],{"className":234,"code":235,"language":114},[112],"import { v2 as cloudinary } from \"cloudinary\";\n\ncloudinary.config({\n  cloud_name: \"your-cloud-name\", \u002F\u002F Replace with your Cloudinary cloud name\n  api_key: \"your-api-key\",       \u002F\u002F Replace with your Cloudinary API key\n  api_secret: \"your-api-secret\", \u002F\u002F Replace with your Cloudinary API secret\n});\n",[116,237,235],{"__ignoreMap":118},[102,239,241],{"id":240},"uploading-encrypted-images",[89,242,243],{},"Uploading Encrypted Images",[109,245,248],{"className":246,"code":247,"language":114},[112],"import fs from \"fs\";\n\nexport const uploadToCloudinary = async (encryptedData, fileName) => {\n  const tempFilePath = `temp\u002F${fileName}.json`;\n  fs.writeFileSync(tempFilePath, JSON.stringify(encryptedData));\n\n  const result = await cloudinary.uploader.upload(tempFilePath, {\n    resource_type: \"raw\",\n    folder: \"encrypted_images\",\n  });\n\n  fs.unlinkSync(tempFilePath); \u002F\u002F Delete temporary file\n  return result.secure_url; \u002F\u002F Return the uploaded file URL\n};\n",[116,249,247],{"__ignoreMap":118},[70,251,252],{},"This uploads the encrypted image data as a JSON file to Cloudinary.",[82,254],{},[85,256,258],{"id":257},"step-5-decrypting-and-serving-images",[89,259,260],{},"Step 5: Decrypting and Serving Images",[102,262,264],{"id":263},"decryption-function",[89,265,266],{},"Decryption Function",[109,268,271],{"className":269,"code":270,"language":114},[112],"\u002F\u002F Load RSA private key\nconst privateKey = fs.readFileSync(\"private_key.pem\", \"utf8\");\n\nexport const decryptImage = (encryptedData) => {\n  const { encryptedData: data, encryptedKey, iv, authTag } = encryptedData;\n\n  \u002F\u002F Decrypt the AES key using RSA private key\n  const key = crypto.privateDecrypt(privateKey, Buffer.from(encryptedKey, \"base64\"));\n\n  const decipher = crypto.createDecipheriv(\"aes-256-gcm\", key, Buffer.from(iv, \"hex\"));\n  decipher.setAuthTag(Buffer.from(authTag, \"hex\"));\n\n  let decrypted = decipher.update(Buffer.from(data, \"base64\"));\n  decrypted = Buffer.concat([decrypted, decipher.final()]);\n\n  return decrypted;\n};\n",[116,272,270],{"__ignoreMap":118},[70,274,275],{},"This function decrypts the AES-encrypted image using the RSA private key and the provided metadata.",[102,277,279],{"id":278},"serving-decrypted-images",[89,280,281],{},"Serving Decrypted Images",[109,283,286],{"className":284,"code":285,"language":114},[112],"import axios from \"axios\";\n\nexport const decodeUserImageController = async (req, res) => {\n  const { version, public_id } = req.params;\n  if (!public_id) {\n    res.status(400).send(\"Invalid request: public_id is required\");\n    return;\n  }\n\n  \u002F\u002F Fetch encrypted data from Cloudinary\n  const encryptedData = (await axios.get(`https:\u002F\u002Fres.cloudinary.com\u002Fyour-cloud-name\u002Fraw\u002Fupload\u002Fv${version}\u002F${public_id}.json`)).data;\n\n  \u002F\u002F Decrypt the image\n  const decrypted = decryptImage(encryptedData);\n\n  \u002F\u002F Serve the decrypted image\n  res.type(\"image\u002Fpng\").send(decrypted);\n};\n",[116,287,285],{"__ignoreMap":118},[70,289,290],{},"This code fetches the encrypted image metadata from Cloudinary, decrypts it, and serves it as a response.",[82,292],{},[85,294,296],{"id":295},"conclusion",[89,297,298],{},"Conclusion",[70,300,301],{},"By following this guide, you can implement a secure system for encrypting and storing sensitive images. Using a combination of AES-GCM for encryption and RSA for key security ensures strong protection. Cloudinary provides a scalable storage solution for encrypted data, and the ability to decrypt and serve images on demand makes this system both flexible and secure.",{"title":118,"searchDepth":303,"depth":303,"links":304},2,[305,309,313,316,321,325],{"id":87,"depth":303,"text":91,"children":306},[307],{"id":104,"depth":308,"text":107},3,{"id":126,"depth":303,"text":129,"children":310},[311,312],{"id":143,"depth":308,"text":146},{"id":155,"depth":308,"text":158},{"id":176,"depth":303,"text":179,"children":314},[315],{"id":193,"depth":308,"text":196},{"id":210,"depth":303,"text":213,"children":317},[318,319,320],{"id":216,"depth":308,"text":219},{"id":228,"depth":308,"text":231},{"id":240,"depth":308,"text":243},{"id":257,"depth":303,"text":260,"children":322},[323,324],{"id":263,"depth":308,"text":266},{"id":278,"depth":308,"text":281},{"id":295,"depth":303,"text":298},"2025-01-08","Learn to encrypt images, upload securely to Cloudinary, and serve them safely with this comprehensive guide on secure image handling processes","md","https:\u002F\u002Fcdn.hashnode.com\u002Fres\u002Fhashnode\u002Fimage\u002Fupload\u002Fv1736336845591\u002F69561304-6153-4593-af17-8b70ed9e0c23.png",{},true,{"title":18,"description":327},"xZ--h-lzziUlVwdCHYYvIQ0TDDm-H-xEIskD8MJqHJg",[335,337],{"title":14,"path":15,"stem":16,"description":336,"children":-1},"Learn how to implement background DNS refresh in Node.js to maintain consistent performance and eliminate latency spikes in high-traffic applications",{"title":22,"path":23,"stem":24,"description":338,"children":-1},"This note provides a detailed exploration of setting up file uploads using Uploadthing in an Express.js application with TypeScript, focusing on security best practices and the potential use of signed URLs."]