Ενότητα 7 · 7.4
Caddy, HSTS και security.txt
Ενότητα 7: Domain, web server και https · Χρόνος ανάγνωσης: 12 λεπτά
Στόχος μαθήματος: Μετά από αυτό το μάθημα μπορείτε να χρησιμοποιείτε το Caddy ως απλό web server με αυτόματο https, να ορίζετε το HSTS και τα σημαντικότερα headers ασφαλείας (σε Caddy και nginx) χωρίς να κάνετε τον ιστότοπό σας απρόσιτο, και να δημοσιεύετε ένα έγκυρο security.txt.
Βασικές έννοιες: Caddy, Caddyfile, αυτόματο https, reverse_proxy, file_server, header, HSTS (Strict-Transport-Security), max-age, includeSubDomains, preload, X-Content-Type-Options, frame-ancestors, Referrer-Policy, security.txt, Contact, Expires, /.well-known/
Caddy: https χωρίς έγνοιες
Στα μαθήματα 7.2 και 7.3 δημιουργήσατε με nginx και certbot έναν ιστότοπο με https. Πρόκειται για δύο προγράμματα, μερικά αρχεία και έναν timer. Το Caddy τα κάνει όλα σε ένα: είναι ένας web server με ενσωματωμένο ACME client. Αν ένα όνομα υπάρχει στη ρύθμιση, δείχνει προς τον server και οι θύρες 80 και 443 είναι προσβάσιμες, τότε το Caddy παίρνει μόνο του ένα πιστοποιητικό, το ανανεώνει έγκαιρα και ανακατευθύνει το http στο https.
Το Caddy υπάρχει στο αποθετήριο πακέτων του Debian και του Ubuntu (σε κάπως παλαιότερη έκδοση)· οι δημιουργοί του προσφέρουν επίσης δικό τους αποθετήριο apt με την πιο πρόσφατη έκδοση, με δικό του κλειδί και Signed-By, όπως στο μάθημα 4.1. Στην ενότητα 9 το Caddy εκτελείται ως container για τις υπηρεσίες Docker σας (μάθημα 9.4). Εδώ το χρησιμοποιείτε ως κανονική υπηρεσία.
anna@web1:~$ sudo apt install caddy
anna@web1:~$ systemctl status caddyΧρησιμοποιήστε είτε nginx είτε Caddy στις θύρες 80 και 443, όχι και τα δύο: μόνο ένα πρόγραμμα μπορεί να ακούει σε μια θύρα.
Το Caddyfile
Η ρύθμιση βρίσκεται στο /etc/caddy/Caddyfile. Ένας στατικός ιστότοπος και μια εφαρμογή πίσω από reverse proxy:
example.com, www.example.com {
root * /var/www/example.com
file_server
encode zstd gzip
}
app.example.com {
reverse_proxy 127.0.0.1:3000
}Αυτό είναι όλο. Το Caddy ακούει αυτόματα σε IPv4 και IPv6, ζητά πιστοποιητικά για τα τρία ονόματα, ανακατευθύνει το http στο https και, με το reverse_proxy, προωθεί αυτόματα τα headers X-Forwarded-For και X-Forwarded-Proto (κάτι που στο nginx κάνατε χειροκίνητα, μάθημα 7.2).
Ο έλεγχος και η επαναφόρτωση γίνονται όπως στο nginx:
anna@web1:~$ caddy validate --config /etc/caddy/Caddyfile
Valid configuration
anna@web1:~$ sudo systemctl reload caddy
anna@web1:~$ sudo journalctl -u caddy -fΣτο journal βλέπετε τη λήψη των πιστοποιητικών. Αν κάτι πάει στραβά, εκεί αναγράφεται ο λόγος: συνήθως ο ίδιος με το certbot (κλειστή θύρα 80, το όνομα δείχνει αλλού, χαλασμένο IPv6· μάθημα 7.3).
HSTS: ο browser θυμάται ότι πρόκειται για https
Η ανακατεύθυνση από http σε https έχει ένα αδύναμο σημείο: το πρώτο αίτημα μέσω http γίνεται ακόμη χωρίς κρυπτογράφηση. Κάποιος στο ίδιο δίκτυο μπορεί να το υποκλέψει και να κρατήσει τον επισκέπτη στο http. Το HSTS (HTTP Strict Transport Security, RFC 6797) λύνει αυτό το πρόβλημα. Ο ιστότοπός σας στέλνει μέσω https αυτό το header:
Strict-Transport-Security: max-age=31536000Ο browser θυμάται τότε για έναν χρόνο (31.536.000 δευτερόλεπτα) ότι αυτό το όνομα επιτρέπεται μόνο μέσω https. Αν ο επισκέπτης πληκτρολογήσει αργότερα http://, ο browser το μετατρέπει μόνος του σε https, πριν σταλεί οτιδήποτε στο δίκτυο. Ένα header HSTS που φτάνει μέσω http αγνοείται από τον browser· μετράει μόνο μέσω https.
Υπάρχει και μια αρνητική πλευρά. Αν ένας browser έχει απομνημονεύσει το HSTS και δει σφάλμα πιστοποιητικού (ληγμένο, λάθος όνομα), ο επισκέπτης δεν έχει κουμπί για να συνεχίσει παρ' όλα αυτά. Ο ιστότοπός σας είναι τότε απρόσιτος για αυτούς τους επισκέπτες μέχρι να διορθωθεί το πιστοποιητικό. Γι' αυτό:
- Ενεργοποιήστε το HSTS μόνο όταν το https λειτουργεί παντού και η ανανέωση γίνεται αυτόματα (μάθημα 7.3).
- Ξεκινήστε με μικρό
max-age, για παράδειγμα μία ημέρα (86400), και αυξήστε το σταδιακά έως έναν χρόνο. - Να είστε προσεκτικοί με το
includeSubDomains: ισχύει για κάθε subdomain, ακόμη και για ένα παλιό subdomain με κάποια συσκευή ή υπηρεσία χωρίς έγκυρο πιστοποιητικό. - Το
preloadζητά καταχώριση σε μια λίστα ενσωματωμένη στους browsers (hstspreload.org). Για αυτό απαιτούνται, μεταξύ άλλων,max-ageτουλάχιστον ενός έτους,includeSubDomainsκαι https σε όλα τα subdomains. Η αφαίρεση από τη λίστα διαρκεί μήνες. Κάντε το μόνο αν είστε βέβαιοι ότι ολόκληρο το domain θα είναι μόνιμα https.
Τα υπόλοιπα headers ασφαλείας
Εκτός από το HSTS, υπάρχουν μερικά headers που με λίγο κόπο βοηθούν πολύ:
| Header | Προστασία από | Τιμή για αρχή |
|---|---|---|
X-Content-Type-Options | ο browser μαντεύει τι είδους αρχείο είναι κάτι, έτσι μια ανεβασμένη «εικόνα» μπορεί να εκτελεστεί ως script | nosniff |
Content-Security-Policy: frame-ancestors (ή το παλαιότερο X-Frame-Options) | clickjacking: ένας άλλος ιστότοπος φορτώνει αόρατα τη σελίδα σας μέσα σε ένα frame | frame-ancestors 'self' (ή X-Frame-Options: SAMEORIGIN) |
Referrer-Policy | η διεύθυνση της σελίδας σας (με όρους αναζήτησης ή αριθμούς παραγγελιών) διαρρέει σε άλλους ιστότοπους | strict-origin-when-cross-origin |
Ένα πλήρες Content-Security-Policy (ποια scripts και styles επιτρέπεται να φορτώνονται) προσαρμόζεται ειδικά σε κάθε ιστότοπο και ξεφεύγει από αυτό το μάθημα· ξεκινήστε μόνο με το frame-ancestors.
Στο Caddy τα ορίζετε με τη directive header μέσα στο μπλοκ ενός ιστότοπου:
example.com, www.example.com {
root * /var/www/example.com
file_server
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
Content-Security-Policy "frame-ancestors 'self'"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}Το Caddy δεν ορίζει μόνο του το HSTS και αυτά τα headers· πρέπει λοιπόν να τα προσθέσετε εσείς. Το -Server αφαιρεί το header Server. Στο μάθημα 9.4 προσθέτετε στο ίδιο είδος μπλοκ το δικό σας header X-Weboke-Cursus για την άσκηση.
Στο nginx προσθέτετε τις ίδιες γραμμές στο μπλοκ server για τη θύρα 443:
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Το always εξασφαλίζει ότι στέλνονται και με τις σελίδες σφάλματος. Προσέξτε μια γνωστή παγίδα του nginx: αν μέσα σε ένα μπλοκ location υπάρχει έστω και ένα add_header, τότε οι γραμμές add_header του μπλοκ server δεν ισχύουν πλέον για εκείνο το location. Γι' αυτό ελέγχετε πάντα το αποτέλεσμα:
anna@laptop:~$ curl -sI https://example.com | grep -iE 'strict|nosniff|frame|referrer'
strict-transport-security: max-age=31536000
x-content-type-options: nosniff
content-security-policy: frame-ancestors 'self'
referrer-policy: strict-origin-when-cross-originsecurity.txt: πώς μπορεί κάποιος να σας αναφέρει ένα κενό ασφαλείας
Ας υποθέσουμε ότι κάποιος βρίσκει ένα κενό ασφαλείας στον server σας, όπως έναν ανοιχτό φάκελο ή μια παρωχημένη υπηρεσία. Πώς ξέρει ποιον πρέπει να ειδοποιήσει; Γι' αυτό υπάρχει το security.txt (RFC 9116): ένα μικρό αρχείο κειμένου σε σταθερή θέση, https://<jouwdomein>/.well-known/security.txt.
Contact: mailto:security@example.com
Expires: 2027-10-31T23:00:00Z
Preferred-Languages: nl, en
Canonical: https://example.com/.well-known/security.txtΟι κανόνες:
- Το
Contactείναι υποχρεωτικό: μια διεύθυνσηmailto:,https://ήtel:όπου καταλήγουν οι αναφορές. Χρησιμοποιήστε μια διεύθυνση που κάποιος πραγματικά διαβάζει. - Το
Expiresείναι υποχρεωτικό και επιτρέπεται να εμφανίζεται μόνο μία φορά: η ημερομηνία μετά την οποία το αρχείο θεωρείται παρωχημένο. Το RFC συνιστά ημερομηνία εντός ενός έτους. Έτσι δεν μένει ξεχασμένο ένα αρχείο με μια διεύθυνση που κανείς δεν θυμάται πια. Σημειώστε στο ημερολόγιό σας πότε πρέπει να το ανανεώσετε. - Τα
Preferred-Languages,CanonicalκαιPolicy(ένας σύνδεσμος προς την πολιτική σας για αναφορές) είναι προαιρετικά. - Το αρχείο πρέπει να σερβίρεται μέσω https, ως απλό κείμενο (
text/plain, UTF-8). Η παλιά θέση/security.txtεπιτρέπεται επιπλέον, αλλά το/.well-known/έχει προτεραιότητα.
Τοποθέτηση σε στατικό ιστότοπο:
anna@web1:~$ sudo mkdir -p /var/www/example.com/.well-known
anna@web1:~$ sudoedit /var/www/example.com/.well-known/security.txt
anna@laptop:~$ curl -sI https://example.com/.well-known/security.txt | grep -i content-type
content-type: text/plain; charset=utf-8Αν στο nginx χρησιμοποιείτε το δίχτυ ασφαλείας location ~ /\. { deny all; } από το μάθημα 7.2, προσθέστε πριν από αυτό μια εξαίρεση: location ^~ /.well-known/ { }. Για μια εφαρμογή πίσω από reverse proxy, στο Caddy μπορείτε να κάνετε τη διαδρομή /.well-known/security.txt να δείχνει σε ένα αρχείο με ένα μπλοκ handle και file_server.
Ένα συνηθισμένο λάθος: ένας ιστότοπος ή μια εφαρμογή που απαντά σε κάθε άγνωστη διαδρομή με την αρχική σελίδα και κατάσταση 200. Τότε το security.txt «υπάρχει», αλλά είναι HTML. Ελέγξτε λοιπόν και το περιεχόμενο.
Σε AlmaLinux και Rocky Linux
Εκεί το Caddy προέρχεται από το αποθετήριο των δημιουργών του ή από EPEL/COPR. Όπως και με το nginx, το SELinux πρέπει να επιτρέπει το reverse proxy (setsebool -P httpd_can_network_connect 1). Το Caddyfile και τα headers είναι τα ίδια.
Σύνοψη
- Το Caddy φροντίζει μόνο του για τα πιστοποιητικά, την ανανέωση και την ανακατεύθυνση στο https· το
reverse_proxyπροωθεί αυτόματα τα σωστά headers. - Το HSTS κάνει τους browsers να θυμούνται ότι ο ιστότοπός σας είναι μόνο https· αυξάνετε σταδιακά το
max-age, να είστε προσεκτικοί με τοincludeSubDomainsκαι πολύ προσεκτικοί με τοpreload. - Ορίστε επιπλέον
X-Content-Type-Options: nosniff,frame-ancestors 'self'και έναReferrer-Policy· στο Caddy μεheader, στο nginx μεadd_header ... always, και ελέγξτε μεcurl -I. - Το security.txt βρίσκεται στο
/.well-known/security.txt, μέσω https, ως απλό κείμενο, με υποχρεωτικόContactκαι έναExpiresεντός ενός έτους.
Κουίζ
Άσκηση
Άσκηση Z7.4: HSTS, headers και security.txt. Ορίστε για τον ιστότοπό σας HSTS (τουλάχιστον 180 ημέρες, αφού το https λειτουργεί καλά για κάποιο διάστημα), X-Content-Type-Options: nosniff και frame-ancestors 'self' (ή X-Frame-Options), και δημοσιεύστε ένα security.txt με Contact και ένα Expires εντός ενός έτους. Συμπληρώστε το hostname σας. Το πρόγραμμα ανακτά το https://<hostnaam>/ και ελέγχει τα headers, και ανακτά το https://<hostnaam>/.well-known/security.txt και ελέγχει τα πεδία. Δεν αποθηκεύει τίποτα εκτός από το αποτέλεσμα και την ημερομηνία Expires.
Για περαιτέρω μελέτη
- HSTS και κεφαλίδες ασφαλείας για τον ιστότοπό σας
- Caddy, Automatic HTTPS: https://caddyserver.com/docs/automatic-https
- Caddy, η directive header: https://caddyserver.com/docs/caddyfile/directives/header
- RFC 6797, HTTP Strict Transport Security: https://www.rfc-editor.org/rfc/rfc6797.html
- Η λίστα HSTS preload και οι απαιτήσεις της: https://hstspreload.org/
- RFC 9116, security.txt: https://www.rfc-editor.org/rfc/rfc9116.html
- securitytxt.org (δημιουργία και έλεγχος): https://securitytxt.org/
