Spring til indhold
WebIndsigter

Sikkerhed på et offentligt site: det I faktisk skal have styr på

Et offentligt site er en dør. Plugin'et i hjørnet er ikke låsen.

Hjemmeside-sikkerhed er ikke et plugin. Det er headers, opdateringer, hemmeligheder og det admin-vindue, I glemte at lukke.

11 minutters læsning
Stiliseret browservindue med performance-grafer og et tydeligt hængelås-lag foran et offentligt site.

Web & drift

Hvis I søger efter hjemmeside-sikkerhed eller WordPress-sikkerhed, har I typisk fået én af tre henvendelser: et bureau der sælger et "security plugin", en mail om at jeres admin ligger åben på /wp-admin, eller en ubehagelig fornemmelse efter at have læst om et hack på et site, der lignede jeres. Ingen af delene fortæller jer, hvad I faktisk skal have styr på.

Vi bygger og rydder op i offentlige sites — Next.js, WordPress, shops. Sikkerhed her er ikke et SOC2-program og ikke et pentest-teater. Det er den kedelige stak, der afgør om en fremmed kan skrive i jeres CMS, læse jeres .env, eller omdirigere jeres besøgende. Det er også det, de fleste plugins lover at løse uden at røre årsagen.

TL;DR

De vigtigste pointer

  • Et security-plugin er et net. Det er ikke en trusselsmodel, og det retter ikke et tema fra 2019 eller en lækket deploy-nøgle.
  • De angreb, der rammer et marketing-site, er kedelige: udnyttede plugins, åbne admin-stier, stjålne hemmeligheder, åbne redirects.
  • Headers (HTTPS, HSTS, en ærlig CSP, Referrer-Policy) er det, browseren kan nægte for jer. Sæt dem. Mål dem. Gør dem ikke til et badge.
  • WordPress og Next.js har samme pligter og forskellige steder, de brækker. Opdateringer versus hemmeligheder i git — begge er jeres.
  • Admin, previews og staging må ikke ligne produktion med svagere lås. Det er der, de fleste "små" sites bliver taget.
  • En eftermiddag med en tjekliste slår et årligt slide om "vi har sikkerhed". Hvis I ikke kan forklare jeres dør, har I ikke låst den.

Hvorfor det her overhovedet er vigtigt

Et offentligt site er interessant for angribere, fordi det er let. Ikke fordi I er interessante. Automatiske scans kigger efter kendte plugin-huller, standard-admin-stier, åbne .git-mapper og nøgler, nogen commitede for to år siden. De behøver ikke kende jer. De behøver et site, der stadig kører det, alle andre har patchet.

Når det går galt på et marketing-site, er det sjældent en filmagtig indbrudsscene. Det er en spam-side i jeres CMS, en redirect til en shop I ikke ejer, eller et skript i et tema-header, der stjæler formularer. SEO dør bagefter — Google er hurtig til at mærke et kompromitteret site, og langsom til at glemme det. Det er også derfor sikkerhed og SEO hænger sammen her, uden at de er det samme.

Teams overreagerer eller underreagerer. Over: et WAF-abonnement, tre plugins og en følelse af at emnet er taget af bordet. Under: "vi er jo bare en brochure." Brochuren har et login, et kontaktformular-endpoint og ofte et preview, der viser næste kampagne. Det er nok. I behøver ikke være en bank for at have en dør, nogen vil prøve.

Det her er ikke en pentest-rapport og ikke et løfte om at I er "sikre". Det er den ingeniørsamtale, I skal have, før I køber det næste plugin: hvad kan en fremmed gøre ved det her site på en eftermiddag, og hvad har I sat i vejen?

Den trusselsmodel der faktisk gælder et offentligt site

