n8n-security-testing
Credential exposure detection, OAuth flow validation, API key management testing, and data sanitization verification for n8n workflows. Use when validating n8n workflow security.
What this skill does
# n8n Security Testing
<default_to_action>
When testing n8n security:
1. SCAN for credential exposure in workflows
2. VERIFY encryption of sensitive data
3. TEST OAuth token handling
4. CHECK for insecure data transmission
5. VALIDATE input sanitization
**Quick Security Checklist:**
- No credentials in workflow JSON
- No credentials in execution logs
- OAuth tokens properly encrypted
- API keys not in version control
- Webhook authentication enabled
- Input data sanitized
**Critical Success Factors:**
- Scan all workflow exports
- Test credential rotation
- Verify encryption at rest
- Check audit logging
</default_to_action>
## Quick Reference Card
### Security Risk Areas
| Area | Risk Level | Testing Focus |
|------|------------|---------------|
| **Credential Storage** | Critical | Encryption, exposure |
| **Webhook Security** | High | Authentication, validation |
| **Expression Injection** | High | Input sanitization |
| **Data Leakage** | Medium | Logging, error messages |
| **OAuth Flows** | Medium | Token handling, refresh |
### Credential Types
| Type | Exposure Risk | Rotation |
|------|---------------|----------|
| **API Keys** | High if exposed | Manual |
| **OAuth Tokens** | Medium (short-lived) | Automatic |
| **Passwords** | Critical | Manual |
| **Webhooks** | Medium | Generate new |
---
## Credential Security Testing
### Scan for Exposed Credentials
```typescript
// Scan workflow JSON for credential exposure
async function scanForExposedCredentials(workflowId: string): Promise<CredentialScanResult> {
const workflow = await getWorkflow(workflowId);
const workflowJson = JSON.stringify(workflow, null, 2);
const sensitivePatterns = [
// API Keys
{ name: 'Generic API Key', pattern: /api[_-]?key["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi },
{ name: 'AWS Access Key', pattern: /AKIA[0-9A-Z]{16}/g },
{ name: 'AWS Secret Key', pattern: /[a-zA-Z0-9/+=]{40}/g },
// Tokens
{ name: 'Bearer Token', pattern: /bearer\s+[a-zA-Z0-9_-]{20,}/gi },
{ name: 'JWT Token', pattern: /eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/g },
{ name: 'Slack Token', pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}/g },
// Passwords
{ name: 'Password Field', pattern: /"password":\s*"[^"]+"/gi },
{ name: 'Secret Field', pattern: /"secret":\s*"[^"]+"/gi },
// OAuth
{ name: 'Client Secret', pattern: /client[_-]?secret["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi },
{ name: 'Refresh Token', pattern: /refresh[_-]?token["\s:=]+["']?([a-zA-Z0-9_-]{20,})["']?/gi }
];
const findings: CredentialFinding[] = [];
for (const pattern of sensitivePatterns) {
const matches = workflowJson.match(pattern.pattern);
if (matches) {
for (const match of matches) {
findings.push({
type: pattern.name,
location: findLocationInWorkflow(workflow, match),
severity: 'CRITICAL',
recommendation: `Remove ${pattern.name} from workflow. Use n8n credentials instead.`
});
}
}
}
return {
workflowId,
scanned: true,
findingsCount: findings.length,
findings,
secure: findings.length === 0
};
}
```
### Verify Credential Encryption
```typescript
// Verify credentials are encrypted at rest
async function verifyCredentialEncryption(credentialId: string): Promise<EncryptionResult> {
// Get credential metadata (not the actual credential)
const credential = await getCredentialMetadata(credentialId);
// Check if credential data is encrypted
const encryptionChecks = {
// Check if stored data looks encrypted (not plain text)
isEncrypted: !isPlainText(credential.data),
// Check encryption algorithm
algorithm: credential.encryptionAlgorithm || 'unknown',
// Check key derivation
keyDerivation: credential.keyDerivation || 'unknown',
// Check if using instance encryption key
instanceEncryption: credential.useInstanceKey || false
};
return {
credentialId,
credentialName: credential.name,
credentialType: credential.type,
encryption: encryptionChecks,
secure: encryptionChecks.isEncrypted && encryptionChecks.algorithm !== 'unknown',
recommendations: generateEncryptionRecommendations(encryptionChecks)
};
}
// Check if data appears to be plain text
function isPlainText(data: string): boolean {
// Plain text credentials often have recognizable patterns
const plainTextPatterns = [
/^[a-zA-Z0-9_-]+$/, // Simple alphanumeric
/^sk-[a-zA-Z0-9]+$/, // API key format
/^Bearer\s/, // Bearer token
];
return plainTextPatterns.some(p => p.test(data));
}
```
### Test Credential Rotation
```typescript
// Test credential rotation process
async function testCredentialRotation(credentialId: string): Promise<RotationTestResult> {
const credential = await getCredentialMetadata(credentialId);
const rotationTests = {
// Check if credential has rotation metadata
hasRotationSchedule: !!credential.rotationSchedule,
lastRotated: credential.lastRotatedAt,
rotationDue: isRotationDue(credential),
// Test OAuth token refresh
oauthRefresh: credential.type.includes('oauth')
? await testOAuthRefresh(credentialId)
: null,
// Check credential age
credentialAge: calculateAge(credential.createdAt),
isStale: calculateAge(credential.createdAt) > 90 // 90 days
};
return {
credentialId,
rotationTests,
recommendations: generateRotationRecommendations(rotationTests)
};
}
// Test OAuth token refresh
async function testOAuthRefresh(credentialId: string): Promise<OAuthRefreshResult> {
try {
// Trigger refresh
const refreshed = await refreshCredential(credentialId);
return {
success: true,
newExpiry: refreshed.expiresAt,
refreshedAt: new Date()
};
} catch (error) {
return {
success: false,
error: error.message,
recommendation: 'Re-authorize OAuth connection'
};
}
}
```
---
## Webhook Security Testing
### Authentication Testing
```typescript
// Test webhook authentication enforcement
async function testWebhookAuthentication(webhookUrl: string): Promise<WebhookAuthResult> {
const authTests = [
// No authentication
{
name: 'No Auth',
headers: {},
expectedStatus: 401
},
// Invalid Basic Auth
{
name: 'Invalid Basic Auth',
headers: { 'Authorization': 'Basic aW52YWxpZDppbnZhbGlk' },
expectedStatus: 401
},
// Invalid Bearer Token
{
name: 'Invalid Bearer',
headers: { 'Authorization': 'Bearer invalid-token-12345' },
expectedStatus: 401
},
// Invalid Header Auth
{
name: 'Invalid Header Auth',
headers: { 'X-API-Key': 'invalid-key' },
expectedStatus: 401
}
];
const results: AuthTestResult[] = [];
for (const test of authTests) {
const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...test.headers
},
body: '{}'
});
results.push({
test: test.name,
status: response.status,
passed: response.status === test.expectedStatus,
actualStatus: response.status,
expectedStatus: test.expectedStatus
});
}
// Check if webhook has ANY auth
const noAuthResponse = results.find(r => r.test === 'No Auth');
const webhookHasAuth = noAuthResponse?.status === 401;
return {
webhookUrl,
hasAuthentication: webhookHasAuth,
testResults: results,
allTestsPassed: results.every(r => r.passed),
recommendation: !webhookHasAuth
? 'CRITICAL: Enable authentication on webhook'
: null
};
}
```
### Input Validation Testing
```typescript
// Test webhook input validation
async function testWebhookInputValidation(webhookUrl: string): Promise<InputValidationResult> {
const maliciousPayloads = [
// XSS attempts
{
name: 'XSS Script Tag',
payload: { text: '<script>alRelated in n8n-testing
n8n-expression-testing
Includedn8n expression syntax validation, context-aware testing, common pitfalls detection, and performance optimization. Use when validating n8n expressions and data transformations.
n8n-integration-testing-patterns
IncludedAPI contract testing, authentication flows, rate limit handling, and error scenario coverage for n8n integrations with external services. Use when testing n8n node integrations.
n8n-trigger-testing-strategies
IncludedWebhook testing, schedule validation, event-driven triggers, and polling mechanism testing for n8n workflows. Use when testing how workflows are triggered.
n8n-workflow-testing-fundamentals
IncludedComprehensive n8n workflow testing including execution lifecycle, node connection patterns, data flow validation, and error handling strategies. Use when testing n8n workflow automation applications.