How the security rating is computed
Version 1, in force since 15 October 2026. Every rule that moves a score is on this page, and the tables are rendered from the same definitions the scoring engine runs.
The scale
- F0–200
- D201–400
- C401–600
- B601–800
- A801–950
A security rating is the result of applying a published rule set to evidence gathered by passive observation: the public DNS records of a domain, the registrar’s public record, one TLS handshake per protocol version with its web servers, one request for the landing page over HTTP and over HTTPS, and public data sources — Shodan’s InternetDB for open ports and known vulnerabilities, certificate-transparency logs, DNS blocklists.
Nothing is probed beyond the landing page, no port is scanned by us, and nothing is sent to the observed system that a browser or a mail server would not send. The rules below are the whole method: every point a score loses is listed here with its cost, and a change to the rules is a new version, dated, that never rewrites an earlier score.
- A domain starts at 950 and loses points for each failed check. The floor is 0.
- Every check has a tier — critical, high, medium, low or informational — with a fixed cost. Informational checks are shown and never cost anything.
- The overall score is computed on every failed check, with no cap per category. Category scores are shown for orientation and do not feed the overall score; a domain with many problems loses the full amount for each.
- An entity with several hosts or domains is pulled toward its worst asset: the score is the average, pulled halfway toward the minimum, so a bad host cannot hide behind good ones.
- A score always comes with its coverage: how many checks were evaluated, how many this version does not cover, and how many could not be observed. A score with twenty checks not observed is a weaker claim than one with two, and the page that shows it says so.
Statuses
Every check of every observation ends in one of these, and the coverage line under a score counts them.
| Status | Meaning | Effect on the score |
|---|---|---|
| pass | observed, rule satisfied | none |
| fail | observed, rule violated; evidence attached | minus the tier cost |
| info | observed, informational | none; shown |
| not covered | this version does not implement the check | none; counted in coverage |
| not observed | could not be observed inside the allowed means: no public-source data, host unresolvable, a source refused | none; counted in coverage, shown as a gap |
| not applicable | the check’s premise is absent — the HSTS details of a host with no HTTPS, the SPF qualifier of a domain with no SPF record | none; counted in coverage, not a gap |
| error | our observer failed: a timeout, our bug | none; the observation is marked partial |
Provisional checks are evaluated and shown with their tier but cost nothing, for at least one method version, so that a reader sees them before they move the number.
Why these costs
No rating vendor publishes per-check costs, and a regression over 396 public reports of one of them recovers only half the variance, so there was nothing to copy. These tiers are decided, not measured, and the reasoning for the most expensive ones is written down so that an auditor can argue with it. What the regression does support is the ordering: a missing DMARC record, no HTTPS, legacy TLS, untrusted certificates and end-of-life software sit at the top; single headers, cookie flags and open standard ports at the bottom.
| Check | Tier | Why |
|---|---|---|
| ENC-1HTTPS is available | critical | No transport security at all; every other TLS check is moot. The single largest cost in the regression. |
| ENC-2Certificate chain is trusted | critical | An untrusted chain is indistinguishable from an interception; browsers warn and users click through. |
| ENC-4Certificate not expired | critical | Browsers block the page; the site is effectively down for careful users and open for careless ones. |
| EML-3SPF does not end in +all | critical | Authorises every sender on the internet to mail as the domain. |
| REP-1Domain not listed on Spamhaus DBL or SURBL | critical | An active abuse signal, not a configuration gap. |
| REP-3Not flagged by Google Safe Browsing | critical | The domain is currently serving something malicious. |
| NET-3No database port exposed (1433, 1521, 3306, 5432, 6379, 9200, 27017, 11211) | critical | Direct exposure of data; the port class most correlated with breach in the public vendor methodologies. |
| NET-2No telnet (23) exposed | critical | Clear-text credentials on a management channel. |
| VUL-2Server software not end-of-life | critical | No security fixes will ever arrive; the second-largest cost in the regression. |
| EML-7DMARC record present | high | The regression’s largest email cost; high rather than critical because SPF may still exist. |
Every other tier assignment follows the same two questions: what can an attacker do with it today, and does the regression over real domains agree with the ordering.
Not in this version
Dark-web and credential-leak monitoring, brand protection (typosquats, fake apps), questionnaires beyond the seven-item attestation below, operational risk as a category, cipher-suite enumeration beyond one weak-list handshake, per-vulnerability severity weighting. Each is listed as not covered where it would appear, and counted in the coverage of every score.
Where the observation comes from
Observation runs from our own worker, hosted in the European Union (Amsterdam), on a shared outbound address. For one domain it makes the DNS lookups listed above, one registrar lookup, at most six TLS handshakes per web host, two requests for the landing page, and the public-source queries — once a day at most, and never more than five hosts per domain.
What is observed is kept with the score as evidence: the record, the certificate dates, the header values. Response bodies are not kept.
Opting out
A domain owner who does not want their public posture observed by this product can ask for the domain to be excluded. Write to hello@tacans.com naming the domain. The request is honoured, and the domain is shown as “not observed at the owner’s request” to every customer who registered it.
The rules
Method version 1. Every domain starts at 950; each failed check subtracts its tier cost; the floor is 0.
| Tier | Cost |
|---|---|
| critical | 100 |
| high | 50 |
| medium | 20 |
| low | 6 |
| info | 0 |
Roll-up
domain_loss = Σ domain-level costs + 0.5 × max(host_loss) + 0.5 × mean(host_loss) domain_score = max(0, 950 − domain_loss) entity_score = 0.5 × min(domain_score) + 0.5 × mean(domain_score)
Worst case per category, counting each mutually exclusive group once. Informational: the overall score has no per-category cap.
| Category | Checks | Covered | Worst-case loss |
|---|---|---|---|
| 12 | 12 | 302 | |
| DNS | 9 | 9 | 134 |
| Encryption | 19 | 19 | 728 |
| Website | 16 | 16 | 226 |
| Network | 6 | 6 | 306 |
| Vulnerability Management | 3 | 2 | 150 |
| Attack Surface | 4 | 4 | 100 |
| IP/Domain Reputation | 3 | 3 | 250 |
| Data Leakage | 2 | 1 | 20 |
| Operational Risk | 0 | 0 | 0 |
Email12 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| EML-1 | SPF record present | domain | high | 50 | DNS TXT | Publish an SPF record naming the senders allowed for this domain. |
| EML-2 | SPF syntax valid (one record, ≤ 10 lookups) | domain | medium | 20 | DNS TXT | Keep one SPF record and at most ten DNS lookups; receivers ignore a broken record. |
| EML-3 | SPF does not end in +all | domain | critical | 100 | DNS TXT | Replace `+all` — it authorises every sender on the internet to mail as this domain. |
| EML-4 | SPF does not end in ?all | domain | high | 50 | DNS TXT | Replace `?all` with `-all` (or `~all` while DMARC is being rolled out). |
| EML-5 | SPF ends in -all rather than ~all | domain | low | 6 | DNS TXT | Move to `-all` once DMARC reporting shows no legitimate sender is missing. |
| EML-6 | SPF has no ptr mechanism | domain | low | 6 | DNS TXT | Remove `ptr`; it is deprecated and slow, and some receivers ignore records that use it. |
| EML-7 | DMARC record present | domain | high | 50 | DNS TXT _dmarc | Publish `_dmarc` with at least `p=none` and a reporting address, then tighten. |
| EML-8 | DMARC policy is not p=none | domain | high | 50 | DNS TXT _dmarc | Move the policy to `p=quarantine`, then `p=reject`; `p=none` protects nobody. |
| EML-9 | DMARC policy is p=reject | domain | low | 6 | DNS TXT _dmarc | Move from `p=quarantine` to `p=reject` once reports are clean. |
| EML-10 | DMARC pct is 100 | domain | low | 6 | DNS TXT _dmarc | Remove `pct` or set it to 100; a partial policy leaves a share of mail unprotected. |
| EML-11 | DMARC has a reporting address (rua) | domain | info | 0 | DNS TXT _dmarc | Add `rua=` so spoofing attempts are reported to you. |
| EML-12 | Every MX host resolves | domain | medium | 20 | DNS MX + A/AAAA | Remove or fix MX records that point at hosts that no longer exist. |
DNS9 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| DNS-1 | DNSSEC enabled | domain | low | 6 | DNS DNSKEY/DS | Sign the zone and publish the DS record at the registrar. |
| DNS-2 | CAA record present | domain | low | 6 | DNS CAA | Publish a CAA record naming the certificate authorities allowed to issue for this domain. |
| DNS-3 | Registrar transfer lock set | domain | medium | 20 | RDAP status | Enable `clientTransferProhibited` at the registrar. |
| DNS-4 | Registrar delete lock set | domain | low | 6 | RDAP status | Enable `clientDeleteProhibited` at the registrar. |
| DNS-5 | Registrar update lock set | domain | low | 6 | RDAP status | Enable `clientUpdateProhibited` at the registrar. |
| DNS-6 | Domain does not expire within 30 days | domain | high | 50 | RDAP expiry | Renew the domain now and enable auto-renewal. |
| DNS-7 | Domain does not expire within 90 days | domain | low | 6 | RDAP expiry | Renew the domain or confirm auto-renewal is on. |
| DNS-8 | Every NS host resolves | domain | medium | 20 | DNS NS + A/AAAA | Remove or fix nameserver records that point at hosts that do not resolve. |
| DNS-9 | Domain not on a registry or registrar hold | domain | medium | 20 | RDAP status | Resolve the hold with the registrar; a held domain can stop resolving without notice. |
Encryption19 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| ENC-1 | HTTPS is available | host | critical | 100 | TLS handshake | Serve the site over HTTPS with a certificate from a trusted authority. |
| ENC-2 | Certificate chain is trusted | host | critical | 100 | TLS handshake | Install the full chain from a publicly trusted authority; a self-signed or orphaned certificate is indistinguishable from an interception. |
| ENC-3 | Hostname matches the certificate | host | high | 50 | TLS handshake | Issue a certificate whose names include this host (or a matching wildcard). |
| ENC-4 | Certificate not expired | host | critical | 100 | TLS handshake | Renew the certificate and automate renewal. |
| ENC-5 | Certificate not expiring within 20 days | host | medium | 20 | TLS handshake | Renew before expiry; automate renewal so this never recurs. |
| ENC-6 | Certificate validity ≤ 398 days | host | low | 6 | TLS handshake | Issue certificates for at most 398 days; browsers distrust longer ones. |
| ENC-7 | Key strength ≥ RSA 2048 / EC 256 | host | high | 50 | TLS handshake | Re-issue with an RSA ≥ 2048-bit or EC ≥ 256-bit key. |
| ENC-8 | Signature algorithm not SHA-1 or MD5 | host | high | 50 | TLS handshake | Re-issue with a SHA-256 (or stronger) signature. |
| ENC-9 | TLS 1.0 not offered | host | medium | 20 | TLS handshake per protocol | Disable TLS 1.0 on the server. |
| ENC-10 | TLS 1.1 not offered | host | low | 6 | TLS handshake per protocol | Disable TLS 1.1 on the server. |
| ENC-11 | SSL 2 / SSL 3 not offered | host | high | 50 | TLS handshake per protocol | Disable SSLv2 and SSLv3; both are broken. |
| ENC-12 | TLS 1.2 or 1.3 offered | host | high | 50 | TLS handshake per protocol | Enable TLS 1.2 and 1.3; modern clients refuse anything older. |
| ENC-13 | HTTP redirects to HTTPS | host | high | 50 | GET / over HTTP | Redirect every HTTP request to HTTPS with a 301. |
| ENC-14 | HTTPS does not redirect to HTTP | host | high | 50 | GET / over HTTPS | Never downgrade a secure request; fix the redirect target. |
| ENC-15 | HSTS header present | host | medium | 20 | GET / over HTTPS | Send `Strict-Transport-Security` on every HTTPS response. |
| ENC-16 | HSTS max-age ≥ 6 months | host | low | 6 | GET / over HTTPS | Set `max-age` to at least 15552000 seconds. |
| ENC-17 | HSTS includes subdomains | host | low | 6 | GET / over HTTPS | Add `includeSubDomains` once every subdomain serves HTTPS. |
| ENC-18 | Domain on the HSTS preload list | host | info | 0 | HSTS preload status (public) | Submit the domain at hstspreload.org once HSTS is enforced with preload. |
| ENC-19 | No known-weak cipher accepted (RC4, 3DES, NULL, EXPORT) | host | medium | 20 | TLS handshake with a weak-cipher list | Remove RC4, 3DES, NULL and EXPORT suites from the server configuration. |
Website16 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| WEB-1 | Content-Security-Policy present | host | medium | 20 | Response headers | Send a Content-Security-Policy; start with `default-src 'self'` and widen deliberately. |
| WEB-2 | CSP has no unsafe-inline in script-src without a nonce or hash | host | medium | 20 | Response headers | Replace inline scripts with nonces or hashes. |
| WEB-3 | CSP has no unsafe-eval | host | low | 6 | Response headers | Remove `unsafe-eval`; refactor code that needs `eval`. |
| WEB-4 | Clickjacking protection (X-Frame-Options or frame-ancestors) | host | medium | 20 | Response headers | Send `X-Frame-Options: DENY` or a CSP `frame-ancestors` directive. |
| WEB-5 | X-Content-Type-Options: nosniff | host | low | 6 | Response headers | Send `X-Content-Type-Options: nosniff`. |
| WEB-6 | Referrer-Policy is not unsafe-url | host | low | 6 | Response headers | Send `Referrer-Policy: strict-origin-when-cross-origin` or stricter. |
| WEB-7 | Server header carries no version | host | low | 6 | Response headers | Remove the version from the `Server` header. |
| WEB-8 | X-Powered-By absent | host | low | 6 | Response headers | Remove the `X-Powered-By` header. |
| WEB-9 | X-AspNet-Version / X-AspNetMvc-Version absent | host | medium | 20 | Response headers | Disable version headers in the ASP.NET configuration. |
| WEB-10 | Session cookies have Secure | host | high | 50 | Set-Cookie on / | Set the `Secure` attribute on every session cookie. |
| WEB-11 | Session cookies have HttpOnly | host | medium | 20 | Set-Cookie on / | Set `HttpOnly` on session cookies so scripts cannot read them. |
| WEB-12 | Cookies declare SameSite | host | low | 6 | Set-Cookie on / | Set `SameSite=Lax` or `Strict` on every cookie. |
| WEB-13 | Landing page responds 2xx or 3xx | host | low | 6 | GET / over HTTPS | Serve a real page at `/`; an error page is an unmaintained surface. |
| WEB-14 | No mixed content on the landing page | host | medium | 20 | HTML of / | Load every subresource over HTTPS, or send `upgrade-insecure-requests`. |
| WEB-15 | Permissions-Policy present | host | info | 0 | Response headers | Send a `Permissions-Policy` that disables features the site does not use. |
| WEB-16 | CORS does not allow * with credentials | host | medium | 20 | Response headers | Never combine `Access-Control-Allow-Origin: *` with `Access-Control-Allow-Credentials: true`. |
Network6 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| NET-1 | No remote-management port exposed (22, 3389, 5900, 5985/5986) | host | high | 50 | Shodan InternetDB | Put remote management behind a VPN or an allowlist; close it on the public interface. |
| NET-2 | No telnet (23) exposed | host | critical | 100 | Shodan InternetDB | Disable telnet; it sends credentials in clear text. |
| NET-3 | No database port exposed (1433, 1521, 3306, 5432, 6379, 9200, 27017, 11211) | host | critical | 100 | Shodan InternetDB | Bind databases to private interfaces; reach them through the application or a VPN. |
| NET-4 | No file-sharing port exposed (21, 445, 2049) | host | high | 50 | Shodan InternetDB | Close FTP, SMB and NFS on the public interface; use SFTP behind authentication if needed. |
| NET-5 | No plaintext mail-retrieval port (110, 143) on a web host | host | low | 6 | Shodan InternetDB | Serve mail retrieval over TLS (995, 993) and from a mail host, not the web host. |
| NET-6 | Open ports beyond 80 and 443 listed | host | info | 0 | Shodan InternetDB | Review every listed port; close what the host does not need. |
Vulnerability Management3 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| VUL-1 | No known CVEs reported on the exposed services | host | high | 50 | Shodan InternetDB vulns | Patch or upgrade the affected service; re-observe to confirm. |
| VUL-2 | Server software not end-of-life | host | critical | 100 | Response headers + endoflife.date | Upgrade to a supported release; end-of-life software receives no security fixes. |
| VUL-3 | No known-vulnerable JavaScript libraries on the landing page | host | high (not covered) | 0 | HTML of / (retire.js rules) | Upgrade the library to a release without known vulnerabilities. |
Attack Surface4 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| ASF-1 | No dangling subdomain (CNAME to an unclaimed service) | domain | high | 50 | CT search + DNS CNAME | Remove DNS records that point at services you no longer own; they can be claimed by anyone. |
| ASF-2 | No admin interface exposed on an observed host | host | high | 50 | Shodan InternetDB tags | Put administrative interfaces behind a VPN or an allowlist. |
| ASF-3 | Subdomains observed in certificate-transparency logs | domain | info | 0 | CT search | Review the list; decommission what is not meant to exist. |
| ASF-4 | Wildcard DNS present | domain | info | 0 | DNS | Prefer explicit records; a wildcard answers for names you never created. |
IP/Domain Reputation3 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| REP-1 | Domain not listed on Spamhaus DBL or SURBL | domain | critical | 100 | DNSBL | Find and stop the abuse, then request delisting. |
| REP-2 | Resolved IPs not listed on Spamhaus ZEN | host | high | 50 | DNSBL | Clean the host and request delisting; a shared IP may need a move. |
| REP-3 | Not flagged by Google Safe Browsing | domain | critical | 100 | Safe Browsing API (optional key) | Remove the malicious content and request a review in Search Console. |
Data Leakage2 checks
| Id | Check | Level | Tier | Cost | Source | Remediation |
|---|---|---|---|---|---|---|
| LEK-1 | Landing page is not a directory listing | host | medium | 20 | HTML of / | Disable directory listing on the web server. |
| LEK-2 | No credential-shaped strings in landing-page HTML or JavaScript | host | high (not covered) | 0 | HTML of / | Rotate the exposed credential and move it out of client-side code. |
Operational Risknot covered in this version
Attestation (version 1) and the blend
Observation measures what the public internet sees. An attestation records what a person at the customer has been shown about a supplier — certificates on file, clauses in the contract, a named contact, a recent test — answered item by item from the evidence, not from expectation, and recorded under that person’s name with the date.
An attested supplier carries a third number beside the two: the blend, half observation and half attestation, labelled as blended wherever it appears. Items answered as not applicable leave the denominator, so a supplier that never touches card data is not marked down for having no PCI attestation; an unanswered attestation is an absence, never a zero. The observation score and its history are untouched by any attestation.
| Id | Item | What counts as seen | Points |
|---|---|---|---|
| ATT-1 | ISO/IEC 27001 certificate on file | A current certificate from an accredited body, with its scope statement covering the service you buy. | 200 |
| ATT-2 | SOC 2 Type II report on file | A report dated within the last twelve months, read for exceptions. | 200 |
| ATT-3 | PCI DSS attestation on file | An AOC or SAQ dated within the last twelve months; not applicable where the supplier never touches card data. | 100 |
| ATT-4 | A named security contact | A person or a monitored mailbox you have reached at least once. | 50 |
| ATT-5 | Incident notification clause in the contract | The contract names a deadline for telling you about a breach that touches your data. | 150 |
| ATT-6 | Data processing agreement in place | Signed, naming the processing and the sub-processors; not applicable where no personal data is processed. | 100 |
| ATT-7 | Independent penetration test in the last twelve months | A report or an attestation letter from the tester, with the scope. | 150 |
attestation_score = 950 × Σ points(yes) / Σ points(applicable) (null when nothing applicable is answered) blended_score = 0.5 × observation_score + 0.5 × attestation_score (only when both exist)
Version history
A change to any rule is a new version. A score keeps the version it was computed with, and history is never rescored.
| Version | Date | Change |
|---|---|---|
| 1 | 2026-10-15 | First published method. |