Skip to content

Security

guyinwonder168 edited this page Feb 7, 2026 · 1 revision

Security

This guide covers security considerations and best practices for the Redmine Webhook Plugin.

Table of Contents


HTTPS Requirements

⚠️ Critical: HTTPS is Mandatory

Risk of HTTP:

  • Data transmitted in clear text
  • Man-in-the-middle attacks
  • No verification of endpoint authenticity
  • Credentials exposed in logs

Enforcing HTTPS

1. Plugin Validation

The plugin validates webhook URLs:

✅ https://hooks.slack.com/services/... (Accepted)
❌ http://hooks.slack.com/services/... (Rejected with error)

2. SSL/TLS Certificate Validation

# Test HTTPS endpoint
curl -v https://hooks.example.com/webhook 2>&1 | grep -i ssl

# Should show:
# * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
# * Server certificate:
# *  subject: CN=hooks.example.com
# *  start date: Jan  1 00:00:00 2024 GMT
# *  expire date: Jan  1 00:00:00 2025 GMT
# *  issuer: C=US, O=Let's Encrypt
# *  SSL certificate verify ok.

3. Certificate Errors

Common Errors:

Error Meaning Solution
self signed certificate Not trusted Use CA-signed cert
certificate expired Old certificate Renew SSL cert
hostname mismatch Wrong domain Fix certificate domain
unable to get local issuer No CA bundle Update CA certificates

Authentication Methods

Option 1: API Key in URL

Implementation

# Add API key to webhook URL
https://hooks.example.com/webhook?api_key=YOUR_SECRET_KEY

Pros

  • ✅ Simple to implement
  • ✅ Easy to rotate keys
  • ✅ No code changes needed in plugin

Cons

  • ❌ Key exposed in URLs
  • ❌ URLs may be cached in logs
  • ❌ Key visible in proxy/server logs
  • ❌ Difficult to revoke if leaked

Best Practices

Rotate Keys Regularly:

  • Set expiration date (e.g., 90 days)
  • Use cryptographically strong keys (32+ characters)
  • Rotate after suspected leak

Generate Strong Key:

# Generate 32-character key
openssl rand -hex 32
# Output: 8f4b2e3d1c3a67898be2dbd5f8f3a4f

Option 2: HMAC Signature (Recommended)

Implementation

Plugin Side (Future Feature):

  1. Generate secret key: openssl rand -hex 32
  2. Add to Redmine settings (custom field)
  3. Plugin signs payload with HMAC-SHA256
  4. Signature included in payload

External Side Verification:

import hmac
import json

secret = "your_secret_key_here"
payload = json.loads(request.body)

# Calculate expected signature
expected_sig = hmac.new(
  payload_bytes,
  secret.encode('utf-8'),
  hashlib.sha256
).hexdigest()

# Get signature from payload
received_sig = payload.get('signature')

if received_sig == expected_sig:
  print("Signature verified!")
  # Process webhook
  status = 200
else:
  print("Invalid signature!")
  # Reject webhook
  status = 401

Pros

  • ✅ Strong security
  • ✅ Key not exposed in URL
  • ✅ Request body tamper detection
  • ✅ Timestamp replay attack prevention

Cons

  • ❌ More complex to implement
  • ❌ Requires external service changes
  • ❌ Secret key management needed

Best Practices

Key Management:

  • Use environment variables for secrets
  • Never commit secrets to Git
  • Rotate keys quarterly
  • Use different keys for dev/staging/production

Signature Algorithm:

# Recommended: HMAC-SHA256
# Avoid: MD5 (broken), SHA1 (deprecated)

Option 3: OAuth 2.0 Bearer Token

Implementation

# External service provides OAuth token
token = "your_bearer_token_here"

curl -X POST https://hooks.example.com/webhook \
  -H "Authorization: Bearer ${token}" \
  -H "Content-Type: application/json" \
  -d @payload.json

Pros

  • ✅ Industry standard
  • ✅ Easy revocation
  • ✅ Token scope control

Cons

  • ❌ Requires OAuth flow setup
  • ❌ More infrastructure
  • ❌ Token refresh needed

IP Whitelisting

Why Whitelist?

Restrict outbound webhook traffic to specific IP addresses:

Benefits:

  • ✅ Prevents data exfiltration
  • ✅ Controls which endpoints can receive webhooks
  • ✅ Reduces attack surface

Configuration

1. Find Redmine Server IP

# Check outbound IP
curl ifconfig.me

# OR check with network team
# Output: Your public IP is 203.0.113.1

2. Configure External Service Whitelist

Slack:

Workspace Settings > Security > Whitelist
Add IP: 203.0.113.1

Microsoft Teams:

Teams Admin Center > Teams > Manage Teams
Add IP: 203.0.113.1

Custom API:

# Whitelist in your code
ALLOWED_IPS = ['203.0.113.1']

def check_ip(request):
    client_ip = request.remote_addr
    if client_ip not in ALLOWED_IPS:
        return 403, {"error": "IP not whitelisted"}

Verification

# Test from Redmine server
curl -X POST https://hooks.example.com/webhook -d '{"test": "data"}'

# If successful: IP is whitelisted
# If 403 Forbidden: IP not whitelisted

Rate Limiting

Implement on External Service

Protect your webhook endpoint from spam/duplicate events:

