Docs

Security

Security is built into WorkOSync from the first request, not bolted on. Every login and public form is defended, every response is served over TLS, sessions are signed and server-verified, and the platform is designed to shrug off the bots and scanners that probe any site on the internet. This page describes each layer as it is implemented.

Secrets stay on the server

Session and form signing secrets, gateway keys and mail credentials are generated and stored on the server. They never pass through chat, the browser or a URL in plain text, and the documentation never prints them.

Request gate

Every request to WorkOSync passes through a single gate before any page or API runs. The checks run in this order:

#CheckOutcome
1Banned IP address403 on every request, not just the one that earned the ban.
2Scanner probe path (/.env, /.git, /wp-login.php, /wp-admin, /xmlrpc.php, phpunit, phpMyAdmin, any .php or .asp URL)404 and an immediate permanent ban.
3Bad or empty user agent (curl, wget, python-requests, Go-http-client, Java, okhttp, scrapy, sqlmap, nikto, nmap, masscan, nuclei, ffuf, burp and similar)403; three hits in 24 hours bans the address for 24 hours.
4Crawler (Googlebot, Bingbot, DuckDuckBot, Applebot, facebookexternalhit, WhatsApp, Twitterbot, LinkedInBot, Slackbot and any agent naming itself a bot, crawler or spider)Allowed on the public site so search and share previews work; 404 on /admin.
5Oversized POST, PUT or PATCH body413 above 64 KB; the customer and item bulk-import routes allow 4 MB.
6AuthenticationApp pages without a valid session redirect to /login?next=…; API calls get 401.

Public paths that never need a session are the marketing pages, /login, /signup, this documentation, the hosted documents under /pay/, /quote/, /contract/, /proforma/ and /platform/, and the contact and auth APIs.

Transport and headers

Production is served over HTTPS only. Plain HTTP does nothing but answer the certificate challenge and redirect to the secure address, so no login or document is ever exposed over an unencrypted connection.

  • HSTS tells browsers to use HTTPS only.
  • nosniff stops content-type guessing.
  • frame-ancestors and X-Frame-Options block clickjacking.
  • Referrer-Policy and Permissions-Policy limit what leaks and what the page may use.
  • base-uri, form-action and object-src restrictions harden against injection.

Behind a reverse proxy the client address is read from the hop the proxy appended, never from a header the client can forge, and proxy trust is off unless the operator enables it.

Sign-in protection

The sign-in page
The sign-in page carries a hidden honeypot field and a signed render-time token in addition to the visible fields.
ControlWhat it does
Honeypot fieldA hidden field no human fills. Any value bans the address for 24 hours.
Timing tokenA signed render-time token; a submission that arrives less than 1.5 seconds after the form was rendered is rejected.
Per-IP rate limitTen attempts per minute, then Too many attempts. Please try again later.
Escalating IP lockoutFive failures lock the address for 15 minutes, then 24 hours, then permanently.
Per-account throttleTen failures against one email within 15 minutes pause that account briefly. It is a throttle, not a hard lock, so an attacker cannot lock the owner out.
Constant-time checksUnknown emails are compared against a dummy bcrypt hash so timing cannot reveal which accounts exist.
Uniform errorsEvery failure returns the same text: We could not sign you in. Check your email and password and try again.

Sessions

A successful sign-in issues a wos_session cookie: a signed payload (HMAC-SHA256) carrying the user's identity, company and role with a seven-day expiry. The cookie is httpOnly, SameSite=Lax and Secure in production, so scripts cannot read it and it is never sent from another site. The request gate verifies the signature and expiry on every protected page, and each server action re-checks the session itself. Sign out clears the cookie and returns you to the login page. Passwords are stored as bcrypt hashes (cost 11).

Sign-up and public forms

Sign-up and the website contact form carry the same honeypot, timing token (2.5 seconds minimum), per-IP limits (five sign-ups per hour, five contact messages per ten minutes), small body limits, link-spam heuristics and bans for repeat offenders. A duplicate sign-up email receives the same generic error as an invalid one, with equalised timing, so existing accounts cannot be enumerated.

Access inside a company

Each company is a separate tenant with isolated data. Inside it, roles decide which modules a person can open: a restricted role that types a disallowed URL is redirected to the Daily Brief, and a Client role is confined to the portal. The Platform Console is gated to superadmins by the server, and its pages return 404 to every bot. See Roles and permissions.

The Users and access page
Role cards decide what each team can open. Owner and Administrator are fixed; every other role is editable.

Audit trail

Mutating actions are recorded in the append-only audit log with the actor, action, subject, time and IP address, and every approval decision is stamped with the time it was logged.

Bring-your-own AI keys

AI provider keys entered in Ask AI are stored only in your browser and sent only to the provider's official API. WorkOSync servers never receive them. Treat them like passwords: use a key with a spending limit and disconnect it on shared devices.

Backups and liveness

Data is backed up on the schedule set in Platform Settings, with retention in days, and a restore always takes a safety copy first and migrates the schema forward. A liveness probe watches the running service and restarts it if it hangs, because a process that is up but frozen still needs to recover on its own.

Verified live, not just written

These protections are tested against real requests after every deploy: a scanner-style UA must get 403 and be banned, a browser and Googlebot must get 200, and a bot on /admin must get 404. Thresholds are deliberately aggressive, so a burst of test attacks will ban the tester's own address. In development the loopback address is exempt from bans and lockouts last ten seconds.