04/26 Enhanced functionalities
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
# PassKeeper 🔐
|
||||
|
||||
A self-hosted, zero-knowledge password manager — web app and browser extension. Modelled after LastPass, built on Flask + MySQL + Web Crypto API.
|
||||
|
||||
Your master password and decrypted vault data **never leave your browser**. The server stores only encrypted blobs it cannot read.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Web App
|
||||
- **Zero-knowledge encryption** — AES-256-GCM client-side, 600k-iteration PBKDF2 vault key derivation
|
||||
- **7 item types** — Passwords, Secure Notes, Payment Cards, Bank Accounts, Addresses, Identities, Passkeys
|
||||
- **TOTP/2FA support** — per-site TOTP codes stored inside the encrypted vault blob; live 6-digit display with countdown
|
||||
- **Folder organisation** — create, rename, delete folders; filter vault by folder
|
||||
- **Item sharing** — zero-knowledge ECDH P-256 re-encryption; share with any registered user
|
||||
- **Emergency access** — configurable wait-timer access grant for a trusted contact
|
||||
- **Security dashboard** — weak, reused, and old password detection + **HaveIBeenPwned k-anonymity breach check** (passwords never transmitted)
|
||||
- **Account MFA** — TOTP-based login verification (Google Authenticator / Authy)
|
||||
- **Master password change** — atomic zero-knowledge re-encryption of the entire vault
|
||||
- **Account recovery** — 128-bit recovery code; server never stores it
|
||||
- **Audit log** — server-side trail of all create/edit/delete actions; no sensitive data ever logged
|
||||
- **Encrypted item names** — item names stored as AES-256-GCM ciphertext; server holds only the item type as a label
|
||||
- **Responsive layout** — phone, tablet, laptop, and large desktop breakpoints; collapsible sidebar
|
||||
|
||||
### Browser Extension (Chrome / Edge — Manifest V3)
|
||||
- **Autofill** — detects login forms; injects icon into username and password fields only (scored heuristic, not all text inputs)
|
||||
- **Suggestion dropdown** — anchored below the focused field; filters as you type; keyboard navigation (↑↓ Enter); fills with one click
|
||||
- **Smart domain matching** — matches vault items by domain name, handles bare domains (`github.com`) and subdomains
|
||||
- **Auto-save banner** — prompts to save or update credentials on form submit; duplicate detection; "Never for this site" blocklist
|
||||
- **Inline password generator** — length slider, charset toggles, strength indicator; fully CSPRNG (`crypto.getRandomValues` throughout)
|
||||
- **TOTP live display** — 6-digit code + countdown timer per item in the popup
|
||||
- **SSO bridge** — log in once on the web app; extension picks up the session automatically
|
||||
- **Idle lock** — configurable auto-lock timeout (1 / 5 / 10 / 30 min / Never)
|
||||
- **Security** — decrypted vault data stored in `chrome.storage.session` only (memory-only, never written to disk)
|
||||
|
||||
---
|
||||
|
||||
## Security Architecture
|
||||
|
||||
```
|
||||
Master Password
|
||||
│
|
||||
├─ PBKDF2(email, 100k iter) ──► authHash ──► POST /api/auth/login
|
||||
│ │
|
||||
│ Argon2id(authHash) stored in DB
|
||||
│
|
||||
└─ PBKDF2(enc_key_salt, 600k iter) ──► vaultKey (stays in browser memory only)
|
||||
│
|
||||
AES-256-GCM encrypt
|
||||
│
|
||||
enc_data + iv ──► POST /api/vault
|
||||
enc_name + iv_name (item name, encrypted separately)
|
||||
```
|
||||
|
||||
The server is blind to all vault content. A database breach exposes only encrypted ciphertext.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|---|---|
|
||||
| Backend | Python 3.12, Flask 3.x |
|
||||
| Database | MySQL 8.x |
|
||||
| Frontend | Vanilla JS, Web Crypto API, Jinja2 |
|
||||
| Auth | Argon2id + PBKDF2 + JWT (HS256) |
|
||||
| Encryption | AES-256-GCM (client-side) |
|
||||
| Extension | Chrome Manifest V3 |
|
||||
| Web server | Nginx + Gunicorn + systemd |
|
||||
| Rate limiting | Flask-Limiter + Redis |
|
||||
| TLS | Let's Encrypt / Certbot |
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.12+
|
||||
- MySQL 8.x
|
||||
- Node.js is **not** required — no build step
|
||||
|
||||
### Development (Windows)
|
||||
|
||||
```bash
|
||||
# 1. Clone and set up virtual environment
|
||||
git clone https://github.com/yourname/passkeeper
|
||||
cd passkeeper
|
||||
python -m venv .venv
|
||||
.venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 2. Configure environment
|
||||
copy .env.example .env
|
||||
```
|
||||
|
||||
Edit `.env`:
|
||||
|
||||
```env
|
||||
FLASK_ENV=development
|
||||
SECRET_KEY=your-secret-key
|
||||
JWT_SECRET_KEY=your-jwt-secret
|
||||
MYSQL_HOST=localhost
|
||||
MYSQL_USER=passkeeper
|
||||
MYSQL_PASSWORD=yourpassword
|
||||
MYSQL_DB=passkeeper
|
||||
TOTP_ENCRYPTION_KEY=64-char-hex-string # python -c "import secrets; print(secrets.token_hex(32))"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 3. Create database
|
||||
mysql -u root -p -e "CREATE DATABASE passkeeper CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
mysql -u root -p -e "CREATE USER 'passkeeper'@'localhost' IDENTIFIED BY 'yourpassword';"
|
||||
mysql -u root -p -e "GRANT ALL PRIVILEGES ON passkeeper.* TO 'passkeeper'@'localhost'; FLUSH PRIVILEGES;"
|
||||
|
||||
# 4. Create tables
|
||||
python reset_db.py
|
||||
|
||||
# 5. Run
|
||||
python run.py
|
||||
```
|
||||
|
||||
Open `http://localhost:5000`.
|
||||
|
||||
### Load the Extension (Chrome)
|
||||
|
||||
1. Open `chrome://extensions/`
|
||||
2. Enable **Developer mode** (top-right toggle)
|
||||
3. Click **Load unpacked** → select the `extension/` folder
|
||||
4. Reload the extension after any JS/CSS changes
|
||||
|
||||
Content script logs appear in the **page's** DevTools console. Background service worker logs are at `chrome://extensions/` → PassKeeper → "Service Worker".
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### 1. Server dependencies
|
||||
|
||||
```bash
|
||||
sudo apt update && sudo apt install python3.12 python3.12-venv mysql-server nginx \
|
||||
certbot python3-certbot-nginx redis-server
|
||||
```
|
||||
|
||||
### 2. Application setup
|
||||
|
||||
```bash
|
||||
cd /var/www/passkeeper
|
||||
python3.12 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
cp .env.example .env # set production values
|
||||
flask db upgrade # run all migrations
|
||||
```
|
||||
|
||||
### 3. systemd service
|
||||
|
||||
Copy `scripts/passkeeper.service` to `/etc/systemd/system/passkeeper.service`, then:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now passkeeper
|
||||
journalctl -xeu passkeeper.service
|
||||
```
|
||||
|
||||
Key flags in the service unit: `--preload` (single app import, lower memory), `WatchdogSec=60s`, `Restart=on-failure`.
|
||||
|
||||
### 4. Nginx
|
||||
|
||||
Copy `scripts/passkeeper-nginx.conf` to `/etc/nginx/sites-available/passkeeper`, update `server_name`, then:
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/passkeeper /etc/nginx/sites-enabled/
|
||||
sudo certbot --nginx -d yourdomain.com
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
The Nginx config uses an `upstream` block with `proxy_next_upstream` for zero-downtime Gunicorn restarts, dual rate-limit zones (auth endpoints and general API), and `Cache-Control: immutable` for static assets.
|
||||
|
||||
### 5. Schema migrations (future updates)
|
||||
|
||||
```bash
|
||||
flask db upgrade
|
||||
sudo systemctl reload passkeeper
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Example |
|
||||
|---|---|---|
|
||||
| `SECRET_KEY` | Flask session secret | 64-char random hex |
|
||||
| `JWT_SECRET_KEY` | JWT signing secret | 64-char random hex |
|
||||
| `MYSQL_HOST` | MySQL host | `localhost` |
|
||||
| `MYSQL_USER` | MySQL username | `passkeeper` |
|
||||
| `MYSQL_PASSWORD` | MySQL password | — |
|
||||
| `MYSQL_DB` | Database name | `passkeeper` |
|
||||
| `TOTP_ENCRYPTION_KEY` | Server-side AES key for TOTP secrets | 64-char hex |
|
||||
| `RATELIMIT_STORAGE_URI` | Redis URI for rate limiting | `redis://127.0.0.1:6379/0` |
|
||||
| `CORS_ORIGINS` | Allowed CORS origins | `https://yourdomain.com` |
|
||||
|
||||
Generate secrets:
|
||||
|
||||
```bash
|
||||
python -c "import secrets; print(secrets.token_hex(32))"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
passkeeper/
|
||||
├── app/ # Flask application
|
||||
│ ├── models/ # SQLAlchemy models
|
||||
│ ├── routes/ # API blueprints (auth, vault, folders, sharing, emergency)
|
||||
│ ├── services/ # Auth service (Argon2id, JWT, TOTP encryption)
|
||||
│ ├── static/js/ # Client-side crypto + vault UI
|
||||
│ └── templates/ # Jinja2 HTML templates
|
||||
├── extension/ # Chrome extension (Manifest V3)
|
||||
│ ├── popup/ # Popup UI (HTML + CSS + JS)
|
||||
│ ├── content/ # Content script (form detection, autofill icon, dropdown)
|
||||
│ ├── shared/ # Shared crypto (vault key, encryptName/decryptName)
|
||||
│ ├── bridge/ # SSO bridge (web app ↔ extension session sync)
|
||||
│ └── background.js # Service worker (badges, message relay, idle lock)
|
||||
├── migrations/ # Alembic migration scripts
|
||||
├── scripts/ # Nginx config, systemd unit, backup scripts
|
||||
├── reset_db.py # Dev-only: drop + recreate all tables
|
||||
├── requirements.txt
|
||||
├── run.py # Development server entry point
|
||||
└── wsgi.py # Gunicorn entry point
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Overview
|
||||
|
||||
All vault and folder endpoints require `Authorization: Bearer <access_token>`.
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|---|---|---|
|
||||
| POST | `/api/auth/register` | Create account |
|
||||
| POST | `/api/auth/login` | Authenticate; returns tokens or MFA challenge |
|
||||
| POST | `/api/auth/mfa/verify` | Complete MFA step |
|
||||
| POST | `/api/auth/refresh` | Rotate refresh token |
|
||||
| POST | `/api/auth/logout` | Blacklist both tokens |
|
||||
| GET | `/api/vault` | List all encrypted vault items |
|
||||
| POST | `/api/vault` | Create item (`enc_data`, `iv`, `enc_name`, `iv_name`) |
|
||||
| PUT | `/api/vault/<id>` | Update item |
|
||||
| DELETE | `/api/vault/<id>` | Delete item |
|
||||
| GET | `/api/folders` | List folders |
|
||||
| POST | `/api/sharing` | Share item (ECDH re-encryption) |
|
||||
| POST | `/api/emergency` | Create emergency access grant |
|
||||
| POST | `/api/auth/change-password` | Atomic vault re-encryption on password change |
|
||||
| POST | `/api/auth/recover` | Account recovery (one-time use) |
|
||||
|
||||
---
|
||||
|
||||
## Backup
|
||||
|
||||
Automated MySQL backups with 30-day retention:
|
||||
|
||||
```bash
|
||||
# Install cron job
|
||||
sudo cp scripts/passkeeper-logrotate /etc/logrotate.d/passkeeper
|
||||
crontab scripts/backup.cron
|
||||
```
|
||||
|
||||
Manual backup:
|
||||
|
||||
```bash
|
||||
bash scripts/backup_db.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
MIT — see `LICENSE`.
|
||||
Reference in New Issue
Block a user