• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Step-by-Step Guide to building a custom secure file encryption vault block for Gutenberg using React components

Step-by-Step Guide to building a custom secure file encryption vault block for Gutenberg using React components

Setting Up the WordPress Plugin and React Environment

To build a custom Gutenberg block for a secure file encryption vault, we’ll leverage React for the block’s frontend and editor interfaces. This involves setting up a standard WordPress plugin structure and configuring a modern JavaScript build process. We’ll use `@wordpress/scripts` for this, which provides a streamlined way to compile React components, Sass, and other assets for Gutenberg.

First, create a new directory for your plugin within the wp-content/plugins/ directory of your WordPress installation. Let’s name it secure-file-vault.

Plugin Structure and `plugin.php`

Inside the secure-file-vault directory, create the main plugin file, secure-file-vault.php. This file will contain the plugin header and the necessary hooks to register our Gutenberg block.

<?php
/**
 * Plugin Name: Secure File Vault
 * Plugin URI: https://example.com/secure-file-vault
 * Description: A custom Gutenberg block for securely encrypting and storing files.
 * Version: 1.0.0
 * Author: Your Name
 * Author URI: https://yourwebsite.com
 * License: GPL-2.0-or-later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: secure-file-vault
 * Domain Path: /languages
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
function secure_file_vault_block_init() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'secure_file_vault_block_init' );

`block.json` for Block Registration

Next, create a block.json file in the root of your plugin directory. This file describes your block to WordPress, including its name, title, category, and importantly, the script and style handles that will be enqueued.

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "secure-file-vault/vault",
    "version": "1.0.0",
    "title": "Secure File Vault",
    "category": "widgets",
    "icon": "lock",
    "description": "A secure vault for encrypting and storing files.",
    "keywords": ["encryption", "security", "vault", "file"],
    "attributes": {
        "fileId": {
            "type": "string",
            "default": ""
        },
        "encryptionKey": {
            "type": "string",
            "default": ""
        }
    },
    "supports": {
        "html": false
    },
    "textdomain": "secure-file-vault",
    "editorScript": "file:./build/index.js",
    "editorStyle": "file:./build/index.css",
    "style": "file:./build/style-index.css"
}

Note the editorScript, editorStyle, and style properties. These point to the compiled assets that will be generated by our build process. The attributes section defines the data that our block will manage, such as the ID of the uploaded file and the encryption key.

Installing Dependencies and Building Assets

We need to install the necessary Node.js dependencies. Navigate to your plugin’s directory in your terminal and run:

npm init -y
npm install @wordpress/scripts --save-dev

This installs @wordpress/scripts, which provides the build tools. Now, add a script to your package.json file to easily run the build process:

{
  // ... other package.json content
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  }
  // ...
}

The build script will compile your JavaScript and CSS into the build directory, as specified in block.json. The start script will watch for changes and recompile automatically, which is useful during development.

To perform the initial build, run:

npm run build

This will create the build directory containing index.js, index.css, and style-index.css.

Developing the Block’s Editor Interface (React)

The core of our block’s functionality will be built using React components within the WordPress block editor. We’ll create a source directory for our JavaScript and CSS files.

`src/index.js` – Block Registration and Editor Component

Create a src directory in your plugin’s root. Inside src, create index.js. This file will register the block and define its editor interface.

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import './style.scss'; // For front-end styles
import './editor.scss'; // For editor-only styles
import Edit from './edit';
import save from './save';

registerBlockType( 'secure-file-vault/vault', {
    edit: Edit,
    save,
} );

This code imports necessary WordPress packages, registers our block type, and points to the Edit component for the editor view and the save function for the front-end output.

`src/edit.js` – The Editor Component

Now, let’s create the src/edit.js file. This component will handle the user interface within the Gutenberg editor, allowing users to select a file and input an encryption key.

import { __ } from '@wordpress/i18n';
import {
    useBlockProps,
    MediaUpload,
    MediaUploadCheck,
} from '@wordpress/block-editor';
import { Button, TextControl } from '@wordpress/components';
import './editor.scss';

export default function Edit( { attributes, setAttributes } ) {
    const blockProps = useBlockProps();
    const { fileId, encryptionKey } = attributes;

    const onSelectFile = ( media ) => {
        if ( ! media || ! media.id ) {
            return;
        }
        setAttributes( { fileId: media.id.toString() } );
    };

    const onEncryptionKeyChange = ( value ) => {
        setAttributes( { encryptionKey: value } );
    };

    return (
        <div { ...blockProps }>
            <h3>{ __( 'Secure File Vault Configuration', 'secure-file-vault' ) }</h3>
            <MediaUploadCheck>
                <MediaUpload
                    onSelect={ onSelectFile }
                    allowedTypes={ [ 'image', 'video', 'audio', 'application/pdf', 'application/zip' ] } // Customize allowed file types
                    value={ fileId }
                    render={ ( { open } ) => (
                        <Button
                            variant="primary"
                            icon="upload"
                            onClick={ open }
                            disabled={ !! fileId }
                        >
                            { fileId ? __( 'File Selected', 'secure-file-vault' ) : __( 'Upload File', 'secure-file-vault' ) }
                        </Button>
                    ) }
                />
            </MediaUploadCheck>
            { fileId && (
                <p>{ __( 'File ID:', 'secure-file-vault' ) } { fileId }</p>
            ) }
            <TextControl
                label={ __( 'Encryption Key (AES-256)', 'secure-file-vault' ) }
                value={ encryptionKey }
                onChange={ onEncryptionKeyChange }
                type="password"
                help={ __( 'Enter a strong, unique key for encryption. This key is required to decrypt the file.', 'secure-file-vault' ) }
            />
            <p><small>{ __( 'Note: The actual file content is not stored directly in the post. This block manages metadata and provides a mechanism for secure access.', 'secure-file-vault' ) }</small></p>
        </div>
    );
}

In this component:

  • We use useBlockProps to get the necessary props for the block’s wrapper element.
  • MediaUploadCheck and MediaUpload are used to integrate with the WordPress Media Library, allowing users to select files. We’ve specified common allowed types, but this can be customized.
  • TextControl provides a password input field for the encryption key.
  • The attributes (fileId and encryptionKey) are managed using setAttributes, ensuring they are saved with the post.

`src/save.js` – The Front-end Output

The src/save.js file defines how the block will be rendered on the front-end of the website. For a secure vault, we don’t want to expose the file content directly. Instead, we’ll render a placeholder or a link that requires authentication and decryption.

import { useBlockProps } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';

export default function save( { attributes } ) {
    const blockProps = useBlockProps.save();
    const { fileId, encryptionKey } = attributes;

    // In a real-world scenario, you would NOT save the encryptionKey directly in the block's saved HTML.
    // This is a placeholder for demonstration. The key should be handled securely,
    // perhaps via a user session, a secure API, or a separate secure storage mechanism.
    // For this example, we'll just indicate that a file is associated.

    if ( ! fileId ) {
        return null; // Don't render anything if no file is selected.
    }

    return (
        <div { ...blockProps }>
            <p>{ __( 'Secure File Vault', 'secure-file-vault' ) }</p>
            <p>{ __( 'Encrypted file associated. Access requires proper authentication and decryption.', 'secure-file-vault' ) }</p>
            { /* In a production system, you'd have a mechanism here to trigger download/access
                 after user authentication and providing the correct key, without exposing the key itself. */ }
        </div>
    );
}