I skal ikke starte med nation-state. I skal starte med det, der sker for sites som jeres. Én: kendte sårbarheder i CMS, tema og plugins — især WordPress, især ting I ikke længere bruger, men ikke har slået fra. To: credential stuffing mod /wp-admin, /login, /studio eller det CMS, I glemte at sætte 2FA på. Tre: hemmeligheder i git, i et screenshot, i en Vercel-log, i en gammel .env på en medarbejderlaptop.

Fire: åbne previews og staging med rigtige data og svag auth. Fem: et kontaktformular-endpoint uden rate-limit, der bliver til en spam-kanon eller et sted at prøve injection. Seks: et åbent redirect eller et gammelt subdomain, der peger på en bucket, I ikke længere ejer. Det er listen. Den er kedelig. Den er også den, der tager sites.

Det I typisk ikke skal starte med: at skrive en 40-siders policy, at købe et overlay der lover "malware scan", eller at bede om en fuld pentest af en brochure, før I har slået ubrugte plugins fra. Pentest er rigtigt, når I har noget at teste — et login, et checkout, et API. Først: luk de døre, I selv kan se.

GDPR-samtalen er en anden. Den handler om hvad I sender, og om brugeren sagde ja. Den her handler om hvad en fremmed kan gøre, uanset samtykke. Et site kan have et pænt cookie-banner og stadig et åbent admin. De to tjeklister skal ikke blandes. De skal begge gøres.

Headers, TLS og det browseren kan nægte for jer

HTTPS er gulvet. HSTS fortæller browseren, at den ikke må gå tilbage til HTTP. Uden HSTS kan et netværk, I ikke styrer, stadig prøve at nedgradere det første hop. Sæt det, når I er sikre på, at I ikke har et ældre HTTP-kun-endpoint, I er afhængige af. Det er et ét-linjes header-valg, ikke et projekt.

Content-Security-Policy er det, der begrænser, hvilke scripts og embeds siden må loade. En ærlig CSP er stram og går i stykker, når nogen smider et nyt pixel ind. Det er meningen. En CSP med 'unsafe-inline' og et dusin wildcard-domains er et badge. Start med default-src 'self', tillad det I faktisk bruger, og brug Report-Only en uge, før I håndhæver. Hvis I ikke kan liste jeres script-domains, har I et andet problem — det samme inventar, GDPR-posten beder om.

Referrer-Policy, X-Content-Type-Options, Permissions-Policy og en frame-ancestors der ikke tillader, at I bliver iframet på et fremmed site, er de næste. Ingen af dem gør jer interessante på en konference. Alle sammen fjerner en hel klasse af "nogen indlejrede os og stjal et klik". På Next.js sætter I dem i next.config eller i jeres edge. På Cloudflare kan I sætte dem dér. På WordPress skal temaet eller serveren gøre det — et plugin der lover "alle headers" er fint, hvis I tjekker, at de faktisk sendes.

TLS-certifikatet skal fornyes af sig selv. Hvis et menneske skal huske det, vil det udløbe en lørdag. Let's Encrypt via jeres host, eller den cert-styring jeres edge allerede har, er det rigtige svar. Et screenshot af et hængelås-ikon i et slide er det forkerte.

Plugin-teater versus det I kan eftervise

Det teams typisk køber

Det der ser ud som sikkerhed på et slides.

  • Et WordPress-plugin med et grønt badge og en ugentlig mail
  • En WAF slået til uden at nogen har læst, hvad den blokerer
  • "Vi har 2FA" — på ét login, ikke på CMS, host og git
  • En følelse af at emnet er taget af bordet

Det der faktisk afgør sagen

Det I kan vise en kollega på en eftermiddag.

  • Admin, preview og staging kræver rigtig auth — og 2FA
  • Ubrugte plugins og temaer er væk, resten er opdateret
  • Ingen hemmeligheder i git; rotate det, der har ligget der
  • Headers I kan se i et response, ikke kun i et plugin-panel

En eftermiddags gennemgang, før I køber noget

Uden den her rækkefølge køber I det forkerte — enten et plugin eller en pentest, I ikke er klar til.

  1. 01

    Find admin, preview og staging

    Prøv /wp-admin, /login, /studio, /admin, jeres CMS-URL og det staging-domæne, I brugte sidste år. Hvis de svarer uden en rigtig login-mur — eller med standardbrugeren — er det opgaven. Ikke et plugin.

  2. 02

    List det, sitet kører

    CMS, tema, plugins, Next.js-version, de services I kalder. Slå det fra, I ikke bruger. Opdater det, I bruger. Et forladt formular-plugin er en dør, I har glemt. Tæl ikke. Fjern.

  3. 03

    Søg efter hemmeligheder, I selv har lagt

    git log og jeres host-dashboard: API-nøgler, database-URL'er, webhook-secrets. Hvis de har været i et repo, så rotér dem. At slette filen i et nyt commit slår ikke historikken ihjel.

  4. 04

    Læs response-headers på forsiden og på login

    HTTPS, HSTS, en CSP der ikke er et wildcard-digt, X-Content-Type-Options, en frame-policy. Hvis I ikke kan se dem, har I ikke sat dem — uanset hvad et plugin-panel siger.

  5. 05

    Send én formular og se, hvad der sker

    Rate-limit, captcha eller et andet kedeligt værn. Hvor lander mailen? Hvem kan læse den? Et endpoint der tager 200 uden tøven fra et script, er en kanon. Luk den, før I taler om WAF-regler.

Et offentligt site bliver ikke taget af en filmagtig angriber. Det bliver taget af det plugin, I glemte, og den admin-sti I aldrig skjulte.

WordPress og Next.js — samme pligter, forskellige brud

På WordPress er den typiske brudflade plugin- og tema-overfladen. I har et økosystem, der er nemt at udvide og nemt at glemme. Sikkerhed her er hygiejne: færre plugins, automatiske opdateringer I faktisk overvåger, et tema I kan forklare, og et admin der ikke er åbent for hele internettets credential-lists. Et "security plugin" kan hjælpe med at skjule /wp-admin og blokere nogle paths. Det kan ikke redde et tema, der ikke længere får patches.

På Next.js og et hosted CMS er den typiske brudflade hemmeligheder og preview. En NEXT_PUBLIC_-variabel der ikke skulle have været public. En preview-URL uden auth. En server action der stoler på det, browseren sender. Deploy-nøgler i et GitHub-action-log. I har færre plugins og flere steder, hvor I selv er platformen. Det er ikke sikrere af sig selv. Det er sikrere, hvis I behandler env og preview som produktion.

Begge stakke skylder jer det samme udadtil: HTTPS, headers, et formular-endpoint der ikke er en åben slange, og et admin I kan forsvare. Begge kan sidde bag Cloudflare og få WAF og bot-værn. WAF'en er et net. Den er nyttig. Den er ikke en undskyldning for at køre et CMS, I ikke opdaterer, eller en preview, I deler i Slack uden login.

Hvis I overvejer at skifte fra WordPress til Next.js af sikkerhedsgrunde alene, så vær ærlige: I skifter sted, de kedelige fejl kan sidde. I fjerner plugin-overfladen. I får env- og pipeline-overfladen. Skift hvis I også har andre grunde — redaktion, performance, SEO-kontrol. Skift ikke fordi et slide sagde, at Jamstack er sikkert. Statiske filer er svære at inficere. Jeres build og jeres CMS er det ikke.

Hvad I typisk bør gøre i den situation, I står i

Venstre er det, I typisk siger. Højre er det næste skridt, vi typisk anbefaler.

  • Situation

    I har installeret et security-plugin og kalder jer færdige

    Næste skridt

    Slå plugin-panelet fra og kør eftermiddagens tjekliste. Det panelet ikke viser, er opgaven

  • Situation

    Admin eller staging svarer uden rigtig login

    Næste skridt

    Luk det i dag. Køb ikke en WAF som erstatning for en dør

  • Situation

    I ved ikke, hvilke plugins I stadig kører

    Næste skridt

    List, slå fra, slet. Opdater det, I beholder. Tæl ikke "vi har 20, det går"

  • Situation

    I har en Next.js-app og "ingen WordPress, så vi er sikre"

    Næste skridt

    Tjek env, preview-auth, server actions og nøgler i git. Stakken flyttede sig. Pligten gjorde ikke

  • Situation

    Nogen vil penteste brochuren, før I har gjort det kedelige

    Næste skridt

    Gør det kedelige først. Pentest et login og et checkout, når de findes — ikke et plugin-katalog

Tjekliste før I siger "sitet er låst"

Hvis I mangler flere af punkterne, er løftet for tidligt — uanset hvilket badge et plugin viser.

  • Admin, preview og staging kræver rigtig auth, og 2FA sidder på de konti der kan publicere
  • Ubrugte plugins, temaer og services er væk; resten får opdateringer I ser
  • Ingen deploy-nøgler eller database-URL'er i git — og det, der har været der, er rotéreret
  • Forside og login sender HSTS og en CSP, I kan forklare, ikke et wildcard-digt
  • Kontaktformularen har et kedeligt værn og et sted, mailen lander, I styrer
  • I kan pege på, hvem der får besked, hvis host eller CMS nægter et login

Spørgsmål vi får igen og igen

  • Er et WordPress security-plugin nok?

    Næsten aldrig som det eneste. Det kan skjule admin, blokere nogle paths og minde om opdateringer. Det retter ikke et forladt tema, et plugin I ikke bruger, eller en nøgle i git. Behandl det som et net. Gør hygiejnen. Hvis panelet er grønt og /wp-admin stadig tager password-gæt fra hele internettet uden 2FA, er I ikke færdige.

  • Er Next.js eller et statisk site "sikkert nok" uden den slags?

    Statiske filer er svære at inficere i runtime. Jeres CMS, preview, build og env er det ikke. En lækket deploy-nøgle eller et preview uden auth er samme klasse af dør som et åbent /wp-admin. I har flyttet overfladen. I har ikke slettet pligten. Headers, hemmeligheder og login gælder stadig.

  • Skal vi have en WAF — Cloudflare eller noget andet?

    Et edge-lag der kan rate-limite, blokere kendte bots og sætte headers, er fornuftigt på et offentligt site. Det er et net. Det er ikke en grund til at droppe opdateringer eller lade staging være åben. Vi bruger ofte Cloudflare til den slags, fordi det sidder foran uanset origin. Køb det for at få et net. Køb det ikke som erstatning for at lukke admin.

  • Hvad med GDPR og cookies — er det det samme som sikkerhed?

    Nej. GDPR er, hvad I sender, og om brugeren sagde ja. Sikkerhed er, hvad en fremmed kan gøre ved sitet. Et pænt banner og et åbent admin kan leve side om side. Gør begge tjeklister. Bland dem ikke, så I tror at et cookie-plugin også er en lås.

  • Hvornår giver en pentest mening på et marketing-site?

    Når der er et login, et checkout eller et API, nogen kan misbruge — og når I allerede har gjort det kedelige. En pentest af en brochure med tre forladte plugins er et dyrt dokument om noget, I selv kunne se. Få dørene lukket. Test så det, I ikke selv kan se: auth-flows, uploads, de endpoints I har bygget.

Hvis I kun kan pege på et plugin, når nogen spørger

Lad os gennemgå de døre, sitet faktisk har.

En eftermiddag med admin, headers og hemmeligheder slår et badge, I har købt for at få ro.

Hvorfor vi skrev den her

Det er sådan vi tænker — og det er det vi bygger.

Vores indsigter handler om det vi rent faktisk leverer. Hvis det her ramte noget du arbejder med, så er der en konkret service der ligger tæt på.