Open-source software for Joomla, Gitea, and web platforms. Home of MokoSuite, MokoGitea, and MokoCLI.
Tennessee
standards/security
Security Standards
Secure coding practices based on OWASP Top 10, applied to the MokoConsulting stack.
SQL Injection Prevention
Always use prepared statements. Never concatenate user input into SQL.
// CORRECT — Joomla query builder
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__mokosuitebackup_profiles'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $profileId, ParameterType::INTEGER);
// WRONG — string concatenation
$query = "SELECT * FROM #__mokosuitebackup_profiles WHERE id = " . $_GET['id'];
Cross-Site Scripting (XSS)
Escape all output displayed in HTML:
// CORRECT — escape output
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
echo Text::_('COM_MOKOSUITEBACKUP_TITLE');
// In Joomla templates
<?php echo $this->escape($item->title); ?>
// WRONG — raw output
echo $userInput;
echo $_GET['search'];
For HTML content that must preserve formatting, use Joomla's OutputFilter::stripImages() and InputFilter with a whitelist.
Cross-Site Request Forgery (CSRF)
All state-changing operations must verify CSRF tokens:
// In controller actions
public function save()
{
$this->checkToken(); // Throws on invalid token
// ... process form
}
Forms must include the token:
<form action="..." method="post">
<?php echo HTMLHelper::_('form.token'); ?>
<!-- form fields -->
</form>
Authentication and Authorization
- Always check ACL before data access:
if (!$user->authorise('core.edit', 'com_mokosuitebackup')) {
throw new NotAllowed(Text::_('JERROR_ALERTNOAUTHOR'), 403);
}
- API endpoints must verify Bearer token authentication
- Never trust client-side authorization checks alone
- Session tokens must be regenerated on privilege escalation
Input Validation
Validate all input at system boundaries:
$input = Factory::getApplication()->getInput();
$id = $input->getInt('id', 0); // Cast to integer
$title = $input->getString('title', ''); // Sanitize string
$type = $input->getCmd('type', 'full'); // Alphanumeric only
- Whitelist valid values for enums/options
- Validate file uploads: extension whitelist, MIME type check, size limit
- Reject unexpected input rather than trying to sanitize it
Sensitive Data
- No secrets in source code — use environment variables or config files
- No tokens in URLs — use headers (Authorization, DOLAPIKEY)
- No credentials in logs — redact sensitive fields
- Encrypt at rest — AES-256 for backup archives, bcrypt for passwords
- HTTPS only — all API communication over TLS
Dependency Management
- Run
composer auditbefore each release - Run
npm auditfor TypeScript projects - Run
govulncheckfor Go projects - Pin dependency versions in
composer.lock/package-lock.json - Review new dependencies for license compatibility and maintenance status
- Update dependencies monthly; critical CVEs within 48 hours
File Operations
- Validate file paths to prevent directory traversal
- Use Joomla's
Path::check()andPath::clean() - Set restrictive permissions on uploaded files (644)
- Protect directories with
.htaccess(Deny from all) andindex.html - Never serve uploaded files with their original filename in the URL
Error Handling
- Never expose stack traces, SQL queries, or internal paths to users
- Log detailed errors server-side; show generic messages to users
- Use Joomla's error handling (
enqueueMessage,Log::add) - Return appropriate HTTP status codes from API endpoints
Security Headers
Templates should set:
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'
Pages