Crucially, we are not saving the encryptionKey directly in the save function’s output. This is a critical security consideration. The key should never be exposed in the client-side HTML. The front-end logic would need to interact with a secure backend API to handle decryption and file retrieval.

Styling the Block

Create src/style.scss for front-end styles and src/editor.scss for editor-specific styles.

/* src/style.scss */
.wp-block-secure-file-vault-vault {
    border: 1px solid #ccc;
    padding: 15px;
    margin-bottom: 15px;
    background-color: #f9f9f9;
    border-radius: 4px;

    p {
        margin-bottom: 0.5em;
    }
}
/* src/editor.scss */
.wp-block-secure-file-vault-vault {
    background-color: #eef7ff;
    border: 1px dashed #0073aa;
    padding: 20px;
    text-align: center;

    h3 {
        color: #005177;
        margin-top: 0;
    }

    .components-button {
        margin-top: 10px;
        margin-bottom: 15px;
    }

    .components-text-control {
        margin-top: 15px;
    }
}

After creating these files, run npm run build again to compile the SCSS into CSS files within the build directory.

Server-Side Encryption and Decryption Logic (Conceptual)

The client-side React code handles the user interface and attribute management. However, the actual secure file encryption and decryption must happen server-side to protect the data and keys. This involves several architectural considerations:

