Overview
Wecode handles user-submitted code execution, making security critical. This guide covers essential security configurations to protect your server and user data.
Required Reading: Implementing these security measures is not optional. Failure to secure your Wecode installation can lead to server compromise, data breaches, and unauthorized access.
Critical Security Measures
Move Sensitive Folders Outside Public Directory
HIGHEST PRIORITY: The assignments and tester folders contain user-submitted code and test cases. They must be moved outside the web-accessible directory to prevent direct access and potential code execution vulnerabilities.
Why This Matters
- assignments/: Contains all student-submitted code files
- tester/: Contains test cases and grading scripts
- If accessible via web, attackers could:
- Download submitted code (privacy breach)
- Execute malicious scripts
- Modify test cases
- Access sensitive information
Implementation Steps
- Create Secure Storage Location
# Create directory outside web root
sudo mkdir -p /var/wecode-data
sudo mkdir -p /var/wecode-data/assignments
sudo mkdir -p /var/wecode-data/tester
- Move Existing Folders
# If folders already exist with data
sudo mv /var/www/wecode/assignments/* /var/wecode-data/assignments/
sudo mv /var/www/wecode/tester/* /var/wecode-data/tester/
# Remove old folders
sudo rm -rf /var/www/wecode/assignments
sudo rm -rf /var/www/wecode/tester
- Set Proper Permissions
# Set ownership to web server user
sudo chown -R www-data:www-data /var/wecode-data
# Set directory permissions (rwxrwxr-x)
sudo chmod -R 775 /var/wecode-data
# Ensure PHP can read/write
sudo chmod -R g+w /var/wecode-data
- Update Wecode Configuration
- Log in to Wecode as admin
- Navigate to Settings page
- Update paths:
- Assignments Path:
/var/wecode-data/assignments
- Tester Path:
/var/wecode-data/tester
- Save and verify functionality
- Verify Configuration
# Test that folders are not web-accessible
curl http://your-domain.com/assignments/
# Should return 404 or 403
# Test that PHP can write
sudo -u www-data touch /var/wecode-data/assignments/test.txt
# Should succeed
Example Directory Structure
/var/www/
├── wecode/ # Application directory
│ ├── app/
│ ├── config/
│ ├── public/ # Only this is web-accessible
│ │ └── index.php
│ └── .env
│
└── html/ # Web root (if using separate public dir)
└── public -> /var/www/wecode/public
/var/wecode-data/ # NOT web-accessible
├── assignments/ # Student submissions
└── tester/ # Test cases
File Permissions
Application Directory Permissions
# Set ownership
sudo chown -R www-data:www-data /var/www/wecode
# Directories: 755 (rwxr-xr-x)
sudo find /var/www/wecode -type d -exec chmod 755 {} \;
# Files: 644 (rw-r--r--)
sudo find /var/www/wecode -type f -exec chmod 644 {} \;
# Storage and cache: 775 (rwxrwxr-x)
sudo chmod -R 775 /var/www/wecode/storage
sudo chmod -R 775 /var/www/wecode/bootstrap/cache
Environment File Protection
The .env file contains sensitive credentials including database passwords, API keys, and encryption keys.
# Restrict .env to owner read/write only
sudo chmod 600 /var/www/wecode/.env
sudo chown www-data:www-data /var/www/wecode/.env
# Verify
ls -la /var/www/wecode/.env
# Should show: -rw------- 1 www-data www-data
Storage Permissions
# Ensure web server can write to storage
sudo chown -R www-data:www-data /var/www/wecode/storage
sudo chmod -R 775 /var/www/wecode/storage
# Subdirectories
sudo chmod -R 775 /var/www/wecode/storage/logs
sudo chmod -R 775 /var/www/wecode/storage/framework
sudo chmod -R 775 /var/www/wecode/storage/app
Docker Security
User Permissions for Docker
The web server user needs Docker access to execute submitted code. Configure carefully to avoid privilege escalation.
# Add web server user to docker group
sudo usermod -aG docker www-data
# Verify
groups www-data
# Should include 'docker'
# Restart web server
sudo systemctl restart nginx # or apache2
Docker Socket Security
# Set proper socket permissions
sudo chmod 660 /var/run/docker.sock
sudo chown root:docker /var/run/docker.sock
Container Resource Limits
Prevent resource exhaustion from malicious submissions:
// In Wecode settings or code execution configuration
$dockerLimits = [
'--memory' => '512m', // Max 512MB RAM
'--memory-swap' => '512m', // No swap
'--cpus' => '1.0', // Max 1 CPU core
'--pids-limit' => '100', // Max 100 processes
'--network' => 'none', // No network access
'--read-only' => true, // Read-only filesystem
'--security-opt' => 'no-new-privileges',
];
Docker Network Isolation
# Create isolated network for code execution
docker network create --internal wecode-exec
# Verify network has no external access
docker network inspect wecode-exec | grep Internal
# Should show: "Internal": true
Database Security
Database User Permissions
-- Create dedicated database user with minimal privileges
CREATE USER 'wecode'@'localhost' IDENTIFIED BY 'strong_password_here';
-- Grant only necessary permissions
GRANT SELECT, INSERT, UPDATE, DELETE ON wecode_db.* TO 'wecode'@'localhost';
-- Do NOT grant:
-- GRANT ALL PRIVILEGES - too permissive
-- GRANT FILE - allows reading/writing files
-- GRANT SUPER - allows configuration changes
FLUSH PRIVILEGES;
Database Connection Security
# In .env file
DB_HOST=localhost # Not 0.0.0.0 or %
DB_CONNECTION=mysql
DB_PORT=3306
Disable Remote Database Access
# Edit MySQL/MariaDB config
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
# Set bind address to localhost only
bind-address = 127.0.0.1
# Restart database
sudo systemctl restart mariadb
Regular Security Updates
# Update database server
sudo apt update
sudo apt upgrade mariadb-server
# Check for security advisories
sudo apt list --upgradable | grep mariadb
Environment Configuration Security
Production Environment Settings
# .env file - PRODUCTION settings
APP_ENV=production
APP_DEBUG=false # NEVER true in production
APP_KEY=base64:... # Generate with: php artisan key:generate
# Session security
SESSION_SECURE_COOKIE=true # Requires HTTPS
SESSION_SAME_SITE=strict
# Disable dangerous functions
LOG_LEVEL=error # Don't log debug info
Disable Debug Mode
CRITICAL: APP_DEBUG=true in production exposes sensitive information including database credentials, file paths, and stack traces.
# Verify debug is disabled
grep APP_DEBUG /var/www/wecode/.env
# Must show: APP_DEBUG=false
# PHP configuration
sudo nano /etc/php/8.4/fpm/php.ini
# Set these values:
expose_php = Off
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
# Restart PHP-FPM
sudo systemctl restart php8.4-fpm
Web Server Security
server {
# ... other configuration ...
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
# Disable directory listing
autoindex off;
# Hide Nginx version
server_tokens off;
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Deny access to sensitive files
location ~ \.(env|log|git)$ {
deny all;
}
}
# .htaccess or VirtualHost configuration
<IfModule mod_headers.c>
Header set X-Frame-Options "SAMEORIGIN"
Header set X-Content-Type-Options "nosniff"
Header set X-XSS-Protection "1; mode=block"
Header set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
# Disable directory browsing
Options -Indexes
# Hide server info
ServerTokens Prod
ServerSignature Off
# Protect .env file
<FilesMatch "^\.env">
Require all denied
</FilesMatch>
SSL/TLS Configuration
# Install Certbot for Let's Encrypt
sudo apt install certbot python3-certbot-nginx
# Obtain SSL certificate
sudo certbot --nginx -d judge.example.com
# Auto-renewal
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
PHP Security Configuration
php.ini Hardening
; /etc/php/8.4/fpm/php.ini
; Disable dangerous functions
disable_functions = exec,passthru,system,proc_open,popen
; Note: shell_exec is REQUIRED by Wecode, so we keep it enabled
; But ensure only trusted users can submit code
; Resource limits
max_execution_time = 30
max_input_time = 60
memory_limit = 256M
post_max_size = 20M
upload_max_filesize = 10M
; File upload security
file_uploads = On
upload_tmp_dir = /tmp
; Session security
session.cookie_httponly = 1
session.cookie_secure = 1 ; Requires HTTPS
session.use_strict_mode = 1
session.cookie_samesite = Strict
; Hide PHP version
expose_php = Off
; Error handling
display_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
Shell Execution Security
Wecode requires shell_exec() to function. Ensure only authorized users can access code execution features.
// Verify shell_exec is enabled and working
if (!function_exists('shell_exec')) {
die('shell_exec is disabled. Wecode requires this function.');
}
// Test execution
$output = shell_exec('php -v');
if (empty($output)) {
die('PHP execution via shell_exec is not working.');
}
User Access Security
Role-Based Access Control
- Admin: Full system access
- Head Instructor: Course management
- Instructor: Assignment creation, grading
- Student: Submit code only
Ensure roles are properly configured in Wecode settings.
Password Policies
// Enforce in Wecode or via Laravel configuration
// config/auth.php or validation rules
'password' => [
'min:8', // Minimum 8 characters
'regex:/[a-z]/', // At least one lowercase
'regex:/[A-Z]/', // At least one uppercase
'regex:/[0-9]/', // At least one number
'regex:/[@$!%*?&]/', // At least one special char
]
Account Security
# Disable default admin accounts after creating your own
php artisan db:seed --class=remove_default_admin
# Or manually via SQL
mysql -u root -p wecode_db
DELETE FROM users WHERE username = 'truonganpn';
Firewall Configuration
UFW (Ubuntu)
# Enable firewall
sudo ufw enable
# Allow SSH
sudo ufw allow 22/tcp
# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Deny everything else by default
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Check status
sudo ufw status verbose
Fail2Ban for Brute Force Protection
# Install Fail2Ban
sudo apt install fail2ban
# Create Wecode jail
sudo nano /etc/fail2ban/jail.d/wecode.conf
[wecode-auth]
enabled = true
port = http,https
filter = wecode-auth
logpath = /var/www/wecode/storage/logs/laravel.log
maxretry = 5
bantime = 3600
Moss Integration Security
Protect Moss Credentials
# Store Moss user ID in .env, not in code
MOSS_USER_ID=123456789
# Restrict .env permissions
sudo chmod 600 /var/www/wecode/.env
Network Access for Moss
# Moss requires outbound access to moss.stanford.edu
# Ensure firewall allows HTTPS outbound
sudo ufw allow out 443/tcp
Security Checklist
Use this checklist before deploying to production:
Monitoring and Auditing
Log Monitoring
# Monitor Laravel logs
tail -f /var/www/wecode/storage/logs/laravel.log
# Monitor web server logs
tail -f /var/log/nginx/error.log
# Monitor system logs
sudo tail -f /var/log/syslog
Security Auditing
# Check for suspicious files
sudo find /var/www/wecode -type f -name "*.php" -mtime -1
# Check for world-writable files
sudo find /var/www/wecode -type f -perm 0777
# Check failed login attempts
sudo grep "Failed password" /var/log/auth.log
Regular Maintenance
# Weekly: Update system packages
sudo apt update && sudo apt upgrade
# Weekly: Review logs for suspicious activity
sudo grep -i "error\|warning\|failed" /var/www/wecode/storage/logs/laravel.log
# Monthly: Review user accounts
php artisan tinker
>>> User::where('last_login', '<', now()->subDays(90))->get();
# Monthly: Review file permissions
sudo find /var/www/wecode -type f -perm 0777 -ls
Next Steps