Website security
How this site is secured
We advise clients on their Azure and data security, so it is fair to ask what we do for our own website. This page says so in terms you can check yourself, against the live site. It ends with how to report a weakness if you find one.
What this site is
Files, not an application
Every page is a pre-built HTML file, generated from a private repository and published as a whole. There is no content management system serving pages, no database and no server-side code behind them. The one piece of code that runs on the server is the small function that receives the forms. The editor at /admin/ is not part of the public site: it requires a Microsoft Entra sign-in and an editor role, and it changes the repository, not the live pages.
You can read every page with JavaScript switched off. Scripts are used for the menu, the language switch, the interactive tools and the forms, and each page keeps working as a document without them.
The forms are the one place where something leaves your browser. They post to a function on this site, which checks the submission and relays it through FormSubmit, a relay service, to our mailbox. Nothing is stored on this site.
Response headers
What every response carries
These headers are sent with every page and asset. The first command under Check it yourself shows them.
| Header | Value | What it does |
|---|---|---|
| Strict-Transport-Security | max-age=10886400; includeSubDomains; preload | A browser that has seen the site once refuses to load it over plain HTTP. |
| X-Content-Type-Options | nosniff | A file is treated as the type we say it is, never guessed from its contents. |
| X-Frame-Options | SAMEORIGIN | Another site cannot embed these pages in a frame. |
| Referrer-Policy | strict-origin-when-cross-origin | A site you leave for sees that you came from this domain, never which page. |
| Permissions-Policy | camera=(), microphone=(), geolocation=() | The site cannot ask for your camera, microphone or location. |
| X-DNS-Prefetch-Control | off | The browser does not look up other domains ahead of time on our behalf. |
| X-XSS-Protection | 1; mode=block | A legacy header for older browsers. Current browsers ignore it. |
Transport
Encrypted, and only encrypted
- Plain HTTP is answered with a permanent redirect to HTTPS.
- The site negotiates TLS 1.2 or TLS 1.3 and answers a TLS 1.0 or 1.1 attempt with a protocol version alert. That is the Azure Static Web Apps default, fixed by the platform.
- The certificate is issued by DigiCert and renewed automatically by the hosting platform.
- Mail from masterminds.io is protected by SPF with a hard fail and by DMARC set to reject, so a message that claims to come from us and fails those checks is refused by receiving servers that honour the policy.
Third parties
External services, and what they see
The site uses two common analytics systems, Microsoft Clarity and Google Analytics, to see how its pages are used. When you send a form, the form relay FormSubmit receives what you typed and delivers it to our mailbox. The Analytics Health Check flyer form is a HubSpot form: it loads from HubSpot in a frame, uses Google reCAPTCHA, and what you type in it goes to HubSpot, where it is stored.
The PT Sans Narrow typeface is served from this domain, so no font request leaves it.
Forms
What happens when you send a form
- The form posts to a function on this site, which relays the message through FormSubmit to our mailbox. Nothing is stored on this site.
- A hidden field that only automated senders fill makes the function drop the submission without relaying it.
- When you start on a form the page fetches a signed, single-use token from the function. The token has to be at least three seconds old when the form is posted, and a submission without a valid one is refused. A form sends once per page view.
- Each form carries the number of seconds the page had been open, so an instant submission is visible in the mailbox.
- The careers application form is the exception: it carries file uploads, so it posts to FormSubmit directly with the browser-side checks only.
- The Analytics Health Check flyer form is the other exception: it is a HubSpot form, and HubSpot receives, checks and stores what you send.
Hosting and publishing
Published as a whole, from one place
- The site runs on Azure Static Web Apps, which serves the files, runs the form function and terminates TLS.
- The app is deployed in an Azure region in the European Union; its pages are served from Microsoft's global edge network, so a visitor anywhere is answered from the nearest location.
- Each release is built by GitHub Actions from the repository and replaces the whole site. Nothing is edited on the server by hand.
- Publishing needs a deployment token held as a repository secret. There is no FTP, no deployment password and no server to log in to.
- The editor at
/admin/is served only to accounts that sign in with Microsoft Entra and hold the editor role.
The pages are generated from plain text files in the repository. Build dependencies are pinned in its lock file and run only at build time. At run time the pages load no library from a content delivery network.
Check it yourself
Nothing above needs our word
Each line below runs from a terminal on Windows, macOS or Linux.
curl -I https://masterminds.io/The response headers listed above.
curl -I http://masterminds.io/A 301 to the HTTPS address.
openssl s_client -connect masterminds.io:443 -servername masterminds.io -tls1_2 </dev/nullA completed TLS 1.2 handshake and the DigiCert issuer. Replace
-tls1_2with-tls1_3for the TLS 1.3 handshake.curl -s https://masterminds.io/api/challengeA JSON token, different every time. Posted back to
/api/submitwithin three seconds, the function answerstoo_fast.nslookup -type=TXT _dmarc.masterminds.ioThe DMARC record with
p=reject.nslookup -type=TXT masterminds.ioThe SPF record ending in
-all.
Reporting a weakness
If you find something, we want to hear it first
Security researchers and curious visitors are welcome to look. If you find a weakness in this site, tell us through the form or by email to sanderstad@masterminds.io. We acknowledge every report and tell you what we did about it.
What helps
- The address of the page or file, and the steps to reproduce what you saw.
- What you expected and what happened instead.
- Whether you have shared it anywhere else.
- A way to reach you, if you would like to hear back. A report without one is still welcome.
What we ask
- Do not access, change or download data that is not yours beyond the minimum needed to show the problem.
- Do not run denial-of-service tests, and do not test the third-party services this site relies on. They have their own programmes.
- Give us the chance to fix it before you publish.
The same contact details are published in machine-readable form at /.well-known/security.txt.