File Storage Strategy

Files should not be stored directly in the WordPress uploads directory if they are sensitive. Instead, consider:

  • External Object Storage: Services like AWS S3, Google Cloud Storage, or Azure Blob Storage are ideal. Files can be uploaded directly to these services (potentially via a secure, signed URL generated by your backend) and their IDs or references stored in the WordPress database.
  • Encrypted Database Storage: For smaller files, encrypting them and storing them as BLOBs in a separate, highly secured database table.

Encryption Key Management

This is the most critical part. The encryption key provided by the user in the block editor should:

  • Never be stored in plain text in the WordPress database or post meta.
  • Be used to derive a symmetric encryption key (e.g., AES-256) for the file.
  • Be handled securely during the encryption/decryption process. A common pattern is to use a master key (stored securely, e.g., in environment variables or a secrets manager) to encrypt the file-specific encryption key, which is then stored alongside the file reference.
  • Be provided by the user at the time of access, not stored persistently with the block’s attributes.

Backend API Endpoints

You’ll need to create custom WordPress REST API endpoints or AJAX handlers to:

  • Handle file uploads: Securely receive files, encrypt them using the provided key (or a derived key), and store them in your chosen storage solution.
  • Provide secure download links: When a user requests a file, authenticate them, prompt for the decryption key (if not already provided and validated), decrypt the file server-side, and stream it back to the user.
  • Manage file metadata: Store references to encrypted files, their storage locations, and any necessary decryption metadata.

Example PHP (Conceptual – REST API)

Here’s a conceptual example of a PHP endpoint for initiating a secure download. This would typically be part of a larger class or structure.

 'POST', // Use POST to send the key securely
        'callback'            => 'handle_secure_file_download',
        'permission_callback' => '__return_true', // Implement proper authentication/authorization
    ) );
} );

function handle_secure_file_download( WP_REST_Request $request ) {
    $file_post_id = $request->get_param( 'id' ); // This would be the post ID containing the block
    $encryption_key = $request->get_param( 'encryption_key' ); // Key sent via POST body

    if ( ! $file_post_id || ! is_numeric( $file_post_id ) ) {
        return new WP_Error( 'invalid_request', 'Invalid file ID.', array( 'status' => 400 ) );
    }

    // 1. Authenticate User (e.g., check if logged in, check capabilities)
    if ( ! is_user_logged_in() ) {
        return new WP_Error( 'unauthorized', 'You must be logged in to access this file.', array( 'status' => 401 ) );
    }

    // 2. Retrieve Block Attributes (or associated metadata)
    // This requires a function to get block attributes for a given post.
    // For simplicity, let's assume you have a function `get_secure_vault_attributes($post_id)`
    // that returns an array like ['fileId' => '...', 'encryptionKey' => '...']
    // IMPORTANT: The encryptionKey from attributes is NOT the one used for download.
    // It's a user-provided hint or part of a key derivation process.
    $block_attributes = get_secure_vault_attributes( $file_post_id ); // You need to implement this function

    if ( ! $block_attributes || empty( $block_attributes['fileId'] ) ) {
        return new WP_Error( 'not_found', 'Secure vault block or file not configured.', array( 'status' => 404 ) );
    }

    $stored_file_reference = get_post_meta( $file_post_id, '_secure_vault_file_ref_' . $block_attributes['fileId'], true ); // Example meta key
    $stored_file_path_or_url = $stored_file_reference['path'] ?? ''; // e.g., S3 URL or local path
    $stored_encrypted_data = file_get_contents( $stored_file_path_or_url ); // Fetch encrypted data

    if ( ! $stored_encrypted_data ) {
        return new WP_Error( 'file_error', 'Could not retrieve encrypted file.', array( 'status' => 500 ) );
    }

    // 3. Decrypt the File (Server-Side)
    // This is where your actual decryption logic goes.
    // You'll need a robust crypto library (e.g., OpenSSL PHP extension).
    // The $encryption_key from the request MUST be validated and used correctly.
    // It might be used to decrypt a file-specific key, which then decrypts the file.

    // Example using OpenSSL (requires proper key handling and IV management)
    // This is a simplified example and needs significant security hardening.
    $iv_length = openssl_cipher_iv_length('aes-256-cbc');
    // You'd need to extract the IV from the stored data or have it managed.
    // Let's assume IV is prepended to the encrypted data for this example.
    $iv = substr($stored_encrypted_data, 0, $iv_length);
    $encrypted_data_only = substr($stored_encrypted_data, $iv_length);

    // IMPORTANT: The $encryption_key from the request must be securely handled.
    // It's often better to use a master key to decrypt a file-specific key.
    // For this example, we'll assume the user provided key is directly usable (NOT RECOMMENDED FOR PRODUCTION).
    $decrypted_content = openssl_decrypt(
        $encrypted_data_only,
        'aes-256-cbc',
        $encryption_key, // Use a securely managed key here!
        OPENSSL_RAW_DATA,
        $iv
    );

    if ( $decrypted_content === false ) {
        return new WP_Error( 'decryption_failed', 'File decryption failed. Invalid key or corrupted data.', array( 'status' => 400 ) );
    }

    // 4. Stream the Decrypted File
    $file_info = wp_check_filetype_and_ext( $stored_file_path_or_url, basename( $stored_file_path_or_url ) );
    $mime_type = $file_info['type'] ?? 'application/octet-stream';
    $filename = sanitize_file_name( 'decrypted-' . basename( $stored_file_path_or_url ) );

    header( 'Content-Description: File Transfer' );
    header( 'Content-Type: ' . $mime_type );
    header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
    header( 'Expires: 0' );
    header( 'Cache-Control: must-revalidate' );
    header( 'Pragma: public' );
    header( 'Content-Length: ' . strlen( $decrypted_content ) );

    echo $decrypted_content;
    exit; // Important to stop further WordPress output
}

