SSL/TLS: Securing Data in Transit with Certificates and Encryption
In this tutorial, you will learn about SSL/TLS: Securing Data in Transit with Certificates and Encryption. We cover key concepts, practical examples, and best practices to help you master this topic.
SSL/TLS encrypts data transmitted between clients and servers, preventing eavesdropping, tampering, and man-in-the-middle attacks. For backend services, TLS is essential not just for external APIs but also for internal service-to-service communication.
flowchart TB
Client[Client] -->|TCP Handshake| Server
Server -->|Certificate with Public Key| Client
Client -->|Verify Certificate| CA[Certificate Authority]
CA -->|Trusted| Client
Client -->|Generate Pre-Master Secret| Server
Server -->|Decrypt with Private Key| Session[Session Keys Established]
Session -->|Encrypted Data Exchange| Client
Session -->|Encrypted Data Exchange| Server
subgraph TLS 1.3
Fast[1-RTT Handshake]
FS[Forward Secrecy]
AEAD[AEAD Ciphers Only]
end
What You'll Learn
- TLS handshake and certificate chain validation
- Configuring TLS 1.3 with strong cipher suites
- Mutual TLS (mTLS) for service-to-service authentication
- Certificate management with Let's Encrypt
Why It Matters
Without TLS, all data transmitted between client and server is in plaintext and can be intercepted by anyone on the same network. TLS is not optional — it is required for security, privacy, and regulatory compliance.
Real-World Use
A Microservices architecture uses mTLS for all internal communication. Each service has a certificate signed by an internal CA. mTLS ensures that only authenticated services can communicate and all traffic is encrypted. Certificates are rotated automatically every 30 days.
TLS Configuration
Node.js HTTPS Server with TLS 1.3
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('/etc/ssl/private/key.pem'),
cert: fs.readFileSync('/etc/ssl/certs/cert.pem'),
ca: fs.readFileSync('/etc/ssl/certs/ca.pem'),
minVersion: 'TLSv1.3',
ciphers: [
'TLS_AES_256_GCM_SHA384',
'TLS_AES_128_GCM_SHA256',
'TLS_CHACHA20_POLY1305_SHA256'
].join(':'),
honorCipherOrder: true,
rejectUnauthorized: true,
requestCert: false
};
const server = https.createServer(options, app);
server.listen(443, () => {
console.log('HTTPS server with TLS 1.3 listening on port 443');
});
Expected output:
Server only accepts TLS 1.3 connections. Only AEAD cipher suites are supported. SSLv3, TLS 1.0, 1.1, 1.2 are rejected.
Mutual TLS (mTLS) for Service-to-Service
const https = require('https');
const fs = require('fs');
// Server requires client certificate
const serverOptions = {
key: fs.readFileSync('/etc/ssl/private/service-a-key.pem'),
cert: fs.readFileSync('/etc/ssl/certs/service-a-cert.pem'),
ca: fs.readFileSync('/etc/ssl/certs/internal-ca.pem'),
minVersion: 'TLSv1.3',
requestCert: true,
rejectUnauthorized: true
};
// Client presents certificate
const clientOptions = {
key: fs.readFileSync('/etc/ssl/private/service-b-key.pem'),
cert: fs.readFileSync('/etc/ssl/certs/service-b-cert.pem'),
ca: fs.readFileSync('/etc/ssl/certs/internal-ca.pem'),
minVersion: 'TLSv1.3'
};
// Server validates client certificate
app.use((req, res, next) => {
const cert = req.socket.getPeerCertificate();
if (!cert.subject || !cert.subject.CN) {
return res.status(401).json({ error: 'Client certificate required' });
}
req.clientService = cert.subject.CN;
next();
});
Expected output:
Service B's HTTPS request to Service A includes B's client certificate. Service A validates the certificate against the internal CA. Only services with valid certificates can communicate.
Automatic Certificate Renewal with Let's Encrypt
const acme = require('acme-client');
const fs = require('fs/promises');
async function obtainCertificate(domains) {
const client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.production,
accountKey: await fs.readFile('./account-key.pem', 'utf8')
});
const [cert, key] = await client.auto({
domains,
csr: new acme.csr.Csr({
commonName: domains[0],
altNames: domains
}),
challengePriority: ['http-01'],
challengeCreate: async (authz, challenge, keyAuthorization) => {
// Store challenge for HTTP-01 validation
await fs.writeFile(`/var/www/.well-known/acme-challenge/${challenge.token}`, keyAuthorization);
},
challengeRemove: async (authz, challenge, keyAuthorization) => {
await fs.unlink(`/var/www/.well-known/acme-challenge/${challenge.token}`);
}
});
await fs.writeFile('/etc/ssl/certs/fullchain.pem', cert.toString());
await fs.writeFile('/etc/ssl/private/key.pem', key.toString());
console.log('Certificate obtained and saved');
}
Expected output:
Let's Encrypt validates domain ownership via HTTP-01 challenge. Certificate and key are saved to files. Certificate auto-renews every 60 days.
Common Mistakes
- Using weak cipher suites or outdated TLS versions (TLS 1.0, 1.1, SSLv3).
- Not enabling HSTS, allowing SSL stripping attacks on first visit.
- Ignoring certificate expiration — expired certificates cause connection failures for all users.
- Using self-signed certificates in production without proper CA trust chain.
- Not implementing certificate pinning or CA verification for internal mTLS.
Practice Questions
- What is the difference between TLS 1.2 and TLS 1.3?
- How does mutual TLS (mTLS) authenticate services?
- Why is HSTS important for TLS security?
- What is certificate transparency?
- How does Let's Encrypt automate certificate renewal?
Challenge
Set up a Node.js HTTPS server with TLS 1.3 only. Configure mTLS for a second service that acts as a client. Use Let's Encrypt staging environment to obtain certificates. Test with curl and openssl s_client. Verify that non-mTLS requests are rejected.
FAQ
Mini Project
Create a two-service architecture with mTLS. Service A (Express, port 3001) requires client certificates. Service B (Express, port 3002) calls Service A with its certificate. Generate certificates with a custom CA. Configure TLS 1.3 only. Write a test that verifies mTLS authentication.
What's Next
Continue to Security Logging to learn about security event logging and monitoring.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro