Security
Built for the keys to your kingdom.
Your App Store Connect API key signs every release you ship. Your iOS Distribution certificate is what the App Store trusts. HexSign treats both like the production secrets they are: encrypted with dedicated KMS keys, scoped to your team, and retrievable only over authenticated, audited paths.
Principles
Three commitments we hold ourselves to.
Encrypt before persist
Private keys, App Store Connect keys, and recovery secrets are encrypted before they touch storage, using KMS keys whose policies grant decrypt only to the API role.
Least privilege by default
Roles, IAM policies, and CI scopes are restrictive by default. Owners explicitly grant access; the system never expands it on its own. Defensive denies block privilege-escalation paths even if a policy were ever loosened by mistake.
Everything privileged is logged
Every action that touches signing material (generation, download, revocation, sync, role change, KMS decrypt) is recorded with who, what, when, and from where, in tamper-evident storage.
Data protection
Where your secrets live, and how they're protected.
HexSign separates short-lived application data from long-lived signing material. The two are stored in different services, encrypted with different keys, and accessed by different code paths.
KMS envelope encryption for CSR private keys
Private keys generated for certificate signing requests are encrypted with AWS KMS before they ever touch the database. Each ciphertext is bound to your organization with an encryption context, so a leaked row can't be decrypted out of tenant.
Customer-managed CMK for App Store Connect keys
The .p8 you paste into HexSign is written to AWS Secrets Manager bound to a HexSign-owned KMS key, not the AWS-managed default. Disabling that key in an incident makes every Apple secret unreadable atomically, and every decrypt is logged in CloudTrail.
Database encrypted at rest with a customer-managed key
Postgres uses AES-256 storage encryption keyed off a customer-managed KMS CMK with annual key rotation. Automated backups, snapshots, and point-in-time recovery inherit the same key, and deletion protection is enabled on the production cluster.
TLS-required database connections
The database parameter group enforces require_secure_transport=ON. Any client that forgets to opt in is rejected at the handshake. Lambda always negotiates TLS to RDS.
P12 reconstructed on demand, never stored
We don't keep .p12 files lying around. When you download a certificate, the private key is decrypted in-memory, packaged into a P12 with a freshly generated one-time password, and streamed to you. Nothing is persisted server-side.
Storage map
What ends up where.
| Asset | Where it lives | How it's protected |
|---|---|---|
| App Store Connect .p8 | AWS Secrets Manager | Per-account secret on a HexSign CMK; fetched JIT |
| CSR private keys | Postgres (encrypted column) | KMS envelope encryption, org-scoped context |
| Apple-issued certificates | Postgres | Public DER blob; public material by design |
| .p12 export | Built on demand, streamed | Fresh one-time password, never stored |
| Provisioning profiles | Postgres | Public material; integrity preserved by Apple sig |
| Refresh tokens (CLI) | OS keychain on your machine | Keychain / Secret Service / Credential Manager |
| CLI client secret | Identity provider only | Shown once at creation, never retrievable again |
| Audit & access logs | CloudTrail + S3 (KMS-encrypted) | Versioning, log-file validation, lifecycle to Glacier |
Network & access
No public database, no shared blast radius.
Private VPC, no public database
Postgres lives in private subnets with no public IP. The API runs in the same VPC and reaches the database over an internal security group with TLS. There is no route from the internet.
Strict CORS allowlist
The API rejects requests from unknown origins and never returns Access-Control-Allow-Origin: *. Browser clients are limited to dashboard.hexsign.net, hexsign.io, and known development origins.
TLS everywhere, signed JWTs in transit
All traffic terminates at API Gateway over HTTPS. Calls to App Store Connect are signed with ES256 JWTs from your decrypted .p8, generated per request and cached in-memory only.
Per-tenant isolation in every query
Every read and write is scoped by your organization id at the repository layer. Cross-tenant access requires bypassing the database access layer, not just guessing an id.
WAF in front of every endpoint
AWS WAF runs the AWS Common Rule Set, Known Bad Inputs, SQLi, and Anonymous IP rules on every request. Authentication, signup, and credential issuance routes carry tighter ceilings than read paths.
Per-IP and per-user rate ceilings
Rate-based rules cap both per-IP and per-bearer-token request volume in tight windows. A leaked token can't scrape your account at line rate, and a single IP can't hammer auth or signup.
Identity & access
Strong auth at the front door, RBAC inside.
MFA via TOTP or SMS
Multi-factor authentication is on by default at the user pool level. Users can register an authenticator app or fall back to SMS, and add or revoke factors from their account settings.
Strong password policy
Passwords require lowercase, uppercase, numbers, and symbols, with a minimum length enforced at the identity provider, not the application, so the rule can't be bypassed by API calls.
Verified email, optional verified phone
New accounts must verify their email before they can sign in. Phone numbers, when used, are verified before they can be relied on for SMS MFA or recovery.
RBAC with three roles
Owner, Admin, and Member roles gate billing, user management, CLI token issuance, and write actions. Role checks are enforced server-side on every privileged route. The dashboard's UI hiding is just a courtesy.
Per-user auth activity
Every sign-in, MFA challenge, password change, and risk decision is recorded with IP, device, and geo. You can review your own activity from settings; admins can review their team's.
Global sign-out revokes refresh tokens
Signing out of the dashboard performs a global sign-out, which invalidates the refresh token at the identity provider, not just the local session, so a stolen device can't keep refreshing.
Browser hardening
The dashboard takes the browser seriously.
Most attacks against SaaS apps don't compromise the server. They compromise the tab the operator is using to manage it. HexSign ships with the headers, cookies, and tokens needed to make those attacks fail closed.
Strict Content Security Policy
The dashboard ships with a CSP that allowlists exactly the origins it talks to (HexSign API, Cognito, and the icon CDN), blocks framing entirely, and disables inline event handlers and arbitrary plugin content.
HSTS with preload
Every response sets Strict-Transport-Security with a two-year max-age, includeSubDomains, and the preload flag. After a browser sees the header once, downgrades to HTTP are physically impossible.
Frame, sniff, and referrer protection
X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, and a Permissions-Policy that blocks camera, microphone, geolocation, payment, and USB by default.
Cross-origin isolation
Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy are set so the dashboard window can't be probed by a hostile opener and its resources can't be embedded by other origins.
Double-submit CSRF protection
A SameSite=Strict, Secure cookie pairs with an X-CSRF-Token header on every state-changing route. Constant-time comparison rejects mismatches. Defense-in-depth on top of Bearer-only auth.
No third-party trackers
The dashboard ships with zero session-replay, analytics, or marketing pixels. Your team's browsing of certificates, profiles, and CSRs is never visible to a third party.
CLI & CI
Retrieve signing material safely from your pipeline.
The HexSign CLI is the supported way to pull signing material onto a build agent. It uses OAuth 2.0 with PKCE on your laptop, swaps to scoped client credentials in CI, and ships as a cosign-signed release you can verify before running.
Refresh tokens in your OS keychain
On macOS the refresh token goes into Keychain, on Linux into the Secret Service, on Windows into Credential Manager. Plaintext on disk is never an option. Logout deletes both the keychain entry and any cached tokens.
OAuth + PKCE with cryptographic randomness
Local login uses OAuth 2.0 with PKCE (S256). The code verifier is 64 bytes from crypto/rand; state is 24 bytes from crypto/rand and validated on the callback. Mismatched state aborts the flow.
Resilient loopback callback
The OAuth flow tries a small range of pre-registered loopback ports and uses the first one that binds, so a busy port on a shared workstation doesn't block login.
0600 file modes for downloaded secrets
Downloaded .p12, .password, and .mobileprovision files are written with mode 0600 inside a 0700 directory. The CLI never prints the file contents, only the path it wrote to.
HTTPS-only, no skip-verify escape hatch
There is no --insecure flag, no environment variable, and no code path that disables TLS verification. Release builds can additionally pin the SPKI of our identity-provider intermediates so even a rogue CA can't impersonate us.
Cosign-signed releases
Every archive and the checksums file are signed with cosign in keyless mode and logged to the public Rekor transparency log. You can verify any download came from our release workflow before running it.
Scoped, revocable CI credentials
Service credentials issued from the dashboard are bound to read or write scopes. Routes for users, billing, and credential management are blocked for machine tokens. Revocation is immediate at the identity provider.
In-memory tokens in CI
When running with HEXSIGN_CLIENT_ID / HEXSIGN_CLIENT_SECRET, the CLI keeps the access token in process memory only. Nothing is written to the workspace, so there's nothing for a later step to leak.
- name: Verify the CLI you're about to run
run: |
VER=v1.2.3 ART=hexsign_1.2.3_linux_amd64.tar.gz
curl -sLO "https://github.com/hexsign/hexsign-cli/releases/download/$VER/$ART"
curl -sLO "https://github.com/hexsign/hexsign-cli/releases/download/$VER/$ART.sig"
curl -sLO "https://github.com/hexsign/hexsign-cli/releases/download/$VER/$ART.pem"
cosign verify-blob \
--certificate "$ART.pem" --signature "$ART.sig" \
--certificate-identity-regexp 'https://github.com/hexsign/hexsign-cli/.+' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
"$ART"
- name: Fetch signing material
env:
HEXSIGN_CLIENT_ID: ${{ secrets.HEXSIGN_CLIENT_ID }}
HEXSIGN_CLIENT_SECRET: ${{ secrets.HEXSIGN_CLIENT_SECRET }}
PROFILE_ID: ${{ vars.HEXSIGN_PROFILE_ID }}
CERT_ID: ${{ vars.HEXSIGN_CERT_ID }}
run: |
# Files land at 0600 inside a 0700 directory.
hexsign certificates download "$CERT_ID" --output-dir build/sign
hexsign profiles download "$PROFILE_ID" --output-dir build/sign
# security import / xcrun / xcodebuild from here…Service credentials are scoped to read or write and are blocked from user, billing, and credential-management routes. Even if a runner is compromised, the blast radius is the signing material it pulled, not your account.
Audit & operations
If it touched a secret, it left a trail.
AWS CloudTrail across every region
A multi-region trail records every management API call against the account, including every KMS Encrypt, Decrypt, and GenerateDataKey on the CSR-keys, database, and Apple-keys CMKs. We can answer 'who decrypted what, when' for any access, ours or theirs.
Tamper-evident audit storage
CloudTrail logs land in a private S3 bucket with versioning, KMS encryption, log-file integrity validation, a deny-on-insecure-transport policy, and a Glacier-then-expire lifecycle.
Append-only application audit log
Sensitive actions (Apple account connections, CSR generation, certificate revocation, CLI token issuance, role changes) are written to an audit table with org id, user id, action, entity, and a JSON detail payload.
GuardDuty threat detection
Amazon GuardDuty continuously analyses CloudTrail, VPC Flow Logs, DNS, S3, Lambda network activity, and RDS logins for indicators of compromise: credential exfiltration, anomalous API usage, known-bad IPs.
Sync history per Apple account
Every Apple Developer sync records a row with start time, end time, status, item counts, changes detected, and the error message if it failed. Useful for compliance, useful for debugging.
Verified Stripe webhooks
Billing webhooks are verified with HMAC-SHA256 and a five-minute timestamp window using constant-time comparison. We won't act on a forged or replayed event.
Infrastructure
AWS-native, infrastructure-as-code, least privilege.
The HexSign control plane is defined entirely in Terraform. Every IAM policy, KMS key, WAF rule, and security group is reviewable in version control. Production changes go through the same review process as application code.
AWS, in your region of choice
Postgres, Lambda, Cognito, KMS, Secrets Manager, CloudTrail, GuardDuty, and the WAF are deployed in a single AWS region. Region pinning is straightforward for customers with data residency requirements; talk to us.
Annual KMS key rotation
Customer-managed KMS keys backing the database, the CSR vault, and the Apple-key Secrets Manager entries all have automatic annual rotation enabled. Old key versions remain available for decrypting historical ciphertext.
Backups and PITR
Postgres has automated backups with point-in-time recovery, deletion protection on the cluster, and snapshots encrypted with the same customer-managed key as live data.
Locked-down IAM
Lambda execution roles can read the specific Secrets Manager paths they need and call the specific KMS keys they need, and nothing else. Explicit denies on Cognito privilege-escalation actions stop a future policy mistake from becoming a footgun.
Continuous vulnerability scanning
CI runs govulncheck on every change against the actual call graph, failing the build on any reachable CVE in our dependencies or the Go stdlib version we ship.
Threat detection on by default
GuardDuty inspects CloudTrail, VPC Flow Logs, DNS, S3 data events, Lambda network activity, and RDS logins. Findings are surfaced in the AWS console for review.
Shared responsibility
What you own, what we own.
HexSign owns
- Encrypting your secrets at rest with KMS-managed keys
- Tenant isolation, RBAC, and authorization on every request
- Patching the API, dashboard, CLI, and infrastructure
- Browser hardening (CSP, HSTS, CSRF, frame-ancestors)
- WAF, rate limiting, GuardDuty threat detection
- Tamper-evident audit log retention and CloudTrail data events
- Verifying webhooks and signing every CLI release
- Disclosing security incidents that affect your data
You own
- Enrolling MFA and using a strong, unique password
- Assigning Owner / Admin / Member roles thoughtfully
- Rotating CI client secrets on a cadence that fits your team
- Storing CI client secrets in your CI provider's secret manager
- Verifying the CLI signature on machines that run it in CI
- Running the CLI on machines with full-disk encryption enabled
- Revoking access for departing teammates and stale credentials
Reporting
Found something? We want to hear from you.
We acknowledge new reports within 3 business days. Critical and high-severity issues are patched within 7 days; medium within 30. We're happy to coordinate disclosure and credit you in the advisory.
Preferred: GitHub Security Advisory
Open a private advisory on the affected repository. It stays embargoed until a fix is released and lets us collaborate on a CVE if one is warranted.
Open an advisoryIf GitHub isn't an option, email us with a description, repro steps, and the affected component. Please don't open public issues for security reports.
support@hexsign.ioReady to consolidate signing?
Move your Apple secrets to a system that takes them seriously.
Free 7-day trial, no credit card. Bring your App Store Connect key and we'll keep it in the same kind of vault you'd build for it yourself.