.. include:: ../variables.rst .. _security: Security ======== |APPNAME| is used in healthcare environments that store protected health information, so the platform is built and maintained with a defence-in-depth posture. This page summarises the ongoing security testing the product undergoes and the features available to administrators for monitoring and blocking abusive traffic. To open the security pages, go to :blue:`System` | :blue:`Security`. The Security submenu is visible to root and administrator accounts. .. _security.pentesting: Penetration Testing ------------------- Every release of |APPNAME| is reviewed against a healthcare-compliance security checklist. The review combines static source review with targeted dynamic probes and covers the following categories: * **Authentication** --- password strength, hashing, two-factor enrolment and verification, password-reset flows, and handling of failed login attempts. * **Session and token handling** --- session lifetime, idle timeout, session fixation, token scope, and logout behaviour. * **Authorization and privilege escalation** --- vertical escalation (standard user raising their own privileges), horizontal escalation (one tenant reaching another tenant's data), and insecure direct object references in record IDs passed to the REST API. * **Input handling** --- SQL injection, cross-site scripting, command injection, path traversal, and validation of file uploads. * **Transport and deployment** --- HTTPS configuration, certificate validation, security headers, cookie flags, and safe defaults for new installations. * **Logging and observability** --- that security-relevant events (failed logins, manual blocks, privilege changes) are captured and reviewable by an administrator. Findings from each review are tracked through remediation and retested before the release ships. .. _security.twofactor: Two-Factor Authentication ------------------------- |APPNAME| supports time-based one-time password (TOTP) two-factor authentication. When a user enables two-factor authentication on their profile, the sign-in flow changes as follows: 1. The user signs in with username and password as usual. 2. |APPNAME| creates a pending session and prompts for the current six-digit code from their authenticator app. 3. The session is promoted to a full session only after the code is verified. Every other route is denied while the session is in the pending state. 4. Repeated wrong codes count against the failed-login counter for the user's IP address and contribute to automatic blocking (see :ref:`security.rate.limiting`). Administrators can require two-factor authentication for specific user accounts from the user editor. .. _security.password.encryption: Password Protection ------------------- User passwords are never stored in plain text and are never visible to administrators, including system-level administrators. When a user sets or changes a password, |APPNAME| converts it into a one-way protected form using a modern, industry-standard key derivation function with per-user randomisation. The original password cannot be recovered from what is stored --- not by |APPNAME|, not by support staff, and not by anyone who obtains a copy of the database. At sign-in, |APPNAME| applies the same process to the password the user typed and compares the result to the stored value. The typed password is held only long enough to perform that check and is never written to disk in a readable form. Because stored passwords cannot be reversed, a forgotten password must be **reset**, not retrieved. The password-reset flow requires the user to prove control of their registered email address before a new password can be chosen. .. include:: ../_password.requirements.txt Password protection is layered on top of the other controls described on this page: two-factor authentication, rate limiting, failed-login auto-blocking, and the blocklist. A stolen password alone is not enough to sign in to an account that has two-factor authentication enabled. .. _security.rate.limiting: Rate Limiting and Connection Limits ----------------------------------- |APPNAME| tracks request volume per client IP address and enforces three guards: * **Requests per second** --- a short-window cap that stops burst floods from a single source. * **Requests per minute** --- a longer sliding window that catches sustained scraping or brute-force attempts that stay under the per-second threshold. * **Concurrent connections** --- a hard cap on the number of simultaneous open connections from one IP. This prevents a single client from tying up server resources. The limits are tuned for normal dashboard traffic, including pages that fan out to many widget requests on load. When a client crosses any of the thresholds, its requests are rejected until the window resets. Repeated violations add the IP to the blocked list automatically. Failed login attempts are tracked separately. A client that fails sign-in too many times within a 15-minute window is added to the blocked list and must be unblocked by an administrator before it can try again. .. _security.blocked.ips: Blocked IPs ----------- The Blocked IPs page lists every client IP address |APPNAME| has blocked, together with the reason, when it was first and last seen, how many times it has hit the server, and when the block will expire. .. figure:: /images/lightmode/security-blocked-ips-list.png :align: center |br| Stat Chips ^^^^^^^^^^ Four cards across the top of the page summarise the current state of the blocklist: * ``blocked`` --- total number of IPs currently blocked. * ``blocked in last 24h`` --- IPs that have been seen within the last day. * ``countries`` --- number of distinct countries represented by the currently blocked IPs. * ``top offender`` --- the IP with the highest hit count, followed by its hit count in parentheses. Toolbar ^^^^^^^ The toolbar above the table provides: * **Search** --- filter by IP address, country, city, or internet service provider. * **State** --- show only ``Blocked`` records, only ``Unblocked`` records, or ``All``. * **Reason** --- filter by why the IP was added: * ``Failed Login`` --- too many failed sign-in attempts. * ``Manual`` --- added by an administrator. * ``Rate Flood`` --- exceeded the request-rate limits. * **Country** --- limit the table to a specific country. * **Refresh** --- reload the list from the server. Table Columns ^^^^^^^^^^^^^ The table has the following columns: * **State** --- ``Blocked`` or ``Allowed``. * **IP** --- the client IP address. * **Location** --- city and country if geolocation is available, with the ISP shown on a second line. * **Reason** --- why the IP was added to the list. * **Hits** --- how many times this IP has hit the server. * **First Seen** --- the first time |APPNAME| recorded traffic from this IP. * **Last Seen** --- the most recent time this IP was observed. * **Expires** --- when the block will automatically lift, or ``Permanent`` if the block has no expiry. * A three-dot menu for per-row actions. Row Menu ^^^^^^^^ The three-dot menu on each row exposes these actions: * ``Unblock`` --- allows traffic from the IP again. Only shown for currently blocked records. * ``Re-block`` --- re-applies a 24-hour block to a record that is currently allowed. Only shown for unblocked records. * ``Block Permanently`` --- converts a time-limited block to a permanent one. Only shown for blocked records that have an expiry. * ``View on Map`` --- opens the :ref:`security.threat.map` focused on this IP. * ``Delete`` --- permanently removes the record after a confirmation prompt. Use ``Unblock`` instead if you only want to allow traffic again; deleting the record loses its history. Blocking an IP Manually ^^^^^^^^^^^^^^^^^^^^^^^ Click ``Block IP`` in the page header to add an address manually. The dialog accepts: * **IP Address** --- the address to block. * **Reason** --- ``Manual``, ``Failed Login``, or ``Rate Flood``. * **Duration** --- ``1 hour``, ``6 hours``, ``24 hours``, ``7 days``, ``30 days``, or ``Permanent``. * **Notes** --- optional free-text context for other administrators. .. _security.threat.map: Threat Map ---------- The Threat Map is a geographic view of the currently blocked IPs. Each pin represents one blocked address placed at its approximate geolocation. .. figure:: /images/lightmode/security-threat-map.png :align: center |br| Use the ``List View`` button in the header to jump back to the :ref:`security.blocked.ips` table, or the refresh button to reload the markers from the server. Clicking ``View on Map`` from a row of the blocked-IPs table opens this page centred on that IP. Additional Security Controls ---------------------------- Other security-related features documented elsewhere in this guide: * :ref:`Sign-in and password reset `. * :ref:`Roles and permissions `. * :ref:`Session timeout `. * :ref:`Alerts ` --- security-related system events surface here alongside operational alerts. If you suspect an incident or need help interpreting the blocked IPs list, contact |SUPPORTEMAIL|.