Moko Consulting

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 audit before each release
  • Run npm audit for TypeScript projects
  • Run govulncheck for 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() and Path::clean()
  • Set restrictive permissions on uploaded files (644)
  • Protect directories with .htaccess (Deny from all) and index.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'