Skip to content
~/saber

Software engineer · Security practitioner

SaberEngineering secure, scalable web systems

I build backend systems and then try to break them. Web application security, DevSecOps, and the infrastructure underneath both.

$ curl -sI https://5a8er.irlive

HTTP/2 200

About this list
Security headers served with this page, rendered from the same configuration the server sends.
content-security-policy:
default-src 'none'; script-src 'nonce-{random}'
strict-transport-security:
max-age=31536000; includeSubDomains; preload
cross-origin-opener-policy:
same-origin
x-content-type-options:
nosniff
referrer-policy:
strict-origin-when-cross-origin
x-served-by:
cf

Rendered from the same configuration the server sends. Run the command.

About

Building and breaking, from both sides

I build backend systems and then try to break them. Most of my work sits where application security meets infrastructure: the API that trusts a header it should not, the deployment that leaks an internal hostname, the authorisation check that lives in the route handler instead of the query.

That perspective came from working both sides. Writing Django services taught me where the shortcuts are; looking for those same shortcuts in other people's systems taught me which ones actually get exploited. What survived is a bias toward designs that fail closed, and toward constraints that live in the schema rather than in a code review comment somebody will forget.

Right now I am building two things: a storefront selling hardware wallets to buyers most payment processors decline, and a marketplace where providers across six service markets are ranked on verified reviews. Different products, one question underneath — who is allowed to do what, and what happens the first time somebody lies about it.

Web application security
Access control, authentication flows, and the classes of bug that survive a scanner.
DevSecOps
Scanning and policy as a gate in CI, not a dashboard nobody opens.
Backend systems
Django and API design where correctness under concurrency is the actual requirement.
Networking and OSINT
Mapping what is exposed, and moving traffic where the default path does not work.

Work

Three systems, and what could go wrong with each

Each card leads with the constraint that made the problem hard, not the logos. Open the disclosure for the architecture and the specific controls.

CoinGuard

In progress

Django storefront selling hardware crypto wallets, built for buyers most payment rails will not serve.

Problem

The product is itself a security device, and the buyers are largely people mainstream payment processors decline. That means supporting Stripe, PayPal, Zarinpal, and on-chain payment side by side — four rails with four different failure modes — while the storefront stays trustworthy enough that somebody will hand it a shipping address for the device that will hold their keys.

Approach

One order state machine sits behind every payment gateway, so adding a rail is an adapter rather than a second branch through checkout. The whole stack ships as containers behind Nginx, which terminates TLS with automatically renewed Let's Encrypt certificates, and separate dev and production compose files keep the two from drifting into each other.

  • Django 4.2
  • PostgreSQL
  • Gunicorn
  • Tailwind CSS
  • Docker Compose
  • Nginx
  • Stripe / PayPal / Zarinpal
Architecture & security decisions

Architecture

  • Nginx terminates TLS and serves static assets; Gunicorn runs the application behind it, never exposed directly
  • Separate dev and production compose files, so a local convenience cannot accidentally ship
  • Multiple payment gateways behind one checkout flow rather than one code path per provider
  • Automated deployment and monitoring scripts, so a release is repeatable rather than remembered

Security

  • CSRF protection on every state-changing form; sessions hardened rather than left at defaults
  • Parameterised queries throughout — no SQL assembled from strings
  • Uploads validated on the way in, since a storefront that accepts files accepts whatever it does not check
  • HSTS, X-Frame-Options, and X-Content-Type-Options set at the edge, with HTTP redirected to HTTPS
  • Payment details tokenised rather than stored; role-based access control on the admin surface
  • Security events audit-logged, so an incident has a record to read rather than a guess

TrustHub

In progress

Multi-vendor marketplace across six service markets, where the thing actually being sold is trust.

Problem

Providers list across VPN, VPS, domain, outbound, crypto, and Starlink markets, and buyers choose on verified badges and reviews. That makes authorisation the product rather than a feature of it: a provider must reach only their own listings, a review is worthless if it can be manufactured, and an admin route must not be reachable by guessing a URL.

Approach

Google OAuth is the only self-serve way in, and it grants at most the USER role — signing in can never award itself provider or admin. Password login exists solely for ADMIN, is restricted to that role in production, and is kept off the public navigation. TOTP two-factor sits on top, and a ban applies across both sign-in paths at once rather than closing one door.

  • Next.js
  • TypeScript
  • Prisma
  • PostgreSQL
  • NextAuth
  • Upstash Redis
  • Zod
  • Stripe
  • Playwright
  • Docker
Architecture & security decisions

Architecture

  • Prisma schema is the single source of truth for roles, so the permission model lives in one file rather than scattered through handlers
  • Provider logos go to object storage, so uploaded files never sit on the application server
  • Catalogue browsable by provider or by location, server-rendered so it stays crawlable
  • Playwright end-to-end tests covering the authentication flows, which are the part most expensive to get wrong

Security

  • OAuth grants at most USER — privilege escalation is not something a sign-in can perform
  • Admin credential login is production-restricted to ADMIN and absent from public navigation
  • Passwords bcrypt-hashed; TOTP two-factor available on top
  • Rate limiting in middleware across the auth POST routes, backed by Redis rather than process memory
  • A ban blocks both authentication providers, so closing one route does not leave the other open
  • /api/auth kept same-origin so the CSP's form-action 'self' posture survives the OAuth redirect