// Placeholder function - you need to implement this to retrieve block attributes
function get_secure_vault_attributes( $post_id ) {
    $post = get_post( $post_id );
    if ( ! $post ) {
        return false;
    }

    $blocks = parse_blocks( $post->post_content );
    foreach ( $blocks as $block ) {
        if ( $block['blockName'] === 'secure-file-vault/vault' ) {
            // Return attributes, ensuring fileId and encryptionKey are present
            return array(
                'fileId' => $block['attrs']['fileId'] ?? '',
                'encryptionKey' => $block['attrs']['encryptionKey'] ?? '', // Again, this key is NOT for direct download use
            );
        }
    }
    return false;
}

?>

This conceptual PHP code outlines the steps: authentication, retrieving file references and encrypted data, server-side decryption using PHP’s OpenSSL extension, and streaming the decrypted file. Crucially, the encryption_key should be handled with extreme care. In a production system, you would likely use a more sophisticated key management strategy, such as encrypting the file’s symmetric key with a user-specific or application-specific master key stored securely.

Deployment and Security Best Practices

When deploying this solution, prioritize security:

  • Never store sensitive keys in version control. Use environment variables or a dedicated secrets management system (e.g., HashiCorp Vault, AWS Secrets Manager).
  • Implement robust authentication and authorization for all API endpoints.
  • Sanitize all user inputs rigorously.
  • Use strong, modern encryption algorithms (e.g., AES-256-GCM) and manage Initialization Vectors (IVs) securely.
  • Regularly audit your security practices and dependencies.
  • Consider rate limiting on API endpoints to prevent brute-force attacks.
  • Educate users on creating strong, unique encryption keys.

Building a secure file vault within WordPress requires a multi-layered approach, combining secure client-side interactions with robust server-side encryption and key management. This guide provides the foundational Gutenberg block development and conceptual server-side architecture.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • How to design a modular Action-hook Event Mediator architecture for enterprise-level custom plugins
  • Step-by-Step Guide to building a custom database optimizer portal block for Gutenberg using Next.js headless configurations
  • Optimizing WooCommerce cart response times by lazy loading custom user transaction ledgers assets
  • Step-by-Step Guide: Offloading high-frequency custom subscription logs metadata writes to a Redis KV store
  • How to design a modular Command Query Responsibility Segregation (CQRS) architecture for enterprise-level custom plugins

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (41)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (65)
  • WordPress Plugin Development (72)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • How to design a modular Action-hook Event Mediator architecture for enterprise-level custom plugins
  • Step-by-Step Guide to building a custom database optimizer portal block for Gutenberg using Next.js headless configurations
  • Optimizing WooCommerce cart response times by lazy loading custom user transaction ledgers assets

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala