FEATURE: Docker log-based password recovery with KEK-DEK preservation #303

Merged
ZacharyZcR merged 1 commits from main into dev-1.7.0 2025-09-26 21:30:10 +00:00
ZacharyZcR commented 2025-09-26 07:43:18 +00:00 (Migrated from github.com)

Summary

  • Implements Docker log-based password recovery system for KEK-DEK encrypted applications
  • Adds compromise mode to zero-trust architecture for improved user experience
  • Fixes critical password reset vulnerability that caused permanent data loss
  • Provides migration path to zero-trust mode for future security upgrades

Key Features

  • Physical Access Control: 6-digit recovery codes output to Docker logs requiring server access
  • Data Preservation: Recovery DEK layer preserves encrypted user data during password reset
  • Zero-Trust Migration: Gradual upgrade path from compromise to zero-trust mode
  • Critical Bug Fix: Prevents permanent data loss during password reset operations

Technical Implementation

  • Database Schema: Added recovery_dek, backup_encrypted_dek, zero_trust_mode fields
  • API Endpoints: New /recovery/* endpoints for complete recovery flow
  • UI Components: Redesigned password recovery interface with step-by-step flow
  • Crypto Layer: Recovery layer integrated into existing KEK-DEK architecture
  • Migration Utility: ZeroTrustMigration class for future security upgrades

Security Model

  • Physical access required (Docker logs access)
  • 1-minute code expiry with 3-attempt brute force protection
  • Recovery keys stored encrypted in database
  • Maintains KEK-DEK framework for future zero-trust restoration

Breaking Changes

  • Temporarily breaks zero-trust model for user experience (reversible)
  • Adds new database fields requiring schema migration

Bug Fixes

  • Fixed critical vulnerability in password reset causing permanent encrypted data loss
  • Fixed JWT token storage inconsistency in recovery login flow
  • Proper KEK-DEK re-encryption during password reset operations

Test Plan

  • Verify recovery code generation in Docker logs
  • Test complete recovery flow from code to login
  • Validate encrypted data preservation during password reset
  • Confirm zero-trust migration functionality
  • Test edge cases (expired codes, invalid attempts)

🤖 Generated with Claude Code

## Summary - Implements Docker log-based password recovery system for KEK-DEK encrypted applications - Adds compromise mode to zero-trust architecture for improved user experience - Fixes critical password reset vulnerability that caused permanent data loss - Provides migration path to zero-trust mode for future security upgrades ## Key Features - **Physical Access Control**: 6-digit recovery codes output to Docker logs requiring server access - **Data Preservation**: Recovery DEK layer preserves encrypted user data during password reset - **Zero-Trust Migration**: Gradual upgrade path from compromise to zero-trust mode - **Critical Bug Fix**: Prevents permanent data loss during password reset operations ## Technical Implementation - **Database Schema**: Added `recovery_dek`, `backup_encrypted_dek`, `zero_trust_mode` fields - **API Endpoints**: New `/recovery/*` endpoints for complete recovery flow - **UI Components**: Redesigned password recovery interface with step-by-step flow - **Crypto Layer**: Recovery layer integrated into existing KEK-DEK architecture - **Migration Utility**: `ZeroTrustMigration` class for future security upgrades ## Security Model - Physical access required (Docker logs access) - 1-minute code expiry with 3-attempt brute force protection - Recovery keys stored encrypted in database - Maintains KEK-DEK framework for future zero-trust restoration ## Breaking Changes - Temporarily breaks zero-trust model for user experience (reversible) - Adds new database fields requiring schema migration ## Bug Fixes - Fixed critical vulnerability in password reset causing permanent encrypted data loss - Fixed JWT token storage inconsistency in recovery login flow - Proper KEK-DEK re-encryption during password reset operations ## Test Plan - [ ] Verify recovery code generation in Docker logs - [ ] Test complete recovery flow from code to login - [ ] Validate encrypted data preservation during password reset - [ ] Confirm zero-trust migration functionality - [ ] Test edge cases (expired codes, invalid attempts) 🤖 Generated with [Claude Code](https://claude.ai/code)
gemini-code-assist[bot] commented 2025-09-26 07:43:35 +00:00 (Migrated from github.com)

Summary of Changes

Hello @ZacharyZcR, I'm Gemini Code Assist1 ! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the application's password recovery capabilities and security posture. It introduces a user-friendly recovery mechanism that leverages Docker logs for physical access verification, while critically ensuring that user-encrypted data remains intact during password resets. Furthermore, it lays the groundwork for a more robust security model by offering a migration path from a 'compromise mode' (with recovery) to a 'zero-trust mode' (requiring a user-managed recovery seed), giving users control over their security trade-offs.

Highlights

  • Docker Log-Based Password Recovery: Introduces a new password recovery system where a 6-digit recovery code is output to Docker logs, requiring physical server access for retrieval. This allows users to regain access while preserving encrypted data.
  • KEK-DEK Preservation During Password Reset: Implements a critical fix that ensures user data encrypted with KEK-DEK (Key Encryption Key - Data Encryption Key) is preserved during password reset operations by re-encrypting the DEK with the new KEK derived from the new password.
  • Compromise Mode and Zero-Trust Migration: Adds a 'compromise mode' for improved user experience, which stores a recovery DEK. It also provides a migration path to a 'zero-trust mode' where the plaintext recovery DEK is removed, and a user-provided recovery seed is required for recovery.
  • Critical Password Reset Vulnerability Fix: Addresses a vulnerability that previously caused permanent data loss when users reset their passwords, ensuring that encrypted data remains accessible after a reset.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.


  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩︎

## Summary of Changes Hello @ZacharyZcR, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the application's password recovery capabilities and security posture. It introduces a user-friendly recovery mechanism that leverages Docker logs for physical access verification, while critically ensuring that user-encrypted data remains intact during password resets. Furthermore, it lays the groundwork for a more robust security model by offering a migration path from a 'compromise mode' (with recovery) to a 'zero-trust mode' (requiring a user-managed recovery seed), giving users control over their security trade-offs. ### Highlights * **Docker Log-Based Password Recovery**: Introduces a new password recovery system where a 6-digit recovery code is output to Docker logs, requiring physical server access for retrieval. This allows users to regain access while preserving encrypted data. * **KEK-DEK Preservation During Password Reset**: Implements a critical fix that ensures user data encrypted with KEK-DEK (Key Encryption Key - Data Encryption Key) is preserved during password reset operations by re-encrypting the DEK with the new KEK derived from the new password. * **Compromise Mode and Zero-Trust Migration**: Adds a 'compromise mode' for improved user experience, which stores a recovery DEK. It also provides a migration path to a 'zero-trust mode' where the plaintext recovery DEK is removed, and a user-provided recovery seed is required for recovery. * **Critical Password Reset Vulnerability Fix**: Addresses a vulnerability that previously caused permanent data loss when users reset their passwords, ensuring that encrypted data remains accessible after a reset. <details> <summary><b>Using Gemini Code Assist</b></summary> <br> The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips. <b>Invoking Gemini</b> You can request assistance from Gemini at any point by creating a comment using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands on the current page. Feature | Command | Description --- | --- | --- Code Review | `/gemini review` | Performs a code review for the current pull request in its current state. Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state. Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in pull request comments and review comments. Help | `/gemini help` | Displays a list of available commands. <b>Customization</b> To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github). <b>Limitations & Feedback</b> Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up [here](https://google.qualtrics.com/jfe/form/SV_2cyuGuTWsEw84yG). <b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b> </details> [^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).
gemini-code-assist[bot] (Migrated from github.com) reviewed 2025-09-26 07:44:50 +00:00
gemini-code-assist[bot] (Migrated from github.com) left a comment

Code Review

This pull request introduces a significant new feature: a Docker log-based password recovery system. This is a well-intentioned effort to improve user experience and fix a critical data loss bug during password reset. The implementation correctly preserves user data during a reset and provides a clear migration path to a more secure zero-trust model. However, the current "compromise mode" introduces several critical security vulnerabilities by storing decryption keys in plaintext within the database, both permanently in the users table and temporarily in the settings table. My review focuses on these critical security issues, along with other high-severity concerns related to cryptographic best practices (improper salt usage), unsafe key construction, and code maintainability (bypassing type safety). These must be addressed before this feature can be safely merged.

## Code Review This pull request introduces a significant new feature: a Docker log-based password recovery system. This is a well-intentioned effort to improve user experience and fix a critical data loss bug during password reset. The implementation correctly preserves user data during a reset and provides a clear migration path to a more secure zero-trust model. However, the current "compromise mode" introduces several critical security vulnerabilities by storing decryption keys in plaintext within the database, both permanently in the `users` table and temporarily in the `settings` table. My review focuses on these critical security issues, along with other high-severity concerns related to cryptographic best practices (improper salt usage), unsafe key construction, and code maintainability (bypassing type safety). These must be addressed before this feature can be safely merged.
@@ -1262,0 +1297,4 @@
// Clean sensitive data
originalDEK.fill(0);
newKEK.fill(0);
newRecoveryDEK.fill(0);
gemini-code-assist[bot] (Migrated from github.com) commented 2025-09-26 07:44:50 +00:00

high

The code frequently uses (userCrypto as any) to access methods like decryptDEK, generateKEKSalt, deriveKEK, encryptDEK, storeKEKSalt, and storeEncryptedDEK. This bypasses TypeScript's type safety, breaks the encapsulation of the UserCrypto class, and makes the code brittle and hard to refactor. These methods are likely private for a reason. A proper public API should be exposed on the UserCrypto class to handle the password reset and re-encryption flow in a controlled and type-safe manner.

![high](https://www.gstatic.com/codereviewagent/high-priority.svg) The code frequently uses `(userCrypto as any)` to access methods like `decryptDEK`, `generateKEKSalt`, `deriveKEK`, `encryptDEK`, `storeKEKSalt`, and `storeEncryptedDEK`. This bypasses TypeScript's type safety, breaks the encapsulation of the `UserCrypto` class, and makes the code brittle and hard to refactor. These methods are likely private for a reason. A proper public API should be exposed on the `UserCrypto` class to handle the password reset and re-encryption flow in a controlled and type-safe manner.
@@ -1935,0 +2139,4 @@
const originalDEK = (userCrypto as any).decryptDEK(backupEncryptedDEK, recoveryDEK);
// Create user session with decrypted DEK
const tempToken = crypto.randomBytes(32).toString('hex');
gemini-code-assist[bot] (Migrated from github.com) commented 2025-09-26 07:44:50 +00:00

medium

Math.random() is not a cryptographically secure random number generator. For security-sensitive operations like generating a recovery code, it is best practice to use a secure source of randomness. Node.js's crypto module provides a safer alternative.

    const recoveryCode = crypto.randomInt(100000, 1000000).toString();
![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg) `Math.random()` is not a cryptographically secure random number generator. For security-sensitive operations like generating a recovery code, it is best practice to use a secure source of randomness. Node.js's `crypto` module provides a safer alternative. ```suggestion const recoveryCode = crypto.randomInt(100000, 1000000).toString(); ```
@@ -1935,0 +2143,4 @@
const expiresAt = Date.now() + 5 * 60 * 1000; // 5 minutes
// Store temporary session
const tempKey = `temp_recovery_session_${username}`;
gemini-code-assist[bot] (Migrated from github.com) commented 2025-09-26 07:44:50 +00:00

high

Constructing the database key for the recovery code using a raw username (recovery_code_${username}) is unsafe. If a username contains special characters, it could lead to unexpected behavior or potential injection vulnerabilities, depending on the underlying database driver. It is much safer to use a stable, sanitized identifier, such as the user's ID, to construct this key. This same issue applies to the temporary session key on line 2146.

![high](https://www.gstatic.com/codereviewagent/high-priority.svg) Constructing the database key for the recovery code using a raw username (`recovery_code_${username}`) is unsafe. If a username contains special characters, it could lead to unexpected behavior or potential injection vulnerabilities, depending on the underlying database driver. It is much safer to use a stable, sanitized identifier, such as the user's ID, to construct this key. This same issue applies to the temporary session key on line 2146.
@@ -1935,0 +2256,4 @@
username,
userId: sessionData.userId,
});
gemini-code-assist[bot] (Migrated from github.com) commented 2025-09-26 07:44:49 +00:00

critical

Storing the original Data Encryption Key (dekHex) in plaintext in the settings table, even as part of a temporary session, is a critical security vulnerability. An attacker with database access during this 5-minute window can capture the DEK and decrypt all of that user's data. The DEK should never be stored in plaintext anywhere on the server. Instead, consider creating a temporary, server-side encrypted session that doesn't expose the raw DEK, or pass the DEK to the client to hold in memory for the next step of the flow.

![critical](https://www.gstatic.com/codereviewagent/critical.svg) Storing the original Data Encryption Key (`dekHex`) in plaintext in the `settings` table, even as part of a temporary session, is a critical security vulnerability. An attacker with database access during this 5-minute window can capture the DEK and decrypt all of that user's data. The DEK should never be stored in plaintext anywhere on the server. Instead, consider creating a temporary, server-side encrypted session that doesn't expose the raw DEK, or pass the DEK to the client to hold in memory for the next step of the flow.
@@ -0,0 +86,4 @@
100000,
32,
'sha256'
);
gemini-code-assist[bot] (Migrated from github.com) commented 2025-09-26 07:44:50 +00:00

high

The crypto.pbkdf2Sync function is using a hardcoded string 'zero_trust_recovery' as the salt. When the salt is the same for all users, it is not a salt but a "pepper". This is less secure than using a unique, randomly generated salt for each user, as it makes multi-target attacks (like rainbow tables) more feasible. A unique salt should be generated for each user and stored in the database alongside the backup_encrypted_dek.

![high](https://www.gstatic.com/codereviewagent/high-priority.svg) The `crypto.pbkdf2Sync` function is using a hardcoded string `'zero_trust_recovery'` as the salt. When the salt is the same for all users, it is not a salt but a "pepper". This is less secure than using a unique, randomly generated salt for each user, as it makes multi-target attacks (like rainbow tables) more feasible. A unique salt should be generated for each user and stored in the database alongside the `backup_encrypted_dek`.
@@ -251,0 +340,4 @@
}
function resetRecoveryState() {
setRecoveryStep("request");
gemini-code-assist[bot] (Migrated from github.com) commented 2025-09-26 07:44:50 +00:00

medium

This console.log statement appears to be for debugging purposes. It should be removed from the production codebase to avoid leaking information about token handling into the browser console.

![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg) This `console.log` statement appears to be for debugging purposes. It should be removed from the production codebase to avoid leaking information about token handling into the browser console.
gemini-code-assist[bot] (Migrated from github.com) commented 2025-09-26 07:44:50 +00:00

medium

The legacy password reset flow is conditionally rendered based on false && .... This is an unconventional way to disable a feature in the UI and leaves dead code in the component. If this flow is deprecated, the code should be removed entirely to improve maintainability. If it needs to be kept for reference, it should be properly commented out.

![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg) The legacy password reset flow is conditionally rendered based on `false && ...`. This is an unconventional way to disable a feature in the UI and leaves dead code in the component. If this flow is deprecated, the code should be removed entirely to improve maintainability. If it needs to be kept for reference, it should be properly commented out.
Sign in to join this conversation.