Security #
EthicsPortal handles sensitive whistleblower data. This page documents the specific technical and organizational measures we have in place. It is written for compliance officers, DPOs, and legal teams evaluating the platform.
Last updated: 2026-09-13.
Data encryption #
The sensitive database fields listed below are encrypted at rest using Rails ActiveRecord Encryption with non-deterministic encryption (each encryption produces a unique ciphertext, preventing pattern analysis). This does not cover whole database dumps or file attachments.
| Field | Encrypted | Deterministic |
|---|---|---|
| Report description | Yes | No |
| Reporter name | Yes | No |
| Reporter contact details | Yes | No |
| Structured intake answers (relationship, source, timing, prior reporting, retaliation concern) | Yes | No |
| Message body (reporter–handler communication) | Yes | No |
| Internal case notes | Yes | No |
| Handler two-factor secrets and backup codes | Yes | No |
Non-deterministic encryption means these fields cannot be queried by value at the database level. Even with full database access, an attacker cannot search for a specific reporter name across records.
All connections to EthicsPortal use HTTPS/TLS. Unencrypted HTTP requests are redirected.
Anonymity and privacy #
IP addresses #
EthicsPortal does not record a reporter’s IP address with reports, messages or audit records, and the application’s logs for the whistleblowing channel contain no IP addresses.
Rate limiting on the whistleblowing channel (report submission, case lookup, messaging) keys on a truncated, keyed SHA-256 hash of the request IP. The hash is pseudonymous, not anonymous: it cannot be read back as an address, but anyone holding the application secret could test candidate addresses against it. It is used only for the rate-limit window (at most 10 minutes) and held in a size-capped cache.
The TLS proxy in front of the application keeps its own connection logs, which include IP addresses. They are not linked to reports. They are held in a single 10 MB log file that is continuously overwritten, currently within a few hours; the 7-day server snapshots described under backups can contain a copy.
File metadata stripping #
Uploaded files are automatically stripped of identifying metadata before storage:
| File type | Metadata removed | Method |
|---|---|---|
| Images (JPEG, PNG, TIFF, WebP) | EXIF data: GPS coordinates, camera model, device serial number, author, timestamps | Vips image processing |
| PDF documents | Author, creator application, modification history | exiftool in the standard production setup |
| Video files | GPS, device info, recording software | exiftool in the standard production setup |
| Audio files | Recording device, GPS, software tags | exiftool in the standard production setup |
Metadata removal is performed server-side before storage. For file types handled by exiftool, this depends on the standard production tooling being present.
Virus scanning #
All uploaded files are automatically scanned for malware using ClamAV , an open-source antivirus engine. Scanning happens server-side in a background process after upload. Files that have not passed scanning are blocked from delivery, and infected files are removed automatically.
Files are scanned on EthicsPortal infrastructure — no file data is sent to third-party scanning services.
Handler anonymity #
Whistleblowers never see the real names or email addresses of the people handling their report. All messages from handlers are displayed as “Case handler”. This protects handler identity and prevents social engineering.
No tracking #
EthicsPortal does not use third-party tracking cookies, advertising pixels, or fingerprinting scripts. We use Cloudflare Web Analytics on marketing pages only. It does not set analytics cookies, but Cloudflare may process request metadata, including IP addresses, as described in the Privacy Notice . The whistleblowing channel itself has no analytics.
Current assurance status #
EthicsPortal does not currently claim accredited ISO 27001 certification on this site. It also does not currently publish an independent third-party audit of the anonymity architecture. If that changes, the scope and date will be published here.
Security review materials #
Customers that require procurement or legal review materials can request them during procurement. Available materials may include a signed DPA, registry and tax evidence, a completed security questionnaire, and written answers covering backup and restore procedures, privileged production access, and incident-response handling.
Access control #
Authorization is enforced at the application level using Pundit policies.
| Role | Can view reports | Can manage organization settings | Can assign handlers |
|---|---|---|---|
| Admin | All reports | Yes | Yes |
| Handler | Reports they are assigned to or participating in | No | No |
| Viewer | Read-only, for auditors and legal counsel | No | No |
- Handlers cannot see reports they are neither assigned to nor participating in. Participants are explicitly added by an admin or the primary assignee (for example, looping in legal or HR).
- Reporters have no user account — they access their report via a Case ID (
WB-XXXX-XXXX) plus a 6-digit passcode they choose at submission. - Every controller action checks authorization. Unauthorized access attempts are blocked and logged.
Two-factor authentication #
Handler and admin accounts can enable TOTP-based two-factor authentication via any standard authenticator app (Google Authenticator, 1Password, Authy, and compatible alternatives). Once enabled, sign-in requires both the primary credential and a rotating 6-digit code.
Reporters authenticate with two factors as well: the Case ID (something they hold) and the passcode they chose at submission (something they know). The passcode is stored only as a bcrypt digest and cannot be recovered. The follow-up inbox and message-posting are session-gated behind this check, so a leaked Case ID alone cannot read the report or impersonate the reporter.
Session lifecycle #
Each authenticated session records last_seen_at on every request (debounced). Users can review their active sessions, see when each was last active, revoke any session individually, or sign out of all other sessions at once from the account settings.
Sessions expire automatically after 14 days of inactivity. The next request from an idle session destroys the server-side record, clears the cookie, and forces re-authentication via a fresh magic link. A nightly job sweeps abandoned sessions on the same timeout, so user_agent and ip_address are not retained beyond the idle window even when the user never returns.
Magic-link authentication limits the blast radius of long-lived sessions: a stolen session cookie does not yield a reusable credential, and re-authentication requires email access.
Member access and offboarding #
Organization access is enforced at the request boundary. When a member is deactivated:
- Access to the organization is rejected immediately, including on previously bookmarked URLs.
- Open report assignments are unassigned.
- Participantships are removed.
- The audit-log history attributable to the member is preserved.
- The deactivated member is notified.
- Reactivation does not automatically restore prior case access.
The organization owner, the last active admin, and the portal’s designated compliance officer cannot be deactivated. All deactivation and reactivation events are written to the append-only audit log.
Removing a member deactivates the membership rather than deleting it, so the audit trail and report history stay resolvable. A membership is hard-deleted only when the member’s own account or the organization itself is erased.
Account erasure #
A member who closes their own account has their email address replaced with a non-routable placeholder and their avatar, two-factor credentials, sessions, API tokens, and preferences permanently removed; the account can no longer authenticate or be contacted. If the account never acted inside an organization, the record is deleted outright. If it did, the member’s name remains while their memberships are deactivated so retained case notes, messages, and audit entries stay attributable. The Controller determines the retention period under its documented instructions and applicable law; GDPR erasure rights remain subject to any applicable exceptions for legal obligations or legal claims.
A scrubbed account cannot authenticate by any route: magic link, SSO assertion, or API token.
Rate limiting #
Public portal endpoints are rate-limited to prevent abuse and enumeration attacks:
| Endpoint | Limit |
|---|---|
| Report submission | 5 per 10 minutes per hashed IP |
| Case lookup (Case ID + passcode) | 10 per 3 minutes per hashed IP |
| Message submission | 10 per 3 minutes per hashed IP |
Rate limiting uses the keyed IP hash described above — no IP address is stored for it.
Audit and compliance #
Append-only audit trail #
Defined report and account events are logged with:
- Timestamp (UTC)
- Actor (which user or system process performed the action)
- Action type (for example, report created, status changed, message sent, handler assigned, report exported or report deleted). Each handler’s view of a given report is logged, deduplicated to one entry per handler per report per 24 hours, and reporter follow-up views are logged the same way. Reads of other resources are not logged.
Audit log entries are append-only. No user, including an organization admin, can edit an entry or delete one selectively: PostgreSQL triggers reject modification and truncation at the database level. Entries are removed only by the retention lifecycle or when the organization itself is deleted, and privileged database intervention remains a documented residual risk (risk register R-08 ). The full audit trail is included in PDF case exports for regulatory review.
Data retention #
Organizations configure their own retention period: 12, 24, 36, 48, or 60 months after a report is closed. When the retention period expires, a background job deletes the report and associated data from production; backup copies expire under the separate documented lifecycle.
The Controller remains responsible for selecting a period that satisfies GDPR storage limitation and applicable national record-keeping rules.
CSRF protection #
All form submissions are protected against cross-site request forgery using Rails’ built-in CSRF tokens.
Secure development lifecycle #
EthicsPortal follows a documented development lifecycle for changes that touch the Service. The stages are stated here so a procurement reviewer can map them to ISO/IEC 27001:2022 controls A.8.25–A.8.29 (see the control map for the full mapping).
| Stage | Practice |
|---|---|
| Architecture and design | Features that introduce new personal-data flows, sub-processors, or authorization scopes are evaluated against the encryption, access-control, and audit-trail commitments documented on this page before implementation. |
| Change review | Every production change is checked against a written checklist (encryption coverage, authorization scope, audit-log emission, input validation, secret handling) before deploy. Enforcement is automated rather than discretionary: the full test suite and static analysis run on every change and block the deploy on failure. The suite runs on a hosted CI runner, or on a developer workstation with the completed run recorded as a commit status the pipeline verifies before it will skip the hosted run. Change-approval roles are described during procurement review. |
| Secure coding | The codebase uses framework-level defenses by default — parameterized queries via ActiveRecord, strong parameters, output escaping in views, CSRF tokens, attribute-level encryption, Pundit authorization at the controller boundary. Deviations require a written justification. |
| Security testing in development | Static analysis (Brakeman
, bundler-audit
, importmap audit) runs on every change. Tests cover authorization paths, encryption-at-rest invariants, audit-log emission, and rate-limit enforcement. See dependency and patch management
for the full toolchain. |
| Environment separation | Production and non-production environments are isolated. No production personal data is used outside production; staging and development use synthetic fixtures. |
| Vulnerability response | Reports acknowledged within 2 business days (see responsible disclosure ). Targets: critical issues remediated within 7 days, high within 30, medium within 90. Confirmed issues affecting deployed customers are reported through the incident register when they meet the register’s scope criteria. |
Dependency and patch management #
EthicsPortal does not deploy end-of-life software components. The application runs on actively supported releases of Rails, Ruby, PostgreSQL, and the underlying operating system; upstream security releases are applied on a rolling basis.
Dependencies are scanned continuously in continuous integration:
- Brakeman flags Rails-specific vulnerabilities on every change.
- bundler-audit checks the Gemfile against the Ruby Advisory Database on every change and again on a daily schedule, so newly disclosed advisories are caught even when no code has changed.
importmap auditscans JavaScript imports for known vulnerabilities on every change and again on a daily schedule.- Dependabot opens pull requests weekly for outdated Ruby gems and GitHub Actions, grouped by minor/patch updates.
Components reaching end-of-life upstream are replaced or upgraded before their support window closes.
Infrastructure #
| Component | Provider | Location |
|---|---|---|
| Application server and database | Hetzner | Nuremberg, Germany (EU) |
| File storage | Hetzner Object Storage | Nuremberg, Germany (EU) |
| Transactional email | Mailjet | France (EU) |
| Payment processing | Stripe | Ireland; other countries under applicable safeguards — see Privacy Notice |
- Core report and case-data storage is in the European Union. Email delivery and application telemetry have separate downstream supplier chains; see the DPA .
- No credit card numbers or payment credentials are stored on EthicsPortal servers. All payment data is handled by Stripe.
- Mailjet sends handler notifications and optional reporter receipts and new-message notices. Reporter notices contain the recipient email address, report access code and link, but not the report narrative. Mailjet’s downstream support chain may involve access outside the EEA; see the DPA .
- The marketing site is served via Cloudflare (CDN, United States); the reporting and handler portals are not. This separate public-site processing and its transfer safeguards are described in the Privacy Notice , not the customer DPA subprocessor list.
Backups and restore #
EthicsPortal operates two complementary backup layers, both retained within the EU:
| Layer | What | Where | Retention |
|---|---|---|---|
| Database | Daily PostgreSQL dumps via a Kamal accessory, encrypted with GnuPG symmetric AES-256 on the host before upload | Hetzner Object Storage, Nuremberg (EU) | 21 days for current versions; deleted versions expire after 7 additional days |
| Server | Full disk snapshots of the application host | Hetzner Cloud, Nuremberg (EU) | 7 days |
Recovery objectives. Recovery point objective (RPO) is 24 hours. Recovery time objective (RTO) is 4 hours. These objectives also appear in the service level agreement .
Restore testing. A restore drill runs automatically every month via a CI workflow, and on demand, restoring the latest dump into a disposable environment. It decrypts the dump, restores it, compares every table against live production, and then boots the application against the restored database to confirm an encrypted column still decrypts — row counts alone cannot distinguish a usable backup from ciphertext whose key no longer matches. Backup freshness is monitored continuously (alert if the most recent dump is older than 36 hours).
Encryption. Hetzner states that Object Storage does not encrypt objects at rest by default, so database dumps are encrypted before they leave the host: GnuPG symmetric AES-256, with the passphrase held only by the backup process and escrowed off-machine. Application-layer fields encrypted under Rails ActiveRecord Encryption remain separately encrypted inside each dump. File attachments remain an open gap: they are uploaded without server-side encryption and are not covered by the dump, so attachment-at-rest encryption is still outstanding.
Operational review #
Some operational materials are shared during procurement review rather than published in full on the open web, because they contain infrastructure and response detail that is more appropriate for controlled disclosure.
Topics available on request during procurement include:
- Privileged production-access summary
- Incident-response workflow and escalation contacts
- Business continuity and customer offboarding/export responses
Responsible disclosure #
If you discover a security vulnerability in EthicsPortal, please report it to security@ethicsportal.eu . We ask that you:
- Do not publicly disclose the vulnerability before we have had a chance to address it.
- Provide enough detail for us to reproduce and fix the issue.
- Do not access or modify other customers’ data.
We will acknowledge your report within 2 business days and aim to resolve confirmed vulnerabilities promptly.
No paid bug bounty. Disclosure is unrewarded: we do not pay for vulnerability reports, and we do not sign NDAs, agree commercial terms, or take calls as a precondition to receiving one. Send the affected asset, reproduction steps, and the impact you have established to the address above.
Last updated: