Skip to main content

Overview

Grupo Mecsa CMS implements comprehensive security measures to protect sensitive data and ensure only authorized staff can access administrative functions.

Security Architecture

The CMS uses a multi-layered security approach:
  1. Session-based Authentication - Secure token storage in PHP sessions
  2. Role-based Access Control (RBAC) - Fine-grained permissions by user role
  3. Supabase Row Level Security - Database-level access control
  4. Security Audit Logging - Track unauthorized access attempts

Access Control

Security Check Implementation

Every protected page must include config/security_check.php to enforce authentication and authorization
Location: config/security_check.php The security check runs on every page load and performs:
  • Session validation
  • Token verification
  • Role authorization
  • Access denial for non-staff users

Usage in Pages

Include at the top of every protected PHP file:
<?php
require_once __DIR__ . '/config/security_check.php';
// Rest of your page code
?>

Staff Roles

The system recognizes four staff roles:
RoleVariablePermissions
Admin$isAdminFull system access, user management
Comercial$isComercialClient and project management
Proyecto$isProyectoProject management and tracking
RRHH$isRRHHEmployee and department management
Users without any staff role are immediately blocked and logged out, even if they have valid credentials

Role Detection Logic

$user = $_SESSION['user'] ?? null;

// Check Admin role
$isAdmin = !empty($user['admin']) && 
           ($user['admin'] === true || $user['admin'] === 't' || 
            $user['admin'] === 1 || $user['admin'] === '1');

// Check other roles similarly
$isComercial = !empty($user['comercial']) && (/* boolean checks */);
$isProyecto = !empty($user['proyecto']) && (/* boolean checks */);
$isRRHH = !empty($user['rrhh']) && (/* boolean checks */);

// User must have at least one role
$isStaff = $isAdmin || $isComercial || $isProyecto || $isRRHH;
The system handles multiple boolean representations (true, ‘t’, 1, ‘1’) to accommodate different data sources and database types

Session Management

Session Initialization

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}
The security check safely starts sessions only if not already active.

Required Session Variables

VariableTypeDescription
$_SESSION['token']stringSupabase authentication JWT token
$_SESSION['user']arrayUser profile with role flags
$_SESSION['uid']stringUnique user identifier

Token Validation

If no token is found in the session, users are immediately redirected to login (except from login.php itself)
if (!isset($_SESSION['token'])) {
    if (basename($_SERVER['PHP_SELF']) !== 'login.php') {
        header("Location: ../../login.php?error=session_expired");
        exit;
    }
}

Session Expiration

Users are redirected with specific error codes:
  • session_expired - No valid token found
  • unauthorized_staff_only - Valid user but no staff role
  • unauthorized - Generic access denial

API Key Protection

Development vs Production Keys

CRITICAL: Never commit API keys to version control!

Development Environment

Store keys in local.supabase.php (add to .gitignore):
local.supabase.php
<?php
$supabase_url = 'https://xxxxx.supabase.co';
$supabase_key = 'your-development-anon-key';
$supabase_service_role = 'your-development-service-role-key';
?>

Production Environment

Use environment variables:
# Set in server environment
export SUPABASE_URL="https://production.supabase.co"
export SUPABASE_KEY="production-anon-key"
export SUPABASE_SERVICE_ROLE="production-service-role-key"
Or configure in .htaccess:
SetEnv SUPABASE_URL "https://production.supabase.co"
SetEnv SUPABASE_KEY "production-anon-key"
SetEnv SUPABASE_SERVICE_ROLE "production-service-role-key"

Key Types and Security Levels

Understand the difference between key types to use them appropriately

Anon/Public Key ($supabase_key)

  • Security Level: Standard
  • RLS: Enforced (respects Row Level Security policies)
  • Usage: Normal CRUD operations, user-level authentication
  • Exposure: Can be exposed in client-side code

Service Role Key ($supabase_service_role)

  • Security Level: Administrative
  • RLS: Bypassed (full database access)
  • Usage: User management, password resets, admin operations
  • Exposure: Must NEVER be exposed to clients
The service role key grants unrestricted database access. Only use it server-side for administrative operations.

Security Audit Logging

Audit Log Location

Unauthorized access attempts are logged to:
security_audit.log

Log Format

[2026-03-05 14:23:45] CMS_BLOCK: Page=dashboard.php UID=user123
[2026-03-05 14:24:12] CMS_BLOCK: Page=usuarios.php UID=N/A
Each entry includes:
  • Timestamp
  • Event type (CMS_BLOCK)
  • Page that was blocked
  • User ID (if available)

Log Implementation

@file_put_contents(
    __DIR__ . '/../../security_audit.log',
    date('[Y-m-d H:i:s] ') . 
    "CMS_BLOCK: Page=" . basename($_SERVER['PHP_SELF']) . 
    " UID=" . ($_SESSION['uid'] ?? 'N/A') . "\n",
    FILE_APPEND
);
Regularly review the audit log to detect potential security issues or brute force attempts

Best Practices

1. Credential Management

1

Never Commit Secrets

Add sensitive files to .gitignore:
local.supabase.php
.env
*.key
security_audit.log
2

Use Environment Variables

Store production credentials in environment variables, not in code
3

Rotate Keys Regularly

Periodically regenerate API keys in the Supabase dashboard
4

Separate Dev and Prod

Use different Supabase projects for development and production

2. Session Security

// Configure secure session settings
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);  // HTTPS only
ini_set('session.cookie_samesite', 'Strict');
Always use HTTPS in production to protect session tokens from interception

3. Password Security

  • Enforce strong password requirements
  • Use Supabase’s built-in password hashing
  • Implement password reset flows with time-limited tokens
  • Support multi-factor authentication (MFA)

4. Database Security

Leverage Supabase Row Level Security (RLS) for defense in depth
Example RLS policy:
-- Users can only see their own data
CREATE POLICY "Users view own data"
ON cms.users
FOR SELECT
USING (auth.uid() = id);

-- Admins can see everything
CREATE POLICY "Admins view all"
ON cms.users
FOR SELECT
USING (auth.jwt() ->> 'admin' = 'true');

5. Input Validation

// Sanitize user input
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
$name = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8');

// Validate before processing
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    die('Invalid email');
}

6. CSRF Protection

Implement CSRF tokens for state-changing operations:
// Generate token
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

// Validate on form submission
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    die('CSRF validation failed');
}

Monitoring and Maintenance

Regular Tasks

1

Review Audit Logs

Check security_audit.log weekly for suspicious activity
2

Monitor Sessions

Track active sessions and unusual login patterns
3

Update Dependencies

Keep Composer packages updated:
composer update
4

Security Audits

Perform periodic security reviews of code and configurations

Security Incident Response

If you detect unauthorized access:
  1. Immediate Actions:
    • Revoke compromised API keys in Supabase
    • Force logout all sessions
    • Change admin passwords
  2. Investigation:
    • Review audit logs
    • Check database access logs in Supabase
    • Identify the attack vector
  3. Remediation:
    • Patch the vulnerability
    • Update security policies
    • Notify affected users if necessary

Security Checklist

Ensure all items are checked before deploying to production
  • All sensitive files added to .gitignore
  • Production API keys stored in environment variables
  • HTTPS enabled on production server
  • Secure session settings configured
  • security_check.php included on all protected pages
  • Row Level Security policies configured in Supabase
  • Regular audit log reviews scheduled
  • Strong password policy enforced
  • CSRF protection implemented on forms
  • Input validation on all user inputs
  • Error messages don’t reveal sensitive information
  • Security headers configured (HSTS, CSP, X-Frame-Options)

Next Steps

Supabase Configuration

Configure database connection and credentials

Deployment

Deploy securely to production