OTT Stream Score includes built-in security features that are automatically applied:
Setup automatically sets secure permissions:
.db_bootstrap-chmod 0600(database credentials).installed-chmod 0600(installation marker)/playlists/-chmod 0700(temporary playlist storage during import processing)
- Session-based authentication with 30-minute timeout
- Password hashing with bcrypt
- CSRF protection on all forms
- Rate limiting: 5 failed attempts = 15 minute lockout
- Session regeneration on login (prevents session fixation)
- Secure session cookies (httpOnly, sameSite)
- All user inputs sanitized
- Prepared statements for database queries (prevents SQL injection)
- XSS protection via
htmlspecialchars()on all output
- Failed login attempts logged to database
- Rate limiting by IP address and username
- Account lockout after repeated failures
- Last login timestamp tracking
Add to your .htaccess file:
# Deny access to dotfiles
<FilesMatch "^\.">
Order allow,deny
Deny from all
</FilesMatch>
# Specifically protect sensitive files
<Files ".db_bootstrap">
Order allow,deny
Deny from all
</Files>
<Files ".installed">
Order allow,deny
Deny from all
</Files>
<Files "php_errors.log">
Order allow,deny
Deny from all
</Files>
<Files "epg_cron.log">
Order allow,deny
Deny from all
</Files>
<Files "epg_cron_errors.log">
Order allow,deny
Deny from all
</Files>For Nginx, add to your site configuration:
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ ^/(\.db_bootstrap|\.installed|php_errors\.log|epg_cron\.log|epg_cron_errors\.log) {
deny all;
}Best practice: Move .db_bootstrap outside your web-accessible directory.
# Move file
mv .db_bootstrap /home/user/private/.db_bootstrap
# Update db.php (line 31)
$bootstrap_file = '/home/user/private/.db_bootstrap';For maximum security, use environment variables instead of .db_bootstrap:
- Set environment variables in your web server config
- Modify
db.phpto read from$_ENVorgetenv()
Example (Apache):
SetEnv DB_HOST "localhost"
SetEnv DB_NAME "your_database"
SetEnv DB_USER "your_user"
SetEnv DB_PASS "your_password"Built-in protections for /playlists directory:
- Directory permissions:
0700(owner only) index.phpblocks directory browsing- Files auto-deleted after successful import
- Only exists during upload → import workflow
Best practices:
- ✅ Use the built-in upload system (auto-cleanup)
- ✅ Complete imports promptly (don't leave playlists on server)
- ❌ Never manually store playlists in web-accessible directories
- ❌ Don't upload playlists and leave them sitting for days
Additional hardening (optional):
# Apache: Add to .htaccess
<Directory "/playlists">
Require all denied
</Directory># Nginx: Add to site config
location /playlists/ {
deny all;
}Security considerations:
- Trust your source: Only use EPG URLs from trusted providers
- File size: EPG files can be 100+ MB compressed, ensure sufficient disk space
- XML parsing: Malformed XML could cause processing failures or memory issues
- Decompression: gzip/zip files are automatically decompressed, consuming additional disk space
- Cron access: EPG cron runs with file system write access
Best practices:
- ✅ Use HTTPS URLs for EPG sources when available
- ✅ Monitor disk space usage (EPG files can grow large)
- ✅ Verify EPG source is legitimate before configuring
- ✅ Review EPG logs after initial sync (
epg_cron.log) - ❌ Don't use unknown or untrusted EPG sources
- ❌ Don't ignore EPG sync failures (check
epg_cron_errors.log)
Monitor EPG disk usage:
# Check EPG database size
mysql -u user -p -e "SELECT table_name, ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)' FROM information_schema.TABLES WHERE table_schema = 'your_database' AND table_name = 'epg_data';"
# Check temp file space during processing
du -sh /tmp/Always use HTTPS in production:
- Prevents credential interception
- Protects session cookies
- Required for
securecookie flag
- Use strong, unique database passwords
- Limit database user permissions (don't use root)
- Grant only required permissions:
SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER - Use separate database user for this application
# Application files
chmod 644 *.php
chmod 755 .
# Secure sensitive files
chmod 600 .db_bootstrap
chmod 600 .installed
# Protect log files
chmod 600 php_errors.log
chmod 600 epg_cron.log
chmod 600 epg_cron_errors.log- Keep PHP updated
- Update MySQL/MariaDB
- Pull latest application updates:
git pull origin main - Monitor security advisories
Edit auth.php:
const SESSION_TIMEOUT = 1800; // 30 minutes (default)
const SESSION_TIMEOUT = 3600; // 60 minutes
const SESSION_TIMEOUT = 7200; // 2 hoursEdit auth.php:
const MAX_LOGIN_ATTEMPTS = 5; // Max failed attempts
const LOCKOUT_TIME = 900; // 15 minutes lockout
const ATTEMPT_WINDOW = 900; // Window to count attemptsCurrent: Minimum 8 characters
To enforce stronger passwords, edit validation in admin.php and setup.php:
if (strlen($password) < 12) {
$error = 'Password must be at least 12 characters';
}
// Add complexity requirements
if (!preg_match('/[A-Z]/', $password) || !preg_match('/[0-9]/', $password)) {
$error = 'Password must contain uppercase and numbers';
}-- Recent failed logins
SELECT username, ip_address, attempted_at, COUNT(*) as attempts
FROM login_attempts
WHERE success = 0
AND attempted_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY username, ip_address
ORDER BY attempts DESC;
-- Currently locked out
SELECT ip_address, username, COUNT(*) as attempts, MAX(attempted_at) as last_attempt
FROM login_attempts
WHERE success = 0
AND attempted_at > DATE_SUB(NOW(), INTERVAL 15 MINUTE)
GROUP BY ip_address, username
HAVING attempts >= 5;# Application errors
tail -f php_errors.log
# EPG sync logs
tail -f epg_cron.log
tail -f epg_cron_errors.log- Multiple failed logins from same IP
- Login attempts for non-existent users
- Unusual access times
- Database connection failures
- EPG sync failures or repeated errors
- Abnormal disk space consumption
- Change password immediately (Admin → Account)
- Review login attempts:
SELECT * FROM login_attempts
WHERE username = 'your_username'
ORDER BY attempted_at DESC
LIMIT 50;- Clear all sessions (logout and delete session files)
- Check for unauthorized changes in database
- Create new database user with different password
- Update credentials (Admin → Database)
- Revoke old user permissions:
DROP USER 'old_user'@'localhost';- Review database audit logs for unauthorized access
- Restore from backup
- Scan for malware:
grep -r "eval(base64" .
grep -r "system(" .- Check file modification times:
find . -name "*.php" -mtime -7- Review web server access logs
- Change all passwords
- HTTPS enabled with valid SSL certificate
-
.db_bootstrapprotected (chmod 0600 or moved outside web root) - Strong database password (16+ characters)
- Database user has minimal permissions
-
.htaccessor nginx rules protecting dotfiles and log files -
php_errors.lognot web-accessible -
epg_cron.logandepg_cron_errors.lognot web-accessible - Playlist files in private directories
- EPG URL from trusted source
- Error display disabled in production (
display_errors = 0) - Regular backups configured
- Monitoring enabled for failed login attempts
- Firewall configured (allow only HTTP/HTTPS/SSH)
- SSH key authentication (disable password login)
- Server timezone matches application timezone
- Sufficient disk space for EPG decompression (500MB+ free recommended)
- Review login attempts weekly
- Monitor EPG sync status weekly
- Check disk space usage monthly
- Update application monthly
- Update server packages monthly
- Review EPG logs for errors monthly
- Test backups quarterly
- Review user accounts quarterly
- Audit file permissions quarterly
- Verify EPG source still trusted quarterly
If you discover a security vulnerability:
- Do NOT open a public GitHub issue
- Email security details to repository maintainers
- Include:
- Description of vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if available)
Allow 48-72 hours for initial response.