<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Dekrypt Security blog</title>
  <subtitle>Dekrypt Security designs and engineers secure websites, web apps, mobile apps and security tooling.</subtitle>
  <link href="https://dekryptsecurity.com/blog/feed.xml" rel="self"/>
  <link href="https://dekryptsecurity.com/blog/"/>
  <id>https://dekryptsecurity.com/blog/</id>
  <updated>2026-10-13T00:00:00Z</updated>
  <author><name>Dekrypt Security</name></author>
  <entry>
    <title>Web app, PWA, or native app: how to choose</title>
    <link href="https://dekryptsecurity.com/blog/web-app-pwa-or-native-app/"/>
    <id>https://dekryptsecurity.com/blog/web-app-pwa-or-native-app/</id>
    <updated>2026-10-13T00:00:00Z</updated>
    <published>2026-10-13T00:00:00Z</published>
    <summary>A plain comparison of web apps, progressive web apps and native Android or iOS apps, covering reach, offline use, device features, cost, stores and security.</summary>
    <category term="web apps"/>
    <category term="pwa"/>
    <category term="mobile apps"/>
    <content type="html">&lt;p&gt;&amp;quot;We need an app&amp;quot; is a common request. Sometimes that means a native app in the App Store and Google Play. Often a web app, or a progressive web app that people can install, does the same job for less money and less maintenance.&lt;/p&gt;
