-
Notifications
You must be signed in to change notification settings - Fork 0
Security
This guide covers security considerations and best practices for the Redmine Webhook Plugin.
- HTTPS Requirements
- Authentication Methods
- IP Whitelisting
- Rate Limiting
- Data Protection
- Security Auditing
- Compliance
Risk of HTTP:
- Data transmitted in clear text
- Man-in-the-middle attacks
- No verification of endpoint authenticity
- Credentials exposed in logs
The plugin validates webhook URLs:
✅ https://hooks.slack.com/services/... (Accepted)
❌ http://hooks.slack.com/services/... (Rejected with error)
# 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.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 |
# Add API key to webhook URL
https://hooks.example.com/webhook?api_key=YOUR_SECRET_KEY- ✅ Simple to implement
- ✅ Easy to rotate keys
- ✅ No code changes needed in plugin
- ❌ Key exposed in URLs
- ❌ URLs may be cached in logs
- ❌ Key visible in proxy/server logs
- ❌ Difficult to revoke if leaked
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: 8f4b2e3d1c3a67898be2dbd5f8f3a4fPlugin Side (Future Feature):
- Generate secret key:
openssl rand -hex 32 - Add to Redmine settings (custom field)
- Plugin signs payload with HMAC-SHA256
- 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- ✅ Strong security
- ✅ Key not exposed in URL
- ✅ Request body tamper detection
- ✅ Timestamp replay attack prevention
- ❌ More complex to implement
- ❌ Requires external service changes
- ❌ Secret key management needed
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)# 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- ✅ Industry standard
- ✅ Easy revocation
- ✅ Token scope control
- ❌ Requires OAuth flow setup
- ❌ More infrastructure
- ❌ Token refresh needed
Restrict outbound webhook traffic to specific IP addresses:
Benefits:
- ✅ Prevents data exfiltration
- ✅ Controls which endpoints can receive webhooks
- ✅ Reduces attack surface
# Check outbound IP
curl ifconfig.me
# OR check with network team
# Output: Your public IP is 203.0.113.1Slack:
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"}# 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 whitelistedProtect 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# Rate limit webhook dispatching per endpoint
# Implementation in: app/services/webhook/dispatcher.rb
MAX_PER_MINUTE = 10What'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
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
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_3DESWebhook 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 environmentsEnable 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}"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 |
If security incident is detected:
-
Contain:
- Disable affected endpoints
- Enable global pause
- Block suspicious IPs
-
Investigate:
- Review logs for time range
- Identify affected data
- Determine root cause
-
Remediate:
- Rotate exposed credentials
- Update configurations
- Patch vulnerabilities
-
Communicate:
- Document incident
- Notify affected users
- Implement additional monitoring
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 dataAccess 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
Requirements:
- ✅ No credit card data in webhook payloads
- ✅ HTTPS/TLS encryption
- ✅ Strong authentication (HMAC recommended)
- ✅ Regular security reviews
Before deploying to production, ensure:
- 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
- Appropriate payload mode selected
- Sensitive data minimization
- No credentials in webhook URLs (unless HMAC)
- Secrets stored securely (environment variables)
- Security logging enabled
- Anomaly detection configured
- Alert on suspicious activity
- Regular security reviews scheduled
- Database access restricted
- File permissions set correctly (755 for dirs, 644 for files)
- Regular backups of Redmine
- Redmine updates applied promptly
If you discover a security vulnerability:
- Do NOT create public issue initially
- Email privately to: security@example.com
- Include detailed information:
- Vulnerability type
- Affected versions
- Steps to reproduce
- Proof of concept
- Await security team guidance
- OWASP Webhook Security
- Curl Security Guide
- Transport Layer Security
- Home - Documentation index
- Troubleshooting - Common issues guide
Last Updated: 2026-02-03
Plugin Version: 1.0.0-RC1