For developers
PHP-based, no database server, no credentials file. Everything you need to evaluate, run, and harden it.
pdo_sqlite extension (bundled with most PHP builds).# 1. get the code git clone https://github.com/MohammedNasrallah/passnumber-demo.git cd passnumber-demo # 2. run it (creates the SQLite file on first request) php -S 127.0.0.1:8000 -t app # 3. open the app # http://127.0.0.1:8000/signup.php — create an account (Classic or Sequence) # http://127.0.0.1:8000/login.php — log in
On shared hosting, upload the app/ folder (e.g. to public_html/app). Its data/ folder must be writable by PHP so the SQLite file can be created; the schema creates and upgrades itself on first request.
A single table. Note what is not here: no plaintext passnumber, no symbol choices, no positions.
CREATE TABLE users ( id INTEGER PRIMARY KEY, username TEXT NOT NULL UNIQUE, salt TEXT NOT NULL, -- per-account random salt secret_hash TEXT NOT NULL, -- versioned PBKDF2-SHA256 of the canonical secret hash_version INTEGER, -- upgraded automatically at login login_scheme TEXT, -- 'classic' | 'sequence' neglect_mask TEXT, -- classic: which rows are decoys (non-secret) fs_cats TEXT, -- sequence: ordered category names (non-secret) fs_pattern INTEGER, -- sequence: which input slots are real n_rows INTEGER, n_cols INTEGER, failed INTEGER DEFAULT 0, lock_until INTEGER DEFAULT 0 ); -- NOT here, by design: your symbols, their positions, any plaintext.
Registration derives one canonical token from the user's choices (each row encoded independently so the secret is collision-free), then stores only its salted hash:
// labelled tokens in fixed order — collision-free by construction $canonical = 'r1=🐶|r2=∅|r3=🚗|r4=🎾'; // sequence: 'seq1=animals:🐱|…' $hash = 'v3$'.hash_pbkdf2('sha256', $canonical, $salt.$pepper, 310000); // login rebuilds the canonical from the shuffled board + typed digits, // hashes ONE candidate (deterministic), and compares in constant time if (hash_equals($stored, $rebuilt)) { /* ok */ }
Before you put this in front of real users:
X-Frame-Options, X-Content-Type-Options.The app ships two login methods — Classic (row-bound) and Sequence (3 icons in order) — on one engine; an account is created on one method and stays on it. The demo is plain PHP — there is no Laravel dependency. If your marketing or internal docs mention a framework, reconcile that with what actually ships. The demo targets PHP 8.1+ and is not tested against PHP 7. SQLite is used for zero-config portability; swapping to MySQL/Postgres is a straightforward change to the data layer but is left to the implementer.