# External service example (Ruby/Sinatra)
require 'sinatra'
require 'sinatra/base'
require 'redis'

# Rate limit: 100 requests per minute per endpoint
rate_limit = RateLimiter.new(100, 1.minute)

before '/webhook' do
  endpoint_id = request.env['HTTP_X_ENDPOINT_ID']
  
  unless rate_limit.allowed?(request.ip, endpoint_id)
    status 429
    body "Too Many Requests"
    halt
  end
end

post '/webhook' do
  # Process webhook
  # Increment counter in Redis
  rate_limit.increment(request.ip, endpoint_id)
  status 200
  { "status": "received" }.to_json
end

Configure Plugin (Future Feature)

# Rate limit webhook dispatching per endpoint
# Implementation in: app/services/webhook/dispatcher.rb

MAX_PER_MINUTE = 10

Data Protection

Sensitive Data in Payloads

What's Included:

  • Issue titles, descriptions
  • User names, emails
  • Project information
  • Time entries
  • Custom field values

What Should Be Protected:

  • Personal information (PII) - passwords, tokens
  • Financial data - exact amounts, account numbers
  • Health information - medical details
  • Confidential information - trade secrets

Data Minimization

Use appropriate payload mode:

For Notifications:

Use "Minimal" or "Standard" mode
Avoid: Full payload with all custom fields
Benefit: Less sensitive data transmitted

For Synchronization:

Use "Full" mode only when necessary
Document: Why full data is needed
Secure: Storage of webhook payloads

Data in Transit

HTTPS Encrypts:

  • ✅ TLS 1.2/1.3
  • ✅ Perfect Forward Secrecy
  • ✅ Authentication headers encrypted

Verify Configuration:

# Check for strong ciphers
openssl s_client -connect hooks.example.com:443 -cipher

# Should show: TLSv1.3, TLS_AES_256_GCM_SHA384
# Avoid: TLSv1.0, TLS_RSA_WITH_3DES

Data at Rest

Webhook Deliveries Table:

  • Stores full request/response bodies
  • May contain sensitive data

Protect:

-- Database access restrictions
GRANT SELECT, INSERT, UPDATE ON webhook_deliveries TO 'redmine_app_user';

-- Encryption at rest (if required)
-- Use database encryption for sensitive environments

Security Auditing

Audit Logs

Enable comprehensive logging for security events:

# Log all webhook activity
log.info "Webhook: user=#{User.current.login} action=webhook_sent endpoint_id=#{endpoint.id} resource_type=#{event.resource_type}"

Monitor for Anomalies

Suspicious Activity Patterns:

Pattern Detection Action
Sudden spike Unusual delivery volume Investigate endpoint
Failed attempts Many 401/403 errors Check auth
Different IP New source IP Verify whitelist
Large payloads Unusual data size Check configuration

Incident Response

If security incident is detected:

  1. Contain:

    • Disable affected endpoints
    • Enable global pause
    • Block suspicious IPs
  2. Investigate:

    • Review logs for time range
    • Identify affected data
    • Determine root cause
  3. Remediate:

    • Rotate exposed credentials
    • Update configurations
    • Patch vulnerabilities
  4. Communicate:

    • Document incident
    • Notify affected users
    • Implement additional monitoring

Compliance

GDPR Considerations

Data Subject Rights:

  • Users can request webhook delivery history
  • Ability to export personal data
  • Right to deletion

Implementation:

# Add to admin interface (future feature)
# User can: export their delivery records
# User can: purge their personal data

SOC 2 Compliance

Access Control:

  • ❌ Restrict who can configure webhooks (audit trail)
  • ✅ Log all configuration changes
  • ✅ Require authentication for sensitive actions

Encryption:

  • ✅ HTTPS for all endpoints
  • ✅ Consider payload encryption
  • ✅ Secure storage of webhook secrets

PCI DSS (if processing payments)

Requirements:

  • ✅ No credit card data in webhook payloads
  • ✅ HTTPS/TLS encryption
  • ✅ Strong authentication (HMAC recommended)
  • ✅ Regular security reviews

Security Checklist

Before deploying to production, ensure:

Endpoint Security

  • All webhook URLs use HTTPS
  • Valid SSL/TLS certificates
  • Authentication configured (API key, HMAC, or OAuth)
  • IP whitelisting implemented (if required)
  • Rate limiting on external service

Data Protection

  • Appropriate payload mode selected
  • Sensitive data minimization
  • No credentials in webhook URLs (unless HMAC)
  • Secrets stored securely (environment variables)

Monitoring & Auditing

  • Security logging enabled
  • Anomaly detection configured
  • Alert on suspicious activity
  • Regular security reviews scheduled

Operational Security

  • Database access restricted
  • File permissions set correctly (755 for dirs, 644 for files)
  • Regular backups of Redmine
  • Redmine updates applied promptly

Getting Help

Report Security Vulnerabilities

If you discover a security vulnerability:

  1. Do NOT create public issue initially
  2. Email privately to: security@example.com
  3. Include detailed information:
    • Vulnerability type
    • Affected versions
    • Steps to reproduce
    • Proof of concept
  4. Await security team guidance

Resources


Last Updated: 2026-02-03
Plugin Version: 1.0.0-RC1

Clone this wiki locally