azadrahinternet

Maintained

Persian-language Telegram referral bot for a VPN channel: one Cloudflare Worker, no framework, no build step, no database.

Problem

A referral system invites exactly one attack — counting the same person twice. Rejoins, several invite links, and members who leave and return all have to collapse into one number nobody can inflate. It also has to keep running for an audience whose network actively interferes with the infrastructure most services assume.

Approach

A single Worker with one fetch handler and Cloudflare KV as the only datastore. No framework, no build step, and nothing in the dependency tree to keep patched. Referrals are counted from Telegram's chat_member updates and deduplicated globally, so a given person counts once ever and a rejoin never recounts.

  • Cloudflare Workers
  • Cloudflare KV
  • Plain ES modules
  • Telegram Bot API
  • Wrangler
Architecture & security decisions

Architecture

  • One `export default { fetch }` entry point — no framework, no build step, no bundler to trust
  • Cloudflare KV holds configuration and referral state; there is no second datastore to keep consistent
  • Webhook-driven rather than polling, so the bot costs nothing while idle
  • Referral counting deduplicated globally rather than per invite link

Security

  • Secrets compared in constant time, never with `===`, for both the webhook token and the admin bearer
  • Header-based authentication only — `?secret=` query parameters are refused outright, since URLs end up in logs
  • Every user-controlled string HTML-escaped before it reaches a `parse_mode: HTML` message
  • Strict security headers on every response: CSP, HSTS, `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer`
  • Clients see a generic 401/404/500; the real detail goes to logs, so errors are not a reconnaissance tool
  • Length caps on every admin write, and per-IP rate limiting on the admin surface via the Workers binding

Skills

What each of these was actually used for

Grouped by the kind of problem it solves, with a concrete artifact against each entry rather than a rating nobody can verify.

Backend Engineering

Services that stay correct while the data underneath them changes.

  • Python / Django

    Custom auth backends, permission classes, migrations run against live data

  • Flask

    Small single-purpose services where a full framework would be overhead

  • REST API design

    Versioned endpoints, cursor pagination, idempotency keys on every write

  • PostgreSQL

    Constraints in the schema rather than the application; indexes chosen from query plans

  • Redis

    Session storage, cache invalidation, sliding-window rate-limit counters

  • Background jobs

    Retry with backoff, dead-letter handling, work that survives a restart

Frontend

Interfaces that stay fast and usable without a keyboard trap or a layout shift.

  • React / Next.js

    Server components by default; client islands only where state is genuinely local

  • TypeScript

    Strict mode, no implicit any, request schemas shared between client and server

  • Tailwind CSS

    Token-driven systems — colour decided once in CSS, not per component

  • Accessibility

    Keyboard-complete flows and contrast ratios measured rather than assumed

  • Web performance

    Budgets on shipped JavaScript; images and fonts self-hosted and sized

DevOps

Getting code to production repeatably, and knowing when it breaks.

  • Docker

    Multi-stage builds, non-root runtime users, base images pinned by digest

  • Nginx

    Reverse proxying, TLS termination, per-endpoint connection and rate limits

  • CI/CD

    This site: one commit deployed to two independent origins by GitHub Actions

  • Linux administration

    Service hardening, systemd units, firewall and SSH policy on public hosts

  • Observability

    Structured logs that answer a question, not logs that record an event

Security

Finding the failure before someone else does, and designing so there is less to find.

  • Web application testing

    Access-control flaws, authentication bypass, SSRF, injection, insecure deserialisation

  • Bug bounty workflow

    Recon through to a reproducible proof of concept and a report someone can act on

  • Threat modelling

    Trust boundaries drawn before implementation, not audited after it

  • DevSecOps

    Dependency, secret, and SAST scanning wired into CI as a gate rather than a report

  • Supply chain

    Lockfiles committed, release-age floors on new versions, install scripts reviewed

OSINT

Mapping what an organisation exposes before assuming what it runs.

  • Attack surface discovery

    Subdomain enumeration and certificate transparency logs to find forgotten hosts

  • Exposure analysis

    Credential and secret leakage across public repositories and paste sites

  • Infrastructure fingerprinting

    Correlating DNS, ASN, and TLS metadata into an ownership picture

Networking

Understanding the path a packet takes, especially where that path is contested.

  • DNS

    Zone design, split-horizon resolution, and debugging propagation from first principles

  • TLS

    Certificate chains, SNI behaviour, and cipher policy that survives a scanner

  • VPN and tunnelling

    WireGuard and OpenVPN deployments, protocol selection under active inspection

  • Restricted networks

    Transport selection and traffic shaping where the default path does not work

Contact

Get in touch

Work, security findings, or a question about something above.

What happens to this

  • Delivered to one mailbox. No CRM, no mailing list.
  • Rate limited per address, shared across both origins.
  • Message bodies are not logged and not sent to analytics.
  • Security reports are welcome — see security.txt.