MigrateSafe
Read-only security audit for domain migrations.
When a web platform moves from old.example to new.example, there is a window in which two names,
two hosting environments and two sets of DNS, TLS and e-mail settings coexist. MigrateSafe checks
that window for dangling records, certificate anomalies, transport/session downgrades, spoofable mail,
broken redirects and configuration drift.
It implements the fourteen verification checks (C1–C14) from:
A. A. More, “MigrateSafe: A threat model and verification framework for secure domain migration of web-hosted cryptocurrency trading platforms,” Zenodo, preprint, Oct. 2026, doi: 10.5281/zenodo.23576774.
Install
Requires Python 3.9+ and dnspython.
pip install migratesafe
Or run from a checkout without installing (dnspython must be available):
python3 -m migratesafe audit --new new.example --old old.example
Usage
# full audit of a completed migration
migratesafe audit --new new.example --old old.example
# only the checks that matter up to a given phase (P0 preparation ... P4 retirement)
migratesafe audit --new new.example --old old.example --phase P1
# a subset of checks, with a JSON report for CI or record keeping
migratesafe audit --new new.example --checks C5,C9,C13 --json report.json
# include a scan of the application source for leftover references to the old name
migratesafe audit --new new.example --old old.example --project ~/code/exchange --config migratesafe.json
migratesafe list
Exit codes: 0 all passed, 1 warnings or errors, 2 at least one failure, which makes it usable as a CI gate.
Checks
| ID | Check | Threats | How it is verified |
|---|---|---|---|
| C1 | Zone inventory | T1, T2 | Hostnames from CT logs (crt.sh) and common-name probing are compared with your inventory |
| C2 | Dangling targets | T1 | CNAME/MX/NS targets that no longer resolve; known takeover fingerprints on SaaS targets |
| C3 | Delegation | T3 | Parent NS set equals zone NS set; every nameserver answers authoritatively |
| C4 | DNSSEC | T3 | DS at parent and AD-validated answers, or a documented exception |
| C5 | CAA | T4 | issue/issuewild restrict CAs and an iodef contact exists |
| C6 | CT audit | T1, T4 | Recent certificates are from allowed issuers / permitted by CAA |
| C7 | New-origin TLS | T4, T5 | Valid chain, expiry, SAN scope, TLS 1.0/1.1 refused |
| C8 | Former-host keys | T4 | The former host no longer serves a trusted certificate for your names |
| C9 | HSTS | T5 | max-age ≥ 1 year with includeSubDomains from phase P3 |
| C10 | Legacy redirect | T5, T8 | 301/308 to https://new, path preserved, no open redirect |
| C11 | Session cookies | T1, T5 | Host-only, Secure, HttpOnly, SameSite |
| C12 | Allow-lists and configuration | T2, T8 | Source scan for the old name; Laravel .env checks (APP_URL, SESSION_DOMAIN, ...) |
| C13 | E-mail authentication | T2, T6 | SPF -all, DKIM, DMARC p=reject; Null MX + v=spf1 -all on a retired name |
| C14 | Retention and lookalikes | T2, T7 | RDAP expiry and transfer lock; registered typo/TLD lookalikes |
Threat classes: T1 dangling records / subdomain takeover, T2 residual trust, T3 delegation hijack, T4 certificate anomalies, T5 transport/session downgrade, T6 e-mail spoofing, T7 lookalike domains, T8 configuration drift.
CT data comes from the public crt.sh service, which can lag behind the logs by hours and occasionally returns incomplete results; treat C1/C6 output as a lower bound and re-run if crt.sh is unavailable.
Some things cannot be observed from outside (revoked sessions, provider dashboards, registrar MFA);
those are reported as MANUAL items so they still appear on the checklist.
Configuration (optional)
See example-config.json:
{
"inventory": {"new.example": "migrate", "www.new.example": "migrate", "status.old.example": "delete"},
"allowed_issuers": ["Let's Encrypt"],
"dkim_selectors": ["default"],
"old_host_ip": "203.0.113.10",
"dnssec_exception": false,
"old_domain_mail": "retired",
"session_paths": ["/", "/login"],
"config_ignore": ["CHANGELOG.md"]
}
Safety and ethics
MigrateSafe is read-only. It performs ordinary DNS queries, HTTPS requests and TLS handshakes, and reads
public CT and RDAP data. It never claims resources, requests certificates or modifies anything. Values of
secret-looking keys are masked when .env lines are reported. Only audit domains you own or are
authorised to test.
Evaluation (fault injection)
evaluation/ runs all fourteen checks, unchanged, against a simulated Internet (DNS, HTTP, TLS, CT and RDAP
in memory). It starts from a correctly migrated baseline and injects 28 faults covering threats T1–T8:
python3 -m evaluation.run # writes evaluation/results.md and results.json
Current result: 27/28 faults flagged by the expected check, 1 (registered lookalike) reported as INFO by
design, and 0 false positives on the baseline. See evaluation/results.md for the
per-fault table and limitations.
Tests
python3 -m unittest discover -s tests -v
Author
Er. Abhijeet Ashok More (myabhya). Director, Cyin Solutions Pvt Ltd; Founder, Sumantrak. ORCID 0009-0001-4934-0204 · Google Scholar · LinkedIn
Citation
@misc{more2026migratesafe,
author = {More, Abhijeet Ashok},
title = {MigrateSafe: A Threat Model and Verification Framework for Secure Domain
Migration of Web-Hosted Cryptocurrency Trading Platforms},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.23576774},
note = {Preprint}
}
License
MIT, see LICENSE.
Metadata
Release files for migratesafe 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| migratesafe-0.1.0.tar.gz | 30.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| migratesafe-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.4 kB
Release files / migratesafe-0.1.0.tar.gz
| Download URL | migratesafe-0.1.0.tar.gz |
|---|---|
| Size | 30.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ad5a6bf98f9d3b6ee8df4483cd61cac72ba87cb41da5105bdc8e1ca17a89883a
|
|
BLAKE2b-256 checksum How to use checksums |
54b0d1b683c54e1e9f53248303c664d13fe5c42d16d5efb1d4f857f64cba08e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / migratesafe-0.1.0-py3-none-any.whl
| Download URL | migratesafe-0.1.0-py3-none-any.whl |
|---|---|
| Size | 28.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3d8657ea35a2d58c6e6071ec195a595d937d8d06f54cd19a5cf675c16c2b4cb2
|
|
BLAKE2b-256 checksum How to use checksums |
f3bd67e2796ec91ddd9b0ab481e9b03e18e154ad818f0a1f3da126ec033e1a3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Security for migratesafe 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.