&lt;p&gt;This post compares the three options plainly so you can choose, or at least ask the right questions.&lt;/p&gt;
&lt;h2 id=&quot;the-three-options&quot;&gt;The three options&lt;/h2&gt;
&lt;h3 id=&quot;web-app&quot;&gt;Web app&lt;/h3&gt;
&lt;p&gt;A web app runs in the browser. Users open a URL. There is nothing to install and nothing to approve: you deploy a change and everyone has it immediately. One codebase works on phones, tablets and computers.&lt;/p&gt;
&lt;h3 id=&quot;progressive-web-app-pwa&quot;&gt;Progressive web app (PWA)&lt;/h3&gt;
&lt;p&gt;A PWA is a web app with extra abilities. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN describes it&lt;/a&gt; as an app built with web platform technologies that, like a website, runs on multiple platforms from a single codebase and, like a platform-specific app, can be installed on the device, operate while offline and in the background, and integrate with the device and other installed apps.&lt;/p&gt;
&lt;p&gt;Two pieces make that possible:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;A &lt;strong&gt;web app manifest&lt;/strong&gt;, a small JSON file describing the app&amp;#x27;s name, icons and how it should open when installed.&lt;/li&gt;&lt;li&gt;A &lt;strong&gt;service worker&lt;/strong&gt;, a script that can cache files and respond when the network is unavailable. MDN notes that service workers are only available in secure contexts, meaning the site must be served over HTTPS (with &lt;code&gt;localhost&lt;/code&gt; allowed for development).&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;A minimal manifest looks like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;name&amp;quot;: &amp;quot;Example Bookings&amp;quot;,
  &amp;quot;short_name&amp;quot;: &amp;quot;Bookings&amp;quot;,
  &amp;quot;start_url&amp;quot;: &amp;quot;/&amp;quot;,
  &amp;quot;display&amp;quot;: &amp;quot;standalone&amp;quot;,
  &amp;quot;background_color&amp;quot;: &amp;quot;#0a0d12&amp;quot;,
  &amp;quot;theme_color&amp;quot;: &amp;quot;#0a0d12&amp;quot;,
  &amp;quot;icons&amp;quot;: [
    { &amp;quot;src&amp;quot;: &amp;quot;/icons/icon-192.png&amp;quot;, &amp;quot;sizes&amp;quot;: &amp;quot;192x192&amp;quot;, &amp;quot;type&amp;quot;: &amp;quot;image/png&amp;quot; },
    { &amp;quot;src&amp;quot;: &amp;quot;/icons/icon-512.png&amp;quot;, &amp;quot;sizes&amp;quot;: &amp;quot;512x512&amp;quot;, &amp;quot;type&amp;quot;: &amp;quot;image/png&amp;quot; }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The open-source project &lt;a href=&quot;/projects/&quot;&gt;recall&lt;/a&gt;, is a PWA: it can be installed on iOS, Android and desktop from the browser, with no app store.&lt;/p&gt;
&lt;h3 id=&quot;native-app&quot;&gt;Native app&lt;/h3&gt;
&lt;p&gt;A native app is built for a specific platform, Android or iOS, using that platform&amp;#x27;s tools and distributed through its store. It has the fullest access to the device and the platform&amp;#x27;s look and feel. Android&amp;#x27;s &lt;a href=&quot;https://developer.android.com/guide/components/fundamentals&quot; rel=&quot;noopener noreferrer&quot;&gt;application fundamentals&lt;/a&gt; and Apple&amp;#x27;s developer documentation describe the models each platform uses.&lt;/p&gt;
&lt;h2 id=&quot;how-they-compare&quot;&gt;How they compare&lt;/h2&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Web app&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;PWA&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Native app&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Install&lt;/td&gt;&lt;td&gt;None&lt;/td&gt;&lt;td&gt;Optional, from the browser&lt;/td&gt;&lt;td&gt;From an app store&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Works offline&lt;/td&gt;&lt;td&gt;Usually not&lt;/td&gt;&lt;td&gt;Yes, if built for it&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Codebases&lt;/td&gt;&lt;td&gt;One&lt;/td&gt;&lt;td&gt;One&lt;/td&gt;&lt;td&gt;One per platform (or a cross-platform framework)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Updates&lt;/td&gt;&lt;td&gt;Instant&lt;/td&gt;&lt;td&gt;Instant (service worker refreshes)&lt;/td&gt;&lt;td&gt;Through store review and user updates&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Store listing&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No (some stores allow PWAs; check each)&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Device features&lt;/td&gt;&lt;td&gt;Browser APIs&lt;/td&gt;&lt;td&gt;Browser APIs&lt;/td&gt;&lt;td&gt;Full platform APIs&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Store fees&lt;/td&gt;&lt;td&gt;None&lt;/td&gt;&lt;td&gt;None&lt;/td&gt;&lt;td&gt;Developer account fees apply&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;On the last row: Apple&amp;#x27;s &lt;a href=&quot;https://developer.apple.com/programs/&quot; rel=&quot;noopener noreferrer&quot;&gt;Developer Program&lt;/a&gt; lists a $99 annual membership, and Google Play&amp;#x27;s &lt;a href=&quot;https://support.google.com/googleplay/android-developer/answer/6112435&quot; rel=&quot;noopener noreferrer&quot;&gt;developer account registration&lt;/a&gt; lists a US$25 one-time fee. Those are platform fees, before any development cost.&lt;/p&gt;
&lt;h2 id=&quot;questions-that-decide-it&quot;&gt;Questions that decide it&lt;/h2&gt;
&lt;h3 id=&quot;who-are-your-users-and-how-will-they-find-you&quot;&gt;Who are your users, and how will they find you?&lt;/h3&gt;
&lt;p&gt;If people find you through search, links or a QR code, the web wins: one tap and they are using it. If people expect to search an app store, or your competitors are all in the store, a store listing may matter.&lt;/p&gt;
&lt;h3 id=&quot;does-it-need-to-work-offline&quot;&gt;Does it need to work offline?&lt;/h3&gt;
&lt;p&gt;Field work, travel, poor connectivity. If yes, a web app alone won&amp;#x27;t do, but a PWA often will.&lt;/p&gt;
&lt;h3 id=&quot;does-it-need-device-features-browsers-dont-offer&quot;&gt;Does it need device features browsers don&amp;#x27;t offer?&lt;/h3&gt;
&lt;p&gt;Browsers can do a lot: camera access, location, notifications on many platforms, file access. But some background processing, deep OS integration and certain hardware access remain native-only or behave differently by platform. List the features you need, then check each against current browser support before deciding.&lt;/p&gt;
&lt;h3 id=&quot;how-often-will-it-change&quot;&gt;How often will it change?&lt;/h3&gt;
&lt;p&gt;Web apps and PWAs update instantly. Native apps go through store review for every update, and users may not install updates promptly. If you expect to change things weekly, that matters.&lt;/p&gt;
&lt;h3 id=&quot;what-is-your-budget-for-the-next-two-years-not-just-the-build&quot;&gt;What is your budget for the next two years, not just the build?&lt;/h3&gt;
&lt;p&gt;Two native apps means two codebases to maintain, two sets of OS updates to track, and two store reviews. A PWA means one.&lt;/p&gt;
&lt;h3 id=&quot;is-its-in-the-app-store-a-requirement-on-its-own&quot;&gt;Is &amp;quot;it&amp;#x27;s in the App Store&amp;quot; a requirement on its own?&lt;/h3&gt;
&lt;p&gt;Sometimes it is, for credibility or because a client asked. Be aware that Apple&amp;#x27;s &lt;a href=&quot;https://developer.apple.com/app-store/review/guidelines/&quot; rel=&quot;noopener noreferrer&quot;&gt;App Review Guidelines&lt;/a&gt; (section 4.2, Minimum Functionality) say an app should include features, content and UI that elevate it beyond a repackaged website. Wrapping a website in an app shell may be rejected.&lt;/p&gt;
&lt;h2 id=&quot;security-differences&quot;&gt;Security differences&lt;/h2&gt;
&lt;p&gt;All three can be built securely, and all three can be built badly. A few differences are worth knowing:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The server is your security boundary, always.&lt;/strong&gt; Whatever the client is, browser or native, never trust it. Check permissions and validate input on the server.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Secrets in native apps are not secret.&lt;/strong&gt; App bundles can be downloaded and unpacked. An API key compiled into an app should be treated as public. The same is true for JavaScript in a web app.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Updates are a security feature.&lt;/strong&gt; Web apps get fixes to every user instantly. A native app fix waits for review and for users to update; plan for supporting older versions.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Data on the device.&lt;/strong&gt; Offline-capable apps store data locally. Store only what is needed, and use the platform&amp;#x27;s secure storage for anything sensitive, such as tokens.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;HTTPS is required for PWAs&lt;/strong&gt; anyway, because service workers need a secure context. Use it for everything.&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;recommendation&quot;&gt;Recommendation&lt;/h2&gt;
&lt;p&gt;For most small businesses and first versions:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Start with a web app&lt;/strong&gt;, built mobile-first.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Make it a PWA&lt;/strong&gt; if users come back often or need offline use.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Go native&lt;/strong&gt; when there is a specific feature or distribution need the web can&amp;#x27;t meet, and you&amp;#x27;ve confirmed it.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;This order means you only pay for native development once you know you need it. If you start native and later find a PWA would have done, that money is hard to get back.&lt;/p&gt;
&lt;p&gt;If you&amp;#x27;re not sure which fits your project, send it through the &lt;a href=&quot;/start/&quot;&gt;project brief&lt;/a&gt; and you get a straight answer, including &amp;quot;you don&amp;#x27;t need an app&amp;quot;.&lt;/p&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;MDN Web Docs, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps&quot; rel=&quot;noopener noreferrer&quot;&gt;Progressive web apps&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;MDN Web Docs, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API&quot; rel=&quot;noopener noreferrer&quot;&gt;Service Worker API&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;Android Developers, &lt;a href=&quot;https://developer.android.com/guide/components/fundamentals&quot; rel=&quot;noopener noreferrer&quot;&gt;Application fundamentals&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;Apple Developer, &lt;a href=&quot;https://developer.apple.com/programs/&quot; rel=&quot;noopener noreferrer&quot;&gt;Apple Developer Program&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;Google Play Console Help, &lt;a href=&quot;https://support.google.com/googleplay/android-developer/answer/6112435&quot; rel=&quot;noopener noreferrer&quot;&gt;Create and set up your developer account&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;Apple Developer, &lt;a href=&quot;https://developer.apple.com/app-store/review/guidelines/&quot; rel=&quot;noopener noreferrer&quot;&gt;App Review Guidelines&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Securing a static site on Cloudflare Pages: what is done on this site</title>
    <link href="https://dekryptsecurity.com/blog/securing-a-static-site-on-cloudflare-pages/"/>
    <id>https://dekryptsecurity.com/blog/securing-a-static-site-on-cloudflare-pages/</id>
    <updated>2026-10-13T00:00:00Z</updated>
    <published>2026-10-13T00:00:00Z</published>
    <summary>What was done on this site: security headers with _headers, Functions limited by _routes.json, a hardened contact form, and zero third-party requests.</summary>
    <category term="cloudflare pages"/>
    <category term="headers"/>
    <category term="static sites"/>
    <content type="html">&lt;p&gt;A static site has a small attack surface: no database, no server-side sessions, no admin panel. That does not make it secure by default. Missing headers, third-party scripts and a careless contact form can all undo the advantage.&lt;/p&gt;
&lt;p&gt;This post explains how I set up this site on Cloudflare Pages and why. The platform details are checked against Cloudflare&amp;#x27;s documentation as of the date below. The &lt;a href=&quot;/trust/&quot;&gt;trust page&lt;/a&gt; shows the live header list, generated from the same file at build time.&lt;/p&gt;
&lt;h2 id=&quot;the-shape-of-the-site&quot;&gt;The shape of the site&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;HTML, CSS, fonts and a little JavaScript, all generated ahead of time by a small build script and served from Cloudflare Pages.&lt;/li&gt;&lt;li&gt;One server-side component: a Pages Function at &lt;code&gt;/api/contact&lt;/code&gt; that turns form submissions into an email.&lt;/li&gt;&lt;li&gt;No third-party requests at all. No CDN-hosted libraries, web fonts, analytics or embeds.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;The last point matters most. Every external script is code you did not write running with full access to your page. The simplest way to manage that risk is not to have any.&lt;/p&gt;
&lt;h2 id=&quot;security-headers-with-headers&quot;&gt;Security headers with _headers&lt;/h2&gt;
&lt;p&gt;Cloudflare Pages reads a plain-text file called &lt;code&gt;_headers&lt;/code&gt; from the build output directory and attaches the headers it lists to matching static responses. The &lt;a href=&quot;https://developers.cloudflare.com/pages/configuration/headers/&quot; rel=&quot;noopener noreferrer&quot;&gt;Pages headers documentation&lt;/a&gt; describes the format: a URL pattern on one line, then indented &lt;code&gt;Name: value&lt;/code&gt; lines. You may define up to 100 header rules.&lt;/p&gt;
&lt;p&gt;The rule for every path:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code&gt;/*
  Content-Security-Policy: default-src &amp;#x27;self&amp;#x27;; img-src &amp;#x27;self&amp;#x27; data:; font-src &amp;#x27;self&amp;#x27;; style-src &amp;#x27;self&amp;#x27;; script-src &amp;#x27;self&amp;#x27;; connect-src &amp;#x27;self&amp;#x27;; base-uri &amp;#x27;none&amp;#x27;; object-src &amp;#x27;none&amp;#x27;; form-action &amp;#x27;self&amp;#x27;; frame-ancestors &amp;#x27;none&amp;#x27;; worker-src &amp;#x27;self&amp;#x27;
  X-Content-Type-Options: nosniff
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
  Strict-Transport-Security: max-age=31536000; includeSubDomains
  Cross-Origin-Opener-Policy: same-origin&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What each does:&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Header&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Purpose&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Only load resources from this origin. No inline script or style, no plugins, no &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag, forms post only here, nobody may frame the site, and background workers may load only from this origin. See &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN&amp;#x27;s CSP reference&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Browsers must respect the declared content type instead of guessing.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;X-Frame-Options: DENY&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Older browsers&amp;#x27; version of &lt;code&gt;frame-ancestors &amp;#x27;none&amp;#x27;&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Referrer-Policy&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Send only the origin, not the full URL, to other sites.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Permissions-Policy&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Disable camera, microphone, geolocation and payment APIs the site never uses.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Strict-Transport-Security&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Browsers use HTTPS only, for a year, including subdomains.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Cross-Origin-Opener-Policy&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Isolate this site&amp;#x27;s browsing context from pages that open it.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Two cautions on HSTS: &lt;code&gt;includeSubDomains&lt;/code&gt; applies to every subdomain, so make sure they all serve HTTPS first. And once browsers have seen the header, removing it does not undo it until &lt;code&gt;max-age&lt;/code&gt; passes.&lt;/p&gt;
&lt;h2 id=&quot;the-csp-has-to-be-true&quot;&gt;The CSP has to be true&lt;/h2&gt;
&lt;p&gt;A strict CSP only works if the site obeys it. That meant rules from the start:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;No inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, except JSON-LD structured data, which browsers do not execute.&lt;/li&gt;&lt;li&gt;No inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;, no &lt;code&gt;style=&lt;/code&gt; attributes, no &lt;code&gt;onclick=&lt;/code&gt; handlers.&lt;/li&gt;&lt;li&gt;Fonts self-hosted. Icons as inline SVG.&lt;/li&gt;&lt;li&gt;The theme toggle and tools are external JavaScript files from the same origin.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;The test suite loads every page in a headless browser and fails on any CSP violation or any request to another host.&lt;/p&gt;
&lt;h2 id=&quot;caching&quot;&gt;Caching&lt;/h2&gt;
&lt;p&gt;HTML is sent with &lt;code&gt;no-cache&lt;/code&gt;, so browsers revalidate and see updates immediately. CSS and JavaScript files have a content hash in their names and are cached for a year, because a changed file gets a new name. Fonts are also cached for a year.&lt;/p&gt;
&lt;h2 id=&quot;the-contact-form&quot;&gt;The contact form&lt;/h2&gt;
&lt;p&gt;The form posts JSON to &lt;code&gt;/api/contact&lt;/code&gt;, a Pages Function. Important detail from the documentation: **custom headers in &lt;code&gt;_headers&lt;/code&gt; are not applied to responses generated by Pages Functions.** The function sets its own response headers.&lt;/p&gt;
&lt;h3 id=&quot;limit-where-functions-run&quot;&gt;Limit where Functions run&lt;/h3&gt;
&lt;p&gt;By default, a Functions project can invoke Functions on every route. A &lt;code&gt;_routes.json&lt;/code&gt; file controls this. The &lt;a href=&quot;https://developers.cloudflare.com/pages/functions/routing/&quot; rel=&quot;noopener noreferrer&quot;&gt;Pages Functions routing documentation&lt;/a&gt; describes it: a &lt;code&gt;version&lt;/code&gt; (currently 1), plus &lt;code&gt;include&lt;/code&gt; and &lt;code&gt;exclude&lt;/code&gt; lists, with no more than 100 rules combined and no more than 100 characters each. Mine includes only &lt;code&gt;/api/*&lt;/code&gt;, so every other request is served as a static file without running code.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code&gt;{
  &amp;quot;version&amp;quot;: 1,
  &amp;quot;include&amp;quot;: [&amp;quot;/api/*&amp;quot;],
  &amp;quot;exclude&amp;quot;: []
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;what-the-function-checks&quot;&gt;What the function checks&lt;/h3&gt;
&lt;p&gt;The handler treats every request as untrusted:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;POST&lt;/code&gt; only; anything else gets &lt;code&gt;405&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt; only; anything else gets &lt;code&gt;415&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;A small body size limit.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;Origin&lt;/code&gt; header must be this site.&lt;/li&gt;&lt;li&gt;A strict schema: known fields only, length limits, a valid email address.&lt;/li&gt;&lt;li&gt;A hidden honeypot field. If it is filled in, the request is accepted and silently discarded.&lt;/li&gt;&lt;li&gt;Control characters and line breaks stripped from fields that go into email headers, which prevents header injection.&lt;/li&gt;&lt;li&gt;The email is sent as plain text, never HTML.&lt;/li&gt;&lt;li&gt;Errors are generic. Message contents are never logged.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;The API key for the email provider lives in an environment variable in Cloudflare, not in the repository. If any required variable is missing, the function returns &lt;code&gt;503&lt;/code&gt; and the page shows the email address instead.&lt;/p&gt;
&lt;h3 id=&quot;rate-limiting&quot;&gt;Rate limiting&lt;/h3&gt;
&lt;p&gt;Abuse protection belongs in front of the function. Cloudflare&amp;#x27;s WAF rate-limiting rules can limit requests to &lt;code&gt;/api/contact&lt;/code&gt; per IP. Configure this in the dashboard.&lt;/p&gt;
&lt;h2 id=&quot;redirects&quot;&gt;Redirects&lt;/h2&gt;
&lt;p&gt;Pages also reads a &lt;code&gt;_redirects&lt;/code&gt; file. The &lt;a href=&quot;https://developers.cloudflare.com/pages/configuration/redirects/&quot; rel=&quot;noopener noreferrer&quot;&gt;redirects documentation&lt;/a&gt; limits it to 2,000 static and 100 dynamic redirects. I use only a handful, all to fixed paths on this domain, so there is no open-redirect risk from user-controlled destinations.&lt;/p&gt;
&lt;h2 id=&quot;securitytxt&quot;&gt;security.txt&lt;/h2&gt;
&lt;p&gt;I publish &lt;code&gt;/.well-known/security.txt&lt;/code&gt; per &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9116&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 9116&lt;/a&gt;, with an &lt;code&gt;Expires&lt;/code&gt; date set by the build script. Some hosts do not serve dot-directories by default, so check the live URL after deploying.&lt;/p&gt;
&lt;h2 id=&quot;what-this-does-not-cover&quot;&gt;What this does not cover&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Your Cloudflare account. Use a strong, unique password and multi-factor authentication. Anyone with that account can change everything above.&lt;/li&gt;&lt;li&gt;Your DNS and registrar accounts, for the same reason.&lt;/li&gt;&lt;li&gt;Your build pipeline. If CI can deploy, protect who can change it.&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;a-checklist-for-your-own-static-site&quot;&gt;A checklist for your own static site&lt;/h2&gt;
&lt;ol&gt;&lt;li&gt;Remove third-party scripts you do not need. Self-host the rest.&lt;/li&gt;&lt;li&gt;Write a &lt;code&gt;_headers&lt;/code&gt; file with a CSP that matches what the site actually loads.&lt;/li&gt;&lt;li&gt;Test it in report-only mode, then enforce.&lt;/li&gt;&lt;li&gt;If you use Functions, restrict them with &lt;code&gt;_routes.json&lt;/code&gt; and set headers in the function.&lt;/li&gt;&lt;li&gt;Validate every form input server-side, and rate-limit.&lt;/li&gt;&lt;li&gt;Publish &lt;code&gt;security.txt&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Check the live headers after every deploy.&lt;/li&gt;&lt;/ol&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Cloudflare Docs, &lt;a href=&quot;https://developers.cloudflare.com/pages/configuration/headers/&quot; rel=&quot;noopener noreferrer&quot;&gt;Pages: Headers&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;Cloudflare Docs, &lt;a href=&quot;https://developers.cloudflare.com/pages/functions/routing/&quot; rel=&quot;noopener noreferrer&quot;&gt;Pages Functions: Routing&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;Cloudflare Docs, &lt;a href=&quot;https://developers.cloudflare.com/pages/configuration/redirects/&quot; rel=&quot;noopener noreferrer&quot;&gt;Pages: Redirects&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;IETF, &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9116&quot; rel=&quot;noopener noreferrer&quot;&gt;RFC 9116&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;W3C, &lt;a href=&quot;https://www.w3.org/TR/CSP3/&quot; rel=&quot;noopener noreferrer&quot;&gt;Content Security Policy Level 3&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;MDN Web Docs, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy&quot; rel=&quot;noopener noreferrer&quot;&gt;Content-Security-Policy&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Securing a small business website: headers, forms, and updates</title>
    <link href="https://dekryptsecurity.com/blog/securing-a-small-business-website/"/>
    <id>https://dekryptsecurity.com/blog/securing-a-small-business-website/</id>
    <updated>2026-10-13T00:00:00Z</updated>
    <published>2026-10-13T00:00:00Z</published>
    <summary>Three things that protect most small business websites: security headers, forms that validate on the server, and a routine for updates.</summary>
    <category term="websites"/>
    <category term="headers"/>
    <category term="forms"/>
    <category term="updates"/>
    <content type="html">&lt;p&gt;A small business website is rarely attacked because someone chose you. It is attacked because automated tools scan the whole internet for common weaknesses, and your site was on the list. That is good news: protecting against automated, opportunistic attacks mostly means getting a few basics right.&lt;/p&gt;
&lt;p&gt;This post covers three of them. Security headers, which take minutes. Forms, which are where most input from strangers arrives. And updates, which are where most small sites actually fail.&lt;/p&gt;
&lt;h2 id=&quot;h-1-security-headers&quot;&gt;1. Security headers&lt;/h2&gt;
&lt;p&gt;Security headers are instructions your server sends to the browser with every page. They cost nothing to add and block several classes of attack outright. The &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html&quot; rel=&quot;noopener noreferrer&quot;&gt;OWASP HTTP Headers Cheat Sheet&lt;/a&gt; is the best single reference.&lt;/p&gt;
&lt;h3 id=&quot;the-headers-to-set&quot;&gt;The headers to set&lt;/h3&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Header&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Suggested value&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;What it does&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Strict-Transport-Security&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;max-age=31536000; includeSubDomains&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Browsers always use HTTPS for your site.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Start with &lt;code&gt;default-src &amp;#x27;self&amp;#x27;&lt;/code&gt; and adjust&lt;/td&gt;&lt;td&gt;Limits where scripts, styles and other resources may load from.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;X-Content-Type-Options&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;nosniff&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Stops browsers guessing file types.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Referrer-Policy&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;strict-origin-when-cross-origin&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Doesn&amp;#x27;t leak full URLs to other sites.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Permissions-Policy&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;camera=(), microphone=(), geolocation=()&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Turns off browser features you don&amp;#x27;t use.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;X-Frame-Options&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;DENY&lt;/code&gt; (or CSP &lt;code&gt;frame-ancestors &amp;#x27;none&amp;#x27;&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Stops other sites framing yours (clickjacking).&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h3 id=&quot;hsts-carefully&quot;&gt;HSTS, carefully&lt;/h3&gt;
&lt;p&gt;Strict-Transport-Security tells browsers to use HTTPS only. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN&amp;#x27;s HSTS page&lt;/a&gt; explains why it matters: without it, the very first visit over plain &lt;code&gt;http://&lt;/code&gt; can be intercepted before your redirect to HTTPS happens. Two cautions. &lt;code&gt;includeSubDomains&lt;/code&gt; applies to every subdomain, so make sure they all work over HTTPS first. And once browsers have seen the header, they will refuse plain HTTP until &lt;code&gt;max-age&lt;/code&gt; runs out, so start with a short value (say, &lt;code&gt;max-age=300&lt;/code&gt;) and increase it once you are sure.&lt;/p&gt;
&lt;h3 id=&quot;csp-gradually&quot;&gt;CSP, gradually&lt;/h3&gt;
&lt;p&gt;A Content-Security-Policy is the most powerful header and the one most likely to break something, especially on sites with plugins, chat widgets or embedded videos. Deploy it first as &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt;, which reports problems in the browser console without blocking anything, fix what it reports, then switch to enforcing.&lt;/p&gt;
&lt;h3 id=&quot;headers-to-remove&quot;&gt;Headers to remove&lt;/h3&gt;
&lt;p&gt;OWASP recommends removing &lt;code&gt;X-Powered-By&lt;/code&gt;, which tells attackers which technology you run. It also recommends &lt;strong&gt;not&lt;/strong&gt; setting the old &lt;code&gt;X-XSS-Protection&lt;/code&gt; header, because in some cases it can create vulnerabilities in otherwise safe sites; use a CSP instead.&lt;/p&gt;
&lt;h3 id=&quot;check-your-headers&quot;&gt;Check your headers&lt;/h3&gt;
&lt;p&gt;You can see your headers in your browser&amp;#x27;s developer tools (Network tab, click the page, look at Response Headers). Or use this short Python script, which needs only the standard library:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;import sys
import urllib.request

EXPECTED = [
    &amp;quot;content-security-policy&amp;quot;,
    &amp;quot;strict-transport-security&amp;quot;,
    &amp;quot;x-content-type-options&amp;quot;,
    &amp;quot;referrer-policy&amp;quot;,
    &amp;quot;permissions-policy&amp;quot;,
]
REMOVE = [&amp;quot;x-powered-by&amp;quot;, &amp;quot;x-xss-protection&amp;quot;]

url = sys.argv[1]
req = urllib.request.Request(url, method=&amp;quot;HEAD&amp;quot;)
with urllib.request.urlopen(req, timeout=10) as resp:
    headers = {k.lower(): v for k, v in resp.headers.items()}

for name in EXPECTED:
    print((&amp;quot;OK      &amp;quot; if name in headers else &amp;quot;MISSING &amp;quot;) + name)
for name in REMOVE:
    if name in headers:
        print(&amp;quot;REMOVE  &amp;quot; + name + &amp;quot;: &amp;quot; + headers[name])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save it as &lt;code&gt;check_headers.py&lt;/code&gt; and run &lt;code&gt;python3 check_headers.py https://yoursite.example/&lt;/code&gt;. Every line should say &lt;code&gt;OK&lt;/code&gt;, and no &lt;code&gt;REMOVE&lt;/code&gt; lines should appear. The &lt;a href=&quot;/tools/security-headers/&quot;&gt;security headers generator&lt;/a&gt; writes the configuration for Cloudflare Pages, nginx, Apache or Caddy.&lt;/p&gt;
&lt;h2 id=&quot;h-2-forms&quot;&gt;2. Forms&lt;/h2&gt;
&lt;p&gt;Every form on your site, whether contact, booking, newsletter or login, accepts input from anyone on the internet. Treat that input as untrusted.&lt;/p&gt;
&lt;h3 id=&quot;validate-on-the-server&quot;&gt;Validate on the server&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot; rel=&quot;noopener noreferrer&quot;&gt;OWASP Input Validation Cheat Sheet&lt;/a&gt; is blunt: client-side validation is bypassable; use it for immediate feedback only. Whatever checks you put in the browser, repeat them on the server:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Accept only the fields you expect. Reject anything else.&lt;/li&gt;&lt;li&gt;Set a maximum length for every field.&lt;/li&gt;&lt;li&gt;Check formats: a real-looking email address, a date that is a date.&lt;/li&gt;&lt;li&gt;Strip line breaks from anything that ends up in an email header, such as the subject or &amp;quot;reply-to&amp;quot;. Otherwise someone can inject extra headers and use your form to send spam.&lt;/li&gt;&lt;li&gt;Send notification emails as plain text, not HTML built from user input.&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;stop-spam-without-annoying-people&quot;&gt;Stop spam without annoying people&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;A &lt;strong&gt;honeypot&lt;/strong&gt; field: a field hidden from people but visible to simple bots. If it is filled in, quietly discard the message.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Rate limiting&lt;/strong&gt;: limit how many submissions one address can make per minute. Most hosts and CDNs offer this.&lt;/li&gt;&lt;li&gt;A &lt;strong&gt;challenge&lt;/strong&gt; if spam continues. &lt;a href=&quot;https://developers.cloudflare.com/turnstile/&quot; rel=&quot;noopener noreferrer&quot;&gt;Cloudflare Turnstile&lt;/a&gt;, for example, is described by Cloudflare as a CAPTCHA alternative that can work without showing visitors a puzzle.&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;dont-collect-what-you-dont-need&quot;&gt;Don&amp;#x27;t collect what you don&amp;#x27;t need&lt;/h3&gt;
&lt;p&gt;Every field you collect is data you have to protect. A contact form needs a name, an email address and a message. Ask for more only if you will use it.&lt;/p&gt;
&lt;h3 id=&quot;keep-secrets-out-of-the-page&quot;&gt;Keep secrets out of the page&lt;/h3&gt;
&lt;p&gt;API keys for email services, payment providers or databases belong on the server, in environment variables. Never in JavaScript sent to the browser, and never committed to your code repository.&lt;/p&gt;
&lt;h2 id=&quot;h-3-updates&quot;&gt;3. Updates&lt;/h2&gt;
&lt;p&gt;For sites built on a CMS such as WordPress, outdated software is the most common way in. Automated tools look for known-vulnerable versions of the CMS, its plugins and its themes.&lt;/p&gt;
&lt;p&gt;CISA publishes a &lt;a href=&quot;https://www.cisa.gov/known-exploited-vulnerabilities-catalog&quot; rel=&quot;noopener noreferrer&quot;&gt;Known Exploited Vulnerabilities catalog&lt;/a&gt;, which it describes as the authoritative source of vulnerabilities that have been exploited in the wild. You don&amp;#x27;t need to read it daily, but it shows why patching matters: these are flaws attackers are already using.&lt;/p&gt;
&lt;h3 id=&quot;a-routine-that-works&quot;&gt;A routine that works&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Turn on automatic updates&lt;/strong&gt; for minor and security releases, if your platform supports them.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Check weekly&lt;/strong&gt; for plugin and theme updates. Apply them after taking a backup.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Remove what you don&amp;#x27;t use.&lt;/strong&gt; A deactivated plugin can still be vulnerable if its files are on the server. Delete it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Choose fewer, well-maintained plugins.&lt;/strong&gt; Check when a plugin was last updated before installing it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Keep the server current&lt;/strong&gt; too: PHP or Node.js versions, and the operating system, if you manage it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Back up before updating&lt;/strong&gt;, and test that you can restore.&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;or-avoid-the-problem&quot;&gt;Or avoid the problem&lt;/h3&gt;
&lt;p&gt;A static site, pre-built HTML files with no server-side code or database, has far less to update. If your site is mostly information pages, it may not need a CMS at all. I wrote about &lt;a href=&quot;/blog/securing-a-static-site-on-cloudflare-pages/&quot;&gt;how this site is set up on Cloudflare Pages&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;accounts-the-fourth-basic&quot;&gt;Accounts: the fourth basic&lt;/h2&gt;
&lt;p&gt;None of this helps if someone logs in to your hosting, domain registrar or CMS admin with a stolen password. Use a password manager, a unique password and two-factor authentication on each of those accounts. See &lt;a href=&quot;/blog/personal-security-setup-in-a-weekend/&quot;&gt;personal security you can finish in a weekend&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;a-one-page-checklist&quot;&gt;A one-page checklist&lt;/h2&gt;
&lt;ol&gt;&lt;li&gt;HTTPS everywhere, with HSTS.&lt;/li&gt;&lt;li&gt;Security headers set, and checked with the script above.&lt;/li&gt;&lt;li&gt;A CSP, tested in report-only mode first.&lt;/li&gt;&lt;li&gt;&lt;code&gt;X-Powered-By&lt;/code&gt; removed.&lt;/li&gt;&lt;li&gt;Every form validated on the server, with length limits and a honeypot.&lt;/li&gt;&lt;li&gt;Rate limiting on form endpoints.&lt;/li&gt;&lt;li&gt;No secrets in front-end code.&lt;/li&gt;&lt;li&gt;Automatic updates on; weekly check for the rest.&lt;/li&gt;&lt;li&gt;Unused plugins and themes deleted.&lt;/li&gt;&lt;li&gt;2FA on hosting, domain and admin accounts.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;This won&amp;#x27;t stop a determined, targeted attacker. It will stop most of the automated attacks small business sites actually face.&lt;/p&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;OWASP, &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html&quot; rel=&quot;noopener noreferrer&quot;&gt;HTTP Headers Cheat Sheet&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;MDN Web Docs, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security&quot; rel=&quot;noopener noreferrer&quot;&gt;Strict-Transport-Security&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;OWASP, &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Input Validation Cheat Sheet&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;Cloudflare Docs, &lt;a href=&quot;https://developers.cloudflare.com/turnstile/&quot; rel=&quot;noopener noreferrer&quot;&gt;Turnstile&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;CISA, &lt;a href=&quot;https://www.cisa.gov/known-exploited-vulnerabilities-catalog&quot; rel=&quot;noopener noreferrer&quot;&gt;Known Exploited Vulnerabilities Catalog&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Personal security you can finish in a weekend: passwords, 2FA, backups</title>
    <link href="https://dekryptsecurity.com/blog/personal-security-setup-in-a-weekend/"/>
    <id>https://dekryptsecurity.com/blog/personal-security-setup-in-a-weekend/</id>
    <updated>2026-10-13T00:00:00Z</updated>
    <published>2026-10-13T00:00:00Z</published>
    <summary>A practical weekend plan for a password manager, two-factor authentication and a tested backup, based on NIST, CISA and NCSC guidance.</summary>
    <category term="personal security"/>
    <category term="passwords"/>
    <category term="2fa"/>
    <category term="backups"/>
    <content type="html">&lt;p&gt;Most people know they should use a password manager, turn on two-factor authentication and back up their files. Few do all three, because it feels like a big project. It isn&amp;#x27;t. Split into steps, it fits into a weekend, and each step makes you safer on its own.&lt;/p&gt;
&lt;p&gt;This is the plan I&amp;#x27;d walk you through. It is advice for individuals and small teams, not a guarantee: it lowers your risk a lot, but it cannot remove it.&lt;/p&gt;
&lt;h2 id=&quot;saturday-morning-a-password-manager&quot;&gt;Saturday morning: a password manager&lt;/h2&gt;
&lt;h3 id=&quot;why&quot;&gt;Why&lt;/h3&gt;
&lt;p&gt;Reused passwords are the main problem. When one site leaks, attackers try the same email and password everywhere else. The fix is a different password for every account, which is only practical with a password manager.&lt;/p&gt;
&lt;p&gt;US guidance agrees on what makes a password good. NIST&amp;#x27;s digital identity guidelines, &lt;a href=&quot;https://pages.nist.gov/800-63-4/sp800-63b.html&quot; rel=&quot;noopener noreferrer&quot;&gt;SP 800-63B&lt;/a&gt;, tell services not to impose composition rules (such as &amp;quot;one symbol, one number&amp;quot;) and not to force periodic password changes, only to require a change when there is evidence of compromise. Length and randomness matter more. CISA&amp;#x27;s advice to the public, &lt;a href=&quot;https://www.cisa.gov/secure-our-world/use-strong-passwords&quot; rel=&quot;noopener noreferrer&quot;&gt;Use Strong Passwords&lt;/a&gt;, is: long (at least 16 characters), random, and unique, created with a password manager.&lt;/p&gt;
&lt;h3 id=&quot;steps&quot;&gt;Steps&lt;/h3&gt;
&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Pick a password manager.&lt;/strong&gt; Use a well-known one that works on all your devices. The one built into your phone or browser is fine to start with if it syncs everywhere you need it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Create one strong master password.&lt;/strong&gt; This is the one password you remember. A passphrase of several random words is easier to type and remember than a jumble of symbols. My &lt;a href=&quot;/tools/password/&quot;&gt;password and passphrase generator&lt;/a&gt; creates one in your browser; nothing leaves your device.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Write the master password down once&lt;/strong&gt; and keep the paper somewhere safe at home until you have memorised it. A paper note in a drawer is far safer than a reused password.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install the manager&lt;/strong&gt; on your phone and computer and turn on autofill.&lt;/li&gt;&lt;/ol&gt;
&lt;h3 id=&quot;change-the-important-passwords-first&quot;&gt;Change the important passwords first&lt;/h3&gt;
&lt;p&gt;Do not try to change every account today. Start with the accounts that unlock the others:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Your &lt;strong&gt;main email&lt;/strong&gt; account. Whoever controls it can reset almost every other password.&lt;/li&gt;&lt;li&gt;Your &lt;strong&gt;phone&amp;#x27;s account&lt;/strong&gt; (Apple ID or Google account).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Banking and payment&lt;/strong&gt; apps.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Cloud storage&lt;/strong&gt;, where your photos and documents live.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Work accounts&lt;/strong&gt; and anything with admin rights.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;For each, let the manager generate a new password, save it, and log in again to check it works. Change the rest as you log in to them over the following weeks.&lt;/p&gt;
&lt;h2 id=&quot;saturday-afternoon-two-factor-authentication&quot;&gt;Saturday afternoon: two-factor authentication&lt;/h2&gt;
&lt;h3 id=&quot;why-1&quot;&gt;Why&lt;/h3&gt;
&lt;p&gt;Two-factor (or multi-factor) authentication means a stolen password is not enough on its own. &lt;a href=&quot;https://www.cisa.gov/MFA&quot; rel=&quot;noopener noreferrer&quot;&gt;CISA&amp;#x27;s MFA guidance&lt;/a&gt; lists the common forms, from text-message codes through authenticator apps to phishing-resistant methods, and recommends turning it on first for email, financial services, social media, and online stores.&lt;/p&gt;
&lt;h3 id=&quot;which-kind&quot;&gt;Which kind&lt;/h3&gt;
&lt;p&gt;From weakest to strongest, roughly:&lt;/p&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Method&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Notes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;SMS or voice code&lt;/td&gt;&lt;td&gt;Much better than nothing. Can be intercepted or redirected if someone takes over your number.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Authenticator app&lt;/td&gt;&lt;td&gt;Codes generated on your phone. A good default.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Push approval&lt;/td&gt;&lt;td&gt;Convenient. Only approve prompts you started yourself.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Passkey or security key&lt;/td&gt;&lt;td&gt;Phishing-resistant: it will not work on a fake site. Best where supported.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Use the strongest method each service offers that you can manage reliably.&lt;/p&gt;
&lt;h3 id=&quot;steps-1&quot;&gt;Steps&lt;/h3&gt;
&lt;ol&gt;&lt;li&gt;Turn on 2FA for the same priority list: email, phone account, banking, cloud storage, work.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Save the recovery codes&lt;/strong&gt; each service gives you. Store them in your password manager, and print the codes for your main email account and keep them with your master password note.&lt;/li&gt;&lt;li&gt;If you use an authenticator app, check whether it can back up to a new phone. If it can&amp;#x27;t, losing your phone means using recovery codes for every account.&lt;/li&gt;&lt;/ol&gt;
&lt;h2 id=&quot;sunday-backups&quot;&gt;Sunday: backups&lt;/h2&gt;
&lt;h3 id=&quot;why-2&quot;&gt;Why&lt;/h3&gt;
&lt;p&gt;Phones get lost, laptops die, and ransomware encrypts files. A backup is the only thing that fixes all three.&lt;/p&gt;
&lt;p&gt;A simple rule from CISA&amp;#x27;s paper on &lt;a href=&quot;https://www.cisa.gov/sites/default/files/publications/data_backup_options.pdf&quot; rel=&quot;noopener noreferrer&quot;&gt;data backup options&lt;/a&gt; is &lt;strong&gt;3-2-1&lt;/strong&gt;: keep 3 copies of important files (the original and 2 backups), on 2 different types of media, with 1 copy stored offsite. The UK&amp;#x27;s NCSC &lt;a href=&quot;https://www.ncsc.gov.uk/collection/top-tips-for-staying-secure-online/always-back-up-your-most-important-data&quot; rel=&quot;noopener noreferrer&quot;&gt;backup guidance&lt;/a&gt; puts the scope simply: back up anything that would inconvenience you if you could no longer access it.&lt;/p&gt;
&lt;h3 id=&quot;a-practical-3-2-1-for-one-person&quot;&gt;A practical 3-2-1 for one person&lt;/h3&gt;
&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Original:&lt;/strong&gt; your phone and laptop.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Backup 1, cloud:&lt;/strong&gt; turn on your phone&amp;#x27;s cloud backup and a cloud sync for your documents and photos. It runs automatically.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Backup 2, offline:&lt;/strong&gt; an external drive. Copy your important folders to it once a month, then &lt;strong&gt;unplug it&lt;/strong&gt;. A drive that is always connected can be encrypted by ransomware along with everything else.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;The offline drive at home and the cloud copy elsewhere give you the &amp;quot;1 offsite&amp;quot;.&lt;/p&gt;
&lt;h3 id=&quot;test-a-restore&quot;&gt;Test a restore&lt;/h3&gt;
&lt;p&gt;A backup you have never restored is a hope, not a backup. Pick one file from each backup and open it on another device. If you can&amp;#x27;t, fix it now.&lt;/p&gt;
&lt;p&gt;For files you copy to a drive, you can check they arrived intact by comparing checksums. On Linux or macOS:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ~/Documents
find . -type f -exec shasum -a 256 {} + | sort -k 2 &amp;gt; /tmp/originals.txt
cd /path/to/backup/Documents
find . -type f -exec shasum -a 256 {} + | sort -k 2 &amp;gt; /tmp/backup.txt
diff /tmp/originals.txt /tmp/backup.txt &amp;amp;&amp;amp; echo &amp;quot;Backup matches&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No output from &lt;code&gt;diff&lt;/code&gt; and &amp;quot;Backup matches&amp;quot; means every file is identical. The &lt;a href=&quot;/tools/hash/&quot;&gt;hash and file checksum tool&lt;/a&gt; does the same for single files in your browser.&lt;/p&gt;
&lt;h2 id=&quot;sunday-evening-quick-wins&quot;&gt;Sunday evening: quick wins&lt;/h2&gt;
&lt;p&gt;If you have time left:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Turn on automatic updates&lt;/strong&gt; on your phone, computer and browser.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Lock screens&lt;/strong&gt; with a PIN or biometrics.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Remove apps and browser extensions&lt;/strong&gt; you no longer use.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Check account recovery details&lt;/strong&gt;: make sure the recovery phone number and email on your main accounts are current.&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;what-this-does-not-cover&quot;&gt;What this does not cover&lt;/h2&gt;
&lt;p&gt;This setup protects against the most common problems: reused passwords, stolen passwords, and lost files. It does not protect against a targeted attacker, a compromised device, or every scam. If you think an account has already been taken over, use the service&amp;#x27;s official account recovery process first.&lt;/p&gt;
&lt;p&gt;If you would like help doing this on your own devices, that is what the &lt;a href=&quot;/personal-security/&quot;&gt;personal security&lt;/a&gt; setup sessions are for.&lt;/p&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;NIST, &lt;a href=&quot;https://pages.nist.gov/800-63-4/sp800-63b.html&quot; rel=&quot;noopener noreferrer&quot;&gt;SP 800-63B: Digital Identity Guidelines, Authentication and Authenticator Management&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;CISA, &lt;a href=&quot;https://www.cisa.gov/secure-our-world/use-strong-passwords&quot; rel=&quot;noopener noreferrer&quot;&gt;Use Strong Passwords&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;CISA, &lt;a href=&quot;https://www.cisa.gov/MFA&quot; rel=&quot;noopener noreferrer&quot;&gt;More than a Password (MFA)&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;CISA, &lt;a href=&quot;https://www.cisa.gov/sites/default/files/publications/data_backup_options.pdf&quot; rel=&quot;noopener noreferrer&quot;&gt;Data Backup Options&lt;/a&gt; (PDF). Last verified 2026-10-13.&lt;/li&gt;&lt;li&gt;NCSC (UK), &lt;a href=&quot;https://www.ncsc.gov.uk/collection/top-tips-for-staying-secure-online/always-back-up-your-most-important-data&quot; rel=&quot;noopener noreferrer&quot;&gt;Back up your most important data&lt;/a&gt;. Last verified 2026-10-13.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
</feed>
