no-self-signed-certs
Disallow rejectUnauthorized false in TLS options
Detects disabling of TLS certificate validation via rejectUnauthorized: false or NODE_TLS_REJECT_UNAUTHORIZED=0. This rule is part of eslint-plugin-node-security and provides LLM-optimized error messages with fix suggestions.
π¨ Security rule | π‘ Provides suggestions | β οΈ Set to error in recommended
Quick Summary
| Aspect | Details |
|---|---|
| CWE Reference | CWE-295 (Certificate Issues) |
| Severity | Critical (security vulnerability) |
| Auto-Fix | π‘ Suggests enabling validation |
| Category | Security |
| ESLint MCP | β Optimized for ESLint MCP integration |
| Best For | Node.js applications making HTTPS requests |
Vulnerability and Risk
Vulnerability: Setting rejectUnauthorized: false disables TLS certificate validation entirely. This means the application will accept ANY certificate, including self-signed, expired, and certificates for different domains.
Risk: An attacker performing a man-in-the-middle (MITM) attack can intercept all traffic, read sensitive data, and inject malicious responses. The application cannot distinguish between the legitimate server and the attacker.
Rule Details
This rule detects:
rejectUnauthorized: falsein TLS/HTTPS optionsprocess.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
Why This Matters
| Risk | Impact | Solution |
|---|---|---|
| π΅οΈ MITM Attack | Attacker can read/modify all traffic | Enable certificate validation |
| π Data Interception | Credentials, tokens, PII exposed | Use proper CA certificates |
| π Compliance | Fails PCI-DSS, HIPAA, SOC2 requirements | Never disable in production |
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
allowInTests | boolean | false | Allow in test files |
{
rules: {
'node-security/no-self-signed-certs': ['error', {
allowInTests: true // Only for mocked services in tests
}]
}
}Examples
β Incorrect
import https from 'https';
import axios from 'axios';
// Disabling certificate validation - CRITICAL risk
https.request({
hostname: 'api.example.com',
rejectUnauthorized: false, // β Allows MITM attacks
});
// Same issue with axios
axios.get('https://api.example.com', {
httpsAgent: new https.Agent({
rejectUnauthorized: false, // β Also vulnerable
}),
});
// Global disable - affects ALL requests
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // β Never do thisβ Correct
import https from 'https';
import fs from 'fs';
// Default behavior - validation enabled
https.request({
hostname: 'api.example.com',
// rejectUnauthorized defaults to true
});
// Use custom CA for internal services
const ca = fs.readFileSync('/path/to/internal-ca.pem');
https.request({
hostname: 'internal-api.company.com',
ca: ca, // Custom CA for internal PKI
rejectUnauthorized: true, // β
Explicitly enabled
});
// Pin specific certificate for high-security
const https = require('https');
const tls = require('tls');
const options = {
hostname: 'api.example.com',
checkServerIdentity: (host, cert) => {
// Custom certificate verification
if (cert.fingerprint !== expectedFingerprint) {
return new Error('Certificate mismatch');
}
},
};Security Impact
| Vulnerability | CWE | OWASP | CVSS | Impact |
|---|---|---|---|---|
| Certificate Validation | 295 | A07:2021 | 9.1 Critical | Complete MITM capability |
| Improper Authentication | 287 | A07:2021 | 8.1 High | Server impersonation |
Common Scenarios Where This Appears
Development with Self-Signed Certs
// β Wrong: Disabling validation
rejectUnauthorized: false;
// β
Right: Add dev CA to trust store
ca: fs.readFileSync('./dev-ca.pem');Internal Services
// β Wrong: Disabling for internal APIs
rejectUnauthorized: false;
// β
Right: Use internal PKI with proper CA
ca: fs.readFileSync('/etc/pki/internal-ca.pem');Legacy Systems
// β Wrong: Disabling for old systems
rejectUnauthorized: false;
// β
Right: Update certificates or use TLS proxyMigration Guide
Phase 1: Discovery
{
rules: {
'node-security/no-self-signed-certs': 'warn'
}
}Phase 2: Fix
- For internal services: Set up proper internal CA
- For development: Use mkcert for trusted local certs
- For production: Use Let's Encrypt or commercial CA
Phase 3: Enforcement
{
rules: {
'node-security/no-self-signed-certs': 'error'
}
}Related Rules
require-secure-credential-storage- Secure credential storageno-hardcoded-credentials- Detect hardcoded credentials
Known False Negatives
The rule matches a literal false on a rejectUnauthorized key, and a static
'0' / `0` / 0 written to process.env.NODE_TLS_REJECT_UNAUTHORIZED
(or globalThis.process.envβ¦). Where the object or the assignment sits does
not matter β an agent built inside an axios interceptor is reported like any
other.
Values that are not a literal
Why: The rule does not follow data flow.
// β NOT DETECTED
const insecure = false;
new https.Agent({ rejectUnauthorized: insecure });The env switch passed to a child process
Why: Only an assignment to process.env is checked, not an env object
handed to spawn/fork.
// β NOT DETECTED
spawn('node', ['job.js'], {
env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED: '0' },
});Mitigation: Audit all HTTP client configurations.
Further Reading
- OWASP TLS Cheat Sheet - TLS best practices
- CWE-295: Improper Certificate Validation - Official CWE entry
- mkcert - Local trusted development certificates
βοΈ Options
| Option | Type | Default | Description |
|---|---|---|---|
allowInTests | boolean | false | Allow in test files |
Use this rule
npm install -D oxlint-plugin-node-security{ "jsPlugins": ["oxlint-plugin-node-security"], "rules": { "node-security/no-self-signed-certs": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-node-security'], rules: { 'node-security/no-self-signed-certs': 'error', }, },});oxlint-plugin-node-security re-exports eslint-plugin-node-security/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it β or follow the AI-code-security benchmarks behind these rules.