feat: harden and modernize the file manager (v2.0.0)
Security hardening: - bcrypt password auth via password_hash()/password_verify(), no default creds - Session CSRF token verified with hash_equals() - Secure session cookies: HttpOnly, SameSite=Strict, Secure over HTTPS - Nonce-based Content-Security-Policy, frame/clickjack + nosniff headers - realpath() root containment + symlink exclusion to block traversal - Server-side upload size + MIME(finfo) + extension validation - Configurable IP allow/deny with CIDR, optional HTTPS enforcement - Fixed 500ms login delay to slow brute-force guessing Features (single PHP file, no CDN): - New Bootstrap 5 responsive UI with light/dark theme toggle - Upload (drag+drop), download-only links, delete, rename, copy/move, mkdir - Folder ZIP download, in-browser text editor (<=2MiB) - Multi-user, per-user read-only + per-user root directories - All frontend assets vendored locally under assets/ (349K) Docs: full config reference, password hash guide, Tor hidden-service guide.
This commit is contained in:
+234
@@ -0,0 +1,234 @@
|
||||
# Simple File Manager - Code Review & Improvement Recommendations
|
||||
|
||||
## Current State
|
||||
- **Fork**: `hoelee/simple-file-manager` (based on jcampbell1/simple-file-manager)
|
||||
- **Original Inspiration**: prasathmani/tinyfilemanager (v2.6, Nov 2024)
|
||||
- **Current File**: Single `index.php` (~20KB, 494 lines)
|
||||
- **Last Update**: 2040331 - "default sort ordering is directory first, then name"
|
||||
|
||||
---
|
||||
|
||||
## 🔴 CRITICAL Security Issues
|
||||
|
||||
### 1. **No Authentication by Default** (`$PASSWORD = ''`)
|
||||
- **Risk**: Anyone with URL access gets full file system access
|
||||
- **Fix**: Enforce password or disable by default with clear warning
|
||||
- **Reference**: TinyFM v2.6 has `$use_auth = true` with bcrypt password hashing
|
||||
|
||||
### 2. **Weak Path Traversal Protection**
|
||||
```php
|
||||
// Current: Only checks prefix, can be bypassed
|
||||
if(substr($tmp, 0,strlen($tmp_dir)) !== $tmp_dir) err(403,"Forbidden");
|
||||
```
|
||||
- **Issues**: Symlinks, case-insensitive FS, Unicode normalization bypasses
|
||||
- **Fix**: Use `realpath()` on both paths AFTER resolving symlinks
|
||||
|
||||
### 3. **XSRF Token in Cookie (Not HttpOnly)**
|
||||
```php
|
||||
setcookie('_sfm_xsrf', bin2hex(openssl_random_pseudo_bytes(16)));
|
||||
```
|
||||
- **Risk**: Accessible via JavaScript (XSS → CSRF bypass)
|
||||
- **Fix**: Add `HttpOnly; Secure; SameSite=Strict` flags
|
||||
|
||||
### 4. **Timing Attack on Password Check**
|
||||
```php
|
||||
sha1($t.$_POST['p']) === sha1($t.$PASSWORD) // String comparison leaks timing
|
||||
```
|
||||
- **Fix**: Use `hash_equals()` for constant-time comparison
|
||||
|
||||
### 5. **No File Type Validation on Upload (MIME Only)**
|
||||
```php
|
||||
foreach($disallowed_patterns as $pattern)
|
||||
if(fnmatch($pattern, $_FILES['file_data']['name'])) err(403,...);
|
||||
```
|
||||
- **Risk**: Extension-based only, easily bypassed (e.g., `shell.php.jpg`, polyglots)
|
||||
- **Fix**: Validate MIME type with `finfo_file()`, check magic bytes
|
||||
|
||||
### 6. **No Upload Size Limit Enforcement (Server-Side)**
|
||||
- Only client-side JS check (`MAX_UPLOAD_SIZE`)
|
||||
- **Fix**: Server-side check before `move_uploaded_file()`
|
||||
|
||||
### 7. **Information Disclosure in Errors**
|
||||
```php
|
||||
echo json_encode(['error' => ['code'=>intval($code), 'msg' => $msg]]);
|
||||
```
|
||||
- **Risk**: Stack traces, paths leaked in production
|
||||
- **Fix**: Generic error messages in production mode
|
||||
|
||||
---
|
||||
|
||||
## 🟠 HIGH Priority Improvements
|
||||
|
||||
### 8. **Outdated jQuery (1.8.2 from 2012!)**
|
||||
```html
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
|
||||
```
|
||||
- **Risks**: XSS vulnerabilities, no modern browser support, deprecated APIs
|
||||
- **Fix**: Update to jQuery 3.7+ or use vanilla JS (tinyfilemanager uses 3.6.1)
|
||||
|
||||
### 9. **No Content Security Policy (CSP)**
|
||||
- Inline scripts/styles, external CDN → XSS risk
|
||||
- **Fix**: Add CSP header, move inline JS to separate file or nonce
|
||||
|
||||
### 10. **No Rate Limiting / Brute Force Protection**
|
||||
- Password auth has no lockout, delay, or logging
|
||||
- **Fix**: Add exponential backoff, IP-based rate limiting
|
||||
|
||||
### 11. **Insecure Default Permissions**
|
||||
```php
|
||||
$allow_delete = true;
|
||||
$allow_upload = true;
|
||||
$allow_create_folder = true;
|
||||
$allow_direct_link = true;
|
||||
```
|
||||
- **Risk**: Dangerous defaults for public deployment
|
||||
- **Fix**: Secure defaults (all `false`), opt-in via config
|
||||
|
||||
### 12. **No HTTPS Enforcement**
|
||||
- Password sent in cleartext over HTTP
|
||||
- **Fix**: Check `$_SERVER['HTTPS']`, redirect or reject
|
||||
|
||||
---
|
||||
|
||||
## 🟡 MEDIUM Priority Improvements
|
||||
|
||||
### 13. **Missing Features from TinyFM v2.6**
|
||||
| Feature | TinyFM | Current |
|
||||
|---------|--------|---------|
|
||||
| File editing (ace.js) | ✅ | ❌ |
|
||||
| Syntax highlighting | ✅ | ❌ |
|
||||
| Multi-user with permissions | ✅ | ❌ |
|
||||
| Read-only users | ✅ | ❌ |
|
||||
| User-specific directories | ✅ | ❌ |
|
||||
| IP whitelist/blacklist | ✅ | ❌ |
|
||||
| Chunked uploads (large files) | ✅ | ❌ |
|
||||
| Online doc viewer (Google/MS) | ✅ | ❌ |
|
||||
| Dark/Light theme | ✅ | ❌ |
|
||||
| Multiple languages | ✅ | ❌ |
|
||||
| Config file override | ✅ | ❌ |
|
||||
| Hidden files toggle | ✅ | ❌ |
|
||||
| File permissions/owner display | ✅ | ❌ |
|
||||
| Search/filter | ✅ | ❌ |
|
||||
| Copy/move/rename | ✅ | ❌ |
|
||||
| Zip download | ✅ | ❌ |
|
||||
| Image preview | ✅ | ❌ |
|
||||
|
||||
### 14. **Code Quality Issues**
|
||||
- Mixed PHP/HTML (hard to maintain)
|
||||
- No PSR standards, inconsistent formatting
|
||||
- Global variables instead of dependency injection
|
||||
- No autoloading, no Composer support
|
||||
- Functions defined after usage (works but confusing)
|
||||
|
||||
### 15. **Accessibility Issues**
|
||||
- No ARIA labels, no keyboard navigation for upload area
|
||||
- Color-only indicators (drag-over state)
|
||||
- No focus management
|
||||
|
||||
### 16. **Mobile/Responsive Gaps**
|
||||
- Fixed width: `width:1024` (not responsive)
|
||||
- Touch targets too small
|
||||
- No viewport meta tag
|
||||
|
||||
---
|
||||
|
||||
## 🟢 LOW Priority / Nice to Have
|
||||
|
||||
### 17. **Modern PHP Practices**
|
||||
- Type hints, strict types, scalar/return types
|
||||
- PSR-12 coding standard
|
||||
- Dependency injection container
|
||||
- Unit tests (PHPUnit)
|
||||
|
||||
### 18. **Performance**
|
||||
- No caching headers for static assets
|
||||
- No gzip/compression hints
|
||||
- Scandir() on every request (no caching)
|
||||
|
||||
### 19. **Developer Experience**
|
||||
- No `.editorconfig`, no `phpstan`/`psalm` config
|
||||
- No GitHub Actions CI
|
||||
- No Dockerfile for testing
|
||||
|
||||
---
|
||||
|
||||
## 📋 Recommended Action Plan (Keeping Single File Constraint)
|
||||
|
||||
### Phase 1: Security Hardening (Week 1)
|
||||
1. ✅ Enforce password auth by default
|
||||
2. ✅ Fix path traversal with `realpath()`
|
||||
3. ✅ Add `HttpOnly; Secure; SameSite=Strict` to cookies
|
||||
4. ✅ Use `hash_equals()` for password check
|
||||
5. ✅ Server-side upload size validation
|
||||
6. ✅ MIME type validation with magic bytes
|
||||
7. ✅ Generic error messages (configurable debug mode)
|
||||
|
||||
### Phase 2: Modernization (Week 2)
|
||||
8. ✅ Update jQuery to 3.7.1 (or vanilla JS)
|
||||
9. ✅ Add CSP header
|
||||
10. ✅ Add rate limiting for auth
|
||||
11. ✅ Secure defaults (disable upload/delete by default)
|
||||
12. ✅ HTTPS enforcement check
|
||||
|
||||
### Phase 3: Feature Parity (Selective, Week 3)
|
||||
13. ✅ Add file editing (ace.js via CDN)
|
||||
14. ✅ Add syntax highlighting (highlight.js)
|
||||
15. ✅ Add dark/light theme toggle
|
||||
16. ✅ Add search/filter
|
||||
17. ✅ Add copy/move/rename
|
||||
18. ✅ Make responsive (viewport, flexbox)
|
||||
|
||||
### Phase 4: Polish (Week 4)
|
||||
19. ✅ Accessibility improvements
|
||||
20. ✅ Mobile UX
|
||||
21. ✅ Config file support (optional external `config.php`)
|
||||
22. ✅ PSR-12 formatting within single file
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Reference: TinyFM v2.6 Key Differences
|
||||
|
||||
| Aspect | TinyFM v2.6 | Current Fork |
|
||||
|--------|-------------|--------------|
|
||||
| Lines of code | ~5,700 | ~494 |
|
||||
| Auth system | Multi-user, bcrypt, readonly | Single password, sha1 |
|
||||
| File operations | Full CRUD + zip + edit | List, upload, delete, mkdir |
|
||||
| Security | IP rules, CSP, rate limit | Basic XSRF only |
|
||||
| UI | Bootstrap 5, DataTables, responsive | Custom CSS, jQuery 1.8 |
|
||||
| Extensibility | Config file, plugins | Hardcoded config only |
|
||||
| PHP requirement | 7.4+ | 5.6+ (implied) |
|
||||
|
||||
---
|
||||
|
||||
## 💡 Decision Point
|
||||
|
||||
**Option A**: Harden current minimal fork (keep ~500 lines)
|
||||
- Pros: Simple, fast, familiar
|
||||
- Cons: Missing many useful features, security debt
|
||||
|
||||
**Option B**: Adopt TinyFM v2.6 as base, customize
|
||||
- Pros: Battle-tested, feature-rich, active maintenance
|
||||
- Cons: Larger (~230KB), more complex
|
||||
|
||||
**Option C**: Hybrid - cherry-pick security + key features from TinyFM
|
||||
- Pros: Best of both worlds
|
||||
- Cons: More integration work
|
||||
|
||||
**Recommendation**: **Option C** - The current fork is too minimal for production use. Cherry-pick:
|
||||
1. All security fixes from TinyFM
|
||||
2. Auth system (bcrypt, multi-user)
|
||||
3. File editing + syntax highlighting
|
||||
4. Responsive UI (Bootstrap 5)
|
||||
5. Config file support
|
||||
|
||||
This keeps it ~single-file (with optional config.php) while gaining production-ready security and features.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Files to Reference from TinyFM v2.6
|
||||
- `tinyfilemanager.php` (main) - security patterns, auth, file ops
|
||||
- Config structure - for optional `config.php` support
|
||||
- CSS/JS CDN references - for modern deps
|
||||
- Chunked upload logic - for large file support
|
||||
|
||||
*Generated: 2026-08-19*
|
||||
@@ -1,35 +1,451 @@
|
||||
simple-file-manager
|
||||
===================
|
||||
# Simple File Manager
|
||||
|
||||
A Simple PHP file manager. The code is a single php file.
|
||||
A single-file PHP file manager — hardened, self-hosted, and CDN-free.
|
||||
|
||||
Just copy `index.php` to a folder on your webserver.
|
||||
Forked from [jcampbell1/simple-file-manager](https://github.com/jcampbell1/simple-file-manager) with security hardening and features inspired by [prasathmani/tinyfilemanager](https://github.com/prasathmani/tinyfilemanager) v2.6.
|
||||
|
||||
## Why it is good
|
||||
Maintained by **Hoelee Enterprise** — [hoelee.com](https://hoelee.com) · WhatsApp +6012-797 2969
|
||||
|
||||
- Single file, there are no images, or css folders.
|
||||
- Ajax based so it is fast, but doesn't break the back button
|
||||
- Allows drag and drop file uploads if the folder is writable by the webserver (`chmod 777 your/folder`)
|
||||
- Suits my aesthetics. More like Dropbox, and less like Windows Explorer
|
||||
- Works with Unicode file names
|
||||
- The interface is usable from an iPad
|
||||
- XSRF protection, and an optional password.
|
||||
---
|
||||
|
||||
## Do not allow uploads on the public web
|
||||
## Why it's good
|
||||
|
||||
If you allow uploads on the public web, it is only a matter of time before your server is hosting and serving very illegal content. Any of the following options will prevent this:
|
||||
- Don't make the folder writable by the webserver `chmod 775`
|
||||
- Set `$allow_upload = false`
|
||||
- Use a password `$PASSWORD = 'some password'`
|
||||
- Use a `.htaccess` file with Apache, or `auth_basic` for nginx
|
||||
- Only use this on a private network
|
||||
- **Single PHP file** (`index.php`) — all frontend JS/CSS stored locally in `assets/`, no CDN requests
|
||||
- **No external dependencies** at runtime — works on air-gapped networks and Tor hidden services
|
||||
- **Hardened security** — bcrypt auth, CSRF tokens, CSP headers, symlink protection, server-side upload validation
|
||||
- **Modern UI** — Bootstrap 5, responsive, light/dark theme toggle
|
||||
- **Full file operations** — upload, download, delete, rename, copy/move, mkdir, ZIP archive, in-browser file editor
|
||||
- **Multi-user support** — per-user passwords, read-only accounts, per-user directory isolation
|
||||
- **Tor-friendly** — no DNS lookups, no external fonts, no analytics, no CDN
|
||||
|
||||
HT: [@beardog108](https://github.com/beardog108)
|
||||
## Requirements
|
||||
|
||||
## Forks
|
||||
- PHP **7.4+** (8.x recommended)
|
||||
- PHP extensions: `fileinfo` (required), `zip` (optional, for ZIP downloads)
|
||||
- Web server: Apache, Nginx, OpenLiteSpeed, or PHP built-in server
|
||||
|
||||
- **Edit feature**. An extension of the initial project which lets you edit files and save them from the main php file. Works asynchronously with ajax requests. Link: [@diego95root](https://github.com/diego95root/File-manager-php)
|
||||
## Quick Start
|
||||
|
||||
## Screenshot
|
||||
### 1. Download
|
||||
|
||||

|
||||
```bash
|
||||
git clone https://github.com/hoelee/simple-file-manager.git
|
||||
cd simple-file-manager
|
||||
```
|
||||
|
||||
Or just copy `index.php` + the `assets/` folder to your webserver.
|
||||
|
||||
### 2. Generate a password hash
|
||||
|
||||
Before the app will run, you need at least one password hash. Generate one with PHP:
|
||||
|
||||
```bash
|
||||
php -r "echo password_hash('your-strong-password', PASSWORD_DEFAULT), PHP_EOL;"
|
||||
```
|
||||
|
||||
Output looks like:
|
||||
|
||||
```
|
||||
$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
|
||||
```
|
||||
|
||||
### 3. Configure
|
||||
|
||||
Open `index.php` and edit the **CONFIGURATION** section at the top:
|
||||
|
||||
```php
|
||||
$app_title = 'My File Manager';
|
||||
|
||||
// Directory users can manage. Use __DIR__ for the project folder,
|
||||
// or an absolute path like '/srv/uploads'.
|
||||
$root_path = __DIR__;
|
||||
|
||||
$use_auth = true;
|
||||
$auth_users = [
|
||||
'admin' => '$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy',
|
||||
];
|
||||
```
|
||||
|
||||
### 4. Serve
|
||||
|
||||
**PHP built-in server** (quick test):
|
||||
|
||||
```bash
|
||||
php -S 0.0.0.0:8080 -t /path/to/simple-file-manager
|
||||
```
|
||||
|
||||
Open `http://localhost:8080` and log in.
|
||||
|
||||
**Nginx:**
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name files.example.com;
|
||||
root /var/www/simple-file-manager;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
|
||||
# Prevent direct execution of any PHP file except index.php
|
||||
location ~ /assets/.*\.php$ {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Apache `.htaccess** (place in the project root):
|
||||
|
||||
```apache
|
||||
Options -Indexes
|
||||
AllowOverride None
|
||||
|
||||
<FilesMatch "^(?!index\.php$).+\.php$">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
```
|
||||
|
||||
**Docker:**
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name file-manager \
|
||||
-p 8080:80 \
|
||||
-v /path/to/files:/var/www/html/data \
|
||||
-v /path/to/simple-file-manager:/var/www/html/app \
|
||||
php:8.2-apache
|
||||
|
||||
# Then visit http://localhost:8080/app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
All settings are in the **CONFIGURATION** block at the top of `index.php`.
|
||||
|
||||
### Authentication
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `$use_auth` | `true` | Enable login. App refuses to run without a valid password hash. |
|
||||
| `$auth_users` | `[]` | Map of `'username' => 'bcrypt-hash'` |
|
||||
| `$readonly_users` | `[]` | Users who can browse/download but not modify |
|
||||
| `$user_directories` | `[]` | Per-user root directory, e.g. `['client-a' => '/srv/client-a']` |
|
||||
| `$block_placeholder_credentials` | `true` | Refuses to run if a placeholder hash is detected |
|
||||
|
||||
### Permissions
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `$allow_upload` | `true` | File upload |
|
||||
| `$allow_delete` | `true` | Delete files/folders |
|
||||
| `$allow_create_folder` | `true` | Create new folders |
|
||||
| `$allow_rename` | `true` | Rename files/folders |
|
||||
| `$allow_copy_move` | `true` | Copy or move between folders |
|
||||
| `$allow_edit` | `true` | In-browser text file editor (≤2 MiB) |
|
||||
| `$allow_archive_download` | `true` | Download folder as ZIP |
|
||||
| `$allow_direct_link` | `false` | Direct file links vs. forced download |
|
||||
|
||||
### Upload Policy
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `$max_upload_size_bytes` | `104857600` (100 MiB) | Per-file upload limit (also configure PHP/nginx) |
|
||||
| `$allowed_upload_extensions` | `[]` | Allowlist, e.g. `['jpg','png','pdf']`; empty = all non-blocked |
|
||||
| `$blocked_upload_extensions` | `[php, phtml, phar, ...]` | Blocklist of dangerous extensions |
|
||||
| `$allowed_upload_mime_types` | `[]` | MIME allowlist via `finfo`; empty = no check |
|
||||
|
||||
### Access Control
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `$ip_access_mode` | `'off'` | `'off'`, `'allow'` (allowlist), or `'deny'` (denylist) |
|
||||
| `$ip_rules` | `[]` | IPs or CIDRs, e.g. `['127.0.0.1', '192.168.1.0/24']` |
|
||||
| `$require_https` | `false` | Reject non-HTTPS requests |
|
||||
|
||||
### UI
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `$default_theme` | `'light'` | `'light'` or `'dark'` (user can toggle) |
|
||||
| `$default_timezone` | `'Asia/Kuala_Lumpur'` | PHP timezone |
|
||||
| `$show_hidden_files` | `false` | Show dotfiles in listing |
|
||||
| `$debug` | `false` | Show PHP errors (keep `false` in production) |
|
||||
|
||||
---
|
||||
|
||||
## User Password Hash Guide
|
||||
|
||||
This app uses **bcrypt** via PHP's `password_hash()`. Never store plaintext passwords.
|
||||
|
||||
### Generate a hash
|
||||
|
||||
**Command line:**
|
||||
|
||||
```bash
|
||||
php -r "echo password_hash('MySecretPass123!', PASSWORD_DEFAULT), PHP_EOL;"
|
||||
```
|
||||
|
||||
**Interactive (prompts for password, hidden input):**
|
||||
|
||||
```bash
|
||||
php -r "echo password_hash(readline('Password: '), PASSWORD_DEFAULT), PHP_EOL;"
|
||||
```
|
||||
|
||||
**PHP script** (`generate-hash.php`):
|
||||
|
||||
```php
|
||||
<?php
|
||||
echo "Enter password: ";
|
||||
system('stty -echo');
|
||||
$password = trim(fgets(STDIN));
|
||||
system('stty echo');
|
||||
echo "\n" . password_hash($password, PASSWORD_DEFAULT) . "\n";
|
||||
```
|
||||
|
||||
### Add a user
|
||||
|
||||
```php
|
||||
$auth_users = [
|
||||
'admin' => '$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy',
|
||||
'viewer' => '$2y$10$X5zP2k3vQ8mF1bN7cR4dT6hJ9lE0wA3sB5nC8oY2uZ1xD4eG7hI3a',
|
||||
];
|
||||
$readonly_users = ['viewer']; // viewer can browse but not modify
|
||||
```
|
||||
|
||||
### Multiple users with isolated directories
|
||||
|
||||
```php
|
||||
$auth_users = [
|
||||
'admin' => '$2y$10$...hash1...',
|
||||
'client-a' => '$2y$10$...hash2...',
|
||||
'client-b' => '$2y$10$...hash3...',
|
||||
];
|
||||
$user_directories = [
|
||||
'client-a' => '/srv/uploads/client-a',
|
||||
'client-b' => '/srv/uploads/client-b',
|
||||
];
|
||||
$readonly_users = ['client-b']; // client-b can only download
|
||||
```
|
||||
|
||||
### Tips
|
||||
|
||||
- Use a unique password per user
|
||||
- Bcrypt cost factor is 10 by default — fine for most servers
|
||||
- To rotate a password, just generate a new hash and replace the old one
|
||||
- The app auto-detects placeholder text (`REPLACE_WITH`) and refuses to start
|
||||
|
||||
---
|
||||
|
||||
## Hosting on Tor Hidden Service
|
||||
|
||||
This file manager is ideal for Tor because it makes **zero external requests** — no CDN, no Google Fonts, no analytics, no DNS lookups. All assets are served from the local `assets/` folder.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- A Tor relay or daemon installed on your server
|
||||
- A web server (Nginx recommended) running on localhost
|
||||
- PHP-FPM
|
||||
|
||||
### Step 1: Install Tor
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
|
||||
```bash
|
||||
sudo apt update && sudo apt install tor
|
||||
```
|
||||
|
||||
**Synology DSM (Docker):**
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
tor:
|
||||
image: dperson/torproxy
|
||||
container_name: tor
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:9050:9050" # SOCKS proxy
|
||||
volumes:
|
||||
- ./tor-data:/var/lib/tor
|
||||
```
|
||||
|
||||
### Step 2: Configure the hidden service
|
||||
|
||||
Edit `/etc/tor/torrc` (Linux) or your Tor config:
|
||||
|
||||
```ini
|
||||
HiddenServiceDir /var/lib/tor/file-manager/
|
||||
HiddenServicePort 80 127.0.0.1:8765
|
||||
```
|
||||
|
||||
Restart Tor:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart tor
|
||||
```
|
||||
|
||||
Get your `.onion` address:
|
||||
|
||||
```bash
|
||||
sudo cat /var/lib/tor/file-manager/hostname
|
||||
# Output: abcdefghijklmnop234567.onion
|
||||
```
|
||||
|
||||
### Step 3: Configure Nginx for localhost only
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 127.0.0.1:8765;
|
||||
server_name _;
|
||||
|
||||
root /var/www/simple-file-manager;
|
||||
index index.php;
|
||||
|
||||
# No HTTPS needed — Tor provides transport encryption
|
||||
# But enforce that requests come through Tor (localhost only)
|
||||
allow 127.0.0.1;
|
||||
deny all;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
|
||||
# Block direct PHP execution in assets/
|
||||
location ~ /assets/.*\.php$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Static asset caching
|
||||
location ~* \.(js|css|png|jpg|gif|svg|ico)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Configure for Tor
|
||||
|
||||
In `index.php`:
|
||||
|
||||
```php
|
||||
$app_title = 'Secure Files';
|
||||
|
||||
// Tor provides end-to-end encryption, so HTTPS is not required
|
||||
$require_https = false;
|
||||
|
||||
// Bind to the managed directory (NOT the project folder)
|
||||
$root_path = '/srv/tor-files';
|
||||
|
||||
// Strong auth — mandatory for .onion services
|
||||
$use_auth = true;
|
||||
$auth_users = [
|
||||
'admin' => '$2y$10$your-bcrypt-hash-here',
|
||||
];
|
||||
|
||||
// Lock down uploads on a public .onion
|
||||
$allow_upload = false; // or true if you trust your users
|
||||
$allow_delete = true;
|
||||
$allow_direct_link = false; // force downloads only
|
||||
|
||||
// Optional: restrict to known clients (Tor exit uses localhost)
|
||||
$ip_access_mode = 'allow';
|
||||
$ip_rules = ['127.0.0.1'];
|
||||
```
|
||||
|
||||
### Step 5: Access
|
||||
|
||||
```bash
|
||||
# Install Tor Browser, then visit:
|
||||
http://abcdefghijklmnop234567.onion
|
||||
```
|
||||
|
||||
### Tor Hardening Checklist
|
||||
|
||||
- [x] No CDN/external requests (assets are local)
|
||||
- [x] No DNS lookups (`.onion` resolves through Tor)
|
||||
- [x] No analytics or third-party scripts
|
||||
- [x] Session cookies are `SameSite=Strict`, `HttpOnly`
|
||||
- [x] CSP prevents loading external resources
|
||||
- [x] Nginx listens on `127.0.0.1` only (not `0.0.0.0`)
|
||||
- [x] Strong bcrypt password configured
|
||||
- [x] `$allow_upload` disabled or restricted
|
||||
- [x] `$allow_direct_link = false` (no hotlinking from clearnet)
|
||||
- [ ] Consider disabling `$allow_edit` if not needed
|
||||
- [ ] Set `$show_hidden_files = false`
|
||||
- [ ] Regularly check Tor logs for relay health
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
### Do not allow uploads on the public web
|
||||
|
||||
If you allow uploads on the public web, it is only a matter of time before your server is hosting and serving illegal content. Prevent this with:
|
||||
|
||||
- Set `$allow_upload = false`
|
||||
- Use a strong password (`$use_auth = true`)
|
||||
- Restrict by IP (`$ip_access_mode = 'allow'`)
|
||||
- Use `.htaccess` (Apache) or `auth_basic` (Nginx)
|
||||
- Only expose on a private network or Tor hidden service
|
||||
|
||||
### Security features in this fork
|
||||
|
||||
| Feature | Status |
|
||||
|---------|--------|
|
||||
| bcrypt password hashing | ✅ `password_hash()` / `password_verify()` |
|
||||
| CSRF protection | ✅ Session token + `hash_equals()` |
|
||||
| Session cookie hardening | ✅ `HttpOnly`, `SameSite=Strict`, `Secure` |
|
||||
| Content Security Policy | ✅ Nonce-based CSP header |
|
||||
| Path traversal protection | ✅ `realpath()` + root containment check |
|
||||
| Symlink protection | ✅ Symlinks hidden & excluded from all operations |
|
||||
| Server-side upload size limit | ✅ Enforced before `move_uploaded_file()` |
|
||||
| Upload extension/MIME validation | ✅ Blocklist + optional allowlists |
|
||||
| IP allow/deny with CIDR | ✅ Configurable |
|
||||
| HTTPS enforcement | ✅ Optional via `$require_https` |
|
||||
| X-Frame-Options | ✅ `DENY` (clickjacking protection) |
|
||||
| X-Content-Type-Options | ✅ `nosniff` |
|
||||
| Referrer-Policy | ✅ `same-origin` |
|
||||
| Brute-force delay | ✅ 500ms fixed delay on login attempt |
|
||||
| No default credentials | ✅ Refuses to run without a real hash |
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
- Original: [jcampbell1/simple-file-manager](https://github.com/jcampbell1/simple-file-manager) (MIT)
|
||||
- Enhancements inspired by: [prasathmani/tinyfilemanager](https://github.com/prasathmani/tinyfilemanager) v2.6
|
||||
- Frontend: [Bootstrap 5.3.3](https://getbootstrap.com/), [jQuery 3.7.1](https://jquery.com/), [Font Awesome 4.7](https://fontawesome.com/v4/), [Ace Editor](https://ace.c9.io/), [Highlight.js](https://highlightjs.org/)
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE)
|
||||
|
||||
---
|
||||
|
||||
## About
|
||||
|
||||
**Hoelee Enterprise** provides website design for SME, program & web app development, web hosting, and marketing.
|
||||
|
||||
- 🌐 [hoelee.com](https://hoelee.com) — Portfolio
|
||||
- 📱 WhatsApp: +6012-797 2969
|
||||
- ✉️ Email: me@hoelee.com
|
||||
|
||||
Vendored
+6
File diff suppressed because one or more lines are too long
Vendored
+4
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-comment,.hljs-quote,.hljs-variable{color:green}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#00f}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:#a31515}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:#2b91af}.hljs-doctag{color:grey}.hljs-attr{color:red}.hljs-bullet,.hljs-link,.hljs-symbol{color:#00b0e8}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
Vendored
+7
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user