Security Features
Security Features
This page catalogues every security control implemented in cloud-share. Each control lists its industry-standard framework reference, the component it applies to, and the threat it addresses.
Frameworks referenced on this page:
| Abbreviation | Standard |
|---|---|
| OWASP | OWASP Top 10 (2021) |
| NIST 800-53 | NIST SP 800-53 Rev. 5 — Security & Privacy Controls |
| NIST 800-132 | NIST SP 800-132 — Password-Based Key Derivation |
| NIST 800-52 | NIST SP 800-52 Rev. 2 — TLS Guidelines |
| NIST CSF | NIST Cybersecurity Framework 2.0 |
| RFC 5869 | HKDF — HMAC-based Key Derivation Function |
| FIPS 197 | AES — Advanced Encryption Standard |
| FIPS 198-1 | HMAC |
| STRIDE | STRIDE Threat Model |
| CWE | MITRE Common Weakness Enumeration |
Transit & Cryptography
Controls protecting data as it crosses physical network boundaries.
AES-256-GCM Encryption
| Property | Detail |
|---|---|
| Component | CloudShare.Core |
| Standard | FIPS 197 · NIST SP 800-38D · OWASP A02:2021 |
| CWE mitigated | CWE-311 — Missing Encryption of Sensitive Data |
All content — text, files, and zip bundles — is encrypted with AES-256-GCM at the application layer before it leaves the sender process. GCM (Galois/Counter Mode) is an authenticated encryption scheme: it provides both confidentiality (content cannot be read) and integrity (any modification to the ciphertext causes decryption to fail).
Each item uses a unique 12-byte random nonce, ensuring identical plaintexts always produce different ciphertexts.
HKDF-SHA256 Key Derivation
| Property | Detail |
|---|---|
| Component | CloudShare.Core |
| Standard | RFC 5869 · NIST SP 800-56C Rev. 2 · NIST SP 800-132 · OWASP A02:2021 |
| CWE mitigated | CWE-916 — Use of Password Hash With Insufficient Computational Effort |
The AES-256 key is derived from the shared secret using HKDF (HMAC-based Key Derivation Function, RFC 5869) with SHA-256 as the underlying hash. A fixed context binding (cloud-share-v1) is used as the HKDF salt, providing cryptographic domain separation.
flowchart LR
A["secret\n(12-char alphanumeric)"] --> B["HKDF-SHA256\nsalt: 'cloud-share-v1'"]
B --> C["32-byte AES-256 key\n(256 bits)"]
This replaces a naive SHA-256(secret) approach, which would not constitute a proper KDF under NIST SP 800-132. HKDF ensures the output key material is indistinguishable from random regardless of the input’s structure.
SHA-512 Integrity Hash
| Property | Detail |
|---|---|
| Component | CloudShare.Core |
| Standard | FIPS 198-1 · NIST SP 800-53 SC-8 · STRIDE: Tampering |
| CWE mitigated | CWE-345 — Insufficient Verification of Data Authenticity |
A SHA-512 hash of the plaintext is computed before encryption and stored in the queue item metadata. The receiver independently computes the hash after decryption and compares it to the sender’s value. A mismatch causes the item to be permanently deleted from the sender and the decrypted content to be discarded.
This provides defense-in-depth: the GCM authentication tag verifies ciphertext integrity at the transport layer; SHA-512 provides an independent application-level content integrity check.
⚠️ Warning: A hash mismatch is a strong signal of tampering. In normal operation this should never occur. If you see a mismatch, do not trust the content — restart the sender and re-queue the item.
TLS 1.3 Transport
| Property | Detail |
|---|---|
| Component | Sender · Receiver |
| Standard | NIST SP 800-52 Rev. 2 · OWASP A02:2021 · STRIDE: Information Disclosure |
| CWE mitigated | CWE-319 — Cleartext Transmission of Sensitive Information |
All traffic traverses Microsoft Dev Tunnels over HTTPS/TLS 1.3 using valid *.devtunnels.ms certificates. The receiver enforces HTTPS-only at startup — providing an HTTP URL is rejected with a validation error before any connection is attempted.
Bearer Token Authentication
| Property | Detail |
|---|---|
| Component | Sender · Receiver |
| Standard | OWASP A07:2021 · NIST SP 800-53 IA-3 · STRIDE: Information Disclosure |
| CWE mitigated | CWE-598 — Sensitive Query String in GET Request |
The shared session secret is transmitted exclusively via the Authorization: Bearer <token> HTTP header on every API call. It is never placed in URL query strings, path parameters, or JavaScript-accessible cookies. This keeps the secret out of access logs, browser history, proxy logs, Referer headers, and DevTunnels infrastructure logs.
Plaintext Zeroed After Decryption
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | NIST SP 800-53 SC-28 · STRIDE: Information Disclosure |
| CWE mitigated | CWE-316 — Cleartext Storage of Sensitive Information in Memory |
CryptographicOperations.ZeroMemory() is called in a finally block to overwrite all decrypted byte arrays immediately after use. This minimises the window during which sensitive plaintext resides in managed heap memory and reduces exposure in the event of a memory dump or swap file access.
Authentication & Access Control
Controls verifying identity and restricting access to local and remote endpoints.
Sender Web UI Token
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | OWASP A01:2021 · NIST SP 800-53 AC-3 · STRIDE: Elevation of Privilege |
| CWE mitigated | CWE-200 — Exposure of Sensitive Information |
A dedicated 128-bit random _uiToken (independent of the encryption secret) gates access to the sender web UI. The browser is opened to http://localhost:{port}/?token=<uiToken>. On the first visit the server validates the token, sets an HttpOnly SameSite=Strict cookie, then redirects to /. All subsequent visits are validated against the cookie. Unauthenticated requests receive 401 Unauthorized.
This ensures the HTML page containing the session secret is never served to unauthenticated local processes, browser extensions, or malware.
ℹ️ Info: The UI token and the encryption secret are independent values generated separately. The UI access token never participates in cryptographic operations; the encryption secret is never used as an access control token. This is the principle of key separation (NIST SP 800-53 SC-12).
Receiver Local Session Token
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | OWASP A01:2021 · NIST SP 800-53 AC-3 · STRIDE: Spoofing |
| CWE mitigated | CWE-306 — Missing Authentication for Critical Function |
A 128-bit random localToken is generated at startup and required on all receiver local endpoints. Fetch-based API calls use Authorization: Bearer <localToken>. The EventSource SSE connection (which cannot set custom headers) uses a local_token HttpOnly cookie instead.
Without this token, any process on the same machine — or any website mounting a DNS-rebinding attack — could receive, read, and delete all shared content. All receiver endpoints return 401 Unauthorized without a valid token.
Brute-Force Lockout
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | OWASP A07:2021 · NIST SP 800-53 AC-7 · STRIDE: Spoofing |
| CWE mitigated | CWE-307 — Improper Restriction of Excessive Authentication Attempts |
After 10 consecutive authentication failures, all sender API endpoints return HTTP 429 Too Many Requests with a Retry-After: 60 header. The lockout lasts 60 seconds. The failure counter is thread-safe (using Interlocked) and resets on any successful authentication. All lockout events are logged as warnings.
10 failures → 60-second lockout → HTTP 429 + Retry-After header
Constant-Time Comparisons
| Property | Detail |
|---|---|
| Component | Sender · Receiver |
| Standard | OWASP A02:2021 · STRIDE: Information Disclosure |
| CWE mitigated | CWE-208 — Observable Timing Discrepancy |
All secret, token, and hash comparisons use CryptographicOperations.FixedTimeEquals(). Standard string equality short-circuits on the first differing character — an attacker can determine the correct value byte-by-byte by measuring response times. Constant-time comparison always takes the same time regardless of where (or whether) a difference occurs.
This applies to: sender secret comparison, sender UI token comparison, receiver local token comparison, and SHA-512 hash verification.
HTTPS-Only Enforcement
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | OWASP A02:2021 · NIST SP 800-52 Rev. 2 |
| CWE mitigated | CWE-295 — Improper Certificate Validation |
The receiver’s NormalizeUrl helper rejects any URL that does not begin with https:// with an explicit validation error: “Only HTTPS URLs are supported.” This prevents accidental use of unencrypted HTTP connections to the sender tunnel, which would expose the secret and all encrypted data to network observers.
Input Validation & Injection Prevention
Controls rejecting malformed or malicious input.
File Size Limits
| Item type | Limit | Enforcement |
|---|---|---|
| Text | 10 MB | Application-level + Kestrel |
| File | 100 MB | Application-level + Kestrel |
| Zip bundle | 100 MB | Application-level + Kestrel |
The Kestrel web server is configured with MaxRequestBodySize = 110_000_000 (110 MB). This hard limit runs before application code and cannot be bypassed via chunked transfer encoding. Application-level size checks run as a second gate (NIST SP 800-53 SC-5, STRIDE: Denial of Service, CWE-770).
Filename Sanitisation
| Property | Detail |
|---|---|
| Component | Sender (upload) · Receiver (download) |
| Standard | OWASP A03:2021 · STRIDE: Tampering |
| CWE mitigated | CWE-22 — Path Traversal · CWE-73 — External Control of File Name |
Filenames are sanitised on both sides:
- Sender: path separators (
/,\), null bytes, oversized names, and empty values are stripped or replaced before the filename is stored in queue metadata. - Receiver:
Path.GetFileName()is applied to the sender-supplied filename regardless of what the sender sends. A compromised sender cannot deliver path-traversal filenames like../../.bashrc.
Item ID Validation
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | OWASP A03:2021 · STRIDE: Tampering |
| CWE mitigated | CWE-20 — Improper Input Validation |
The {id} route parameter on /receive/{id} and /item/{id} is validated against ^[a-zA-Z0-9_-]{8,64}$ before being forwarded as part of the sender URL. This prevents SSRF-adjacent crafted path injection that could cause the receiver to access unintended sender endpoints.
Exception Details Suppressed
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | OWASP A09:2021 · STRIDE: Information Disclosure |
| CWE mitigated | CWE-209 — Error Message Containing Sensitive Information |
Exceptions caught in proxy endpoints return Results.StatusCode(502) rather than re-throwing. This prevents ASP.NET Core’s developer exception page from leaking stack traces, internal class names, library versions, and file paths to HTTP clients.
Anti-CSRF Protection
Controls preventing cross-site request forgery against local API endpoints.
Origin / Referer Header Validation
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | OWASP A01:2021 · NIST SP 800-53 SC-8 · STRIDE: Elevation of Privilege |
| CWE mitigated | CWE-352 — Cross-Site Request Forgery |
Every mutating endpoint (POST /upload/text, POST /upload/file, POST /upload/zip, DELETE /item/{id}) validates the Origin or Referer header when present. If the header is present and does not resolve to the sender’s own localhost:<port> origin, the request is rejected with HTTP 403 Forbidden.
API clients (curl, Postman, the receiver) that omit browser-injected headers are not rejected — they are independently protected by Bearer token authentication which browsers cannot automatically include cross-origin.
Browser request → Origin: https://evil.example.com → HTTP 403 Forbidden
API request → no Origin/Referer header → proceed (Bearer token required)
Receiver proxy → Authorization: Bearer <secret> → proceed
Resource Exhaustion & DoS Prevention
Controls preventing abuse of shared resources.
Rate Limiting (Receiver)
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | NIST SP 800-53 SC-5 · OWASP A04:2021 · STRIDE: Denial of Service |
| CWE mitigated | CWE-770 — Allocation of Resources Without Limits |
The receiver applies a fixed-window rate limiter to all proxied endpoints: a maximum of 30 requests per 10-second window per client. Requests beyond this threshold receive HTTP 429 Too Many Requests. This prevents a compromised sender or rogue local process from flooding the receiver proxy with requests and exhausting socket or memory resources.
Response Size Caps (Receiver)
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | NIST SP 800-53 SC-5 · OWASP A04:2021 · STRIDE: Denial of Service |
| CWE mitigated | CWE-400 — Uncontrolled Resource Consumption |
The receiver enforces hard response-size caps when proxying sender responses:
| Endpoint | Cap | Enforcement |
|---|---|---|
GET /queue (JSON list) | 1 MB | ReadCappedAsync |
GET /item/{id} (encrypted content) | 150 MB | ReadCappedAsync |
ReadCappedAsync checks the Content-Length header first (fast rejection), then streams the response body into a counter. If the cap is exceeded at any point, the operation is aborted, the oversized response is discarded, and HTTP 502 is returned to the browser. Each oversize event is logged with the SECURITY_EVENT: prefix.
SSE Message Validation (Receiver)
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | NIST CSF PR.DS · OWASP A03:2021 · STRIDE: Tampering |
| CWE mitigated | CWE-20 — Improper Input Validation |
The receiver validates every SSE (text/event-stream) message received from the sender before forwarding it to the browser. A line-by-line parser extracts only data: lines and verifies each as valid JSON containing a known type field. Unknown type values and malformed JSON are silently dropped; all other SSE line types (event:, retry:, blank lines) are forwarded without modification.
This prevents a compromised or malicious sender from injecting arbitrary JavaScript or HTML via the event stream that could be executed in the receiver’s browser context.
Zip Aggregate Input Check
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | NIST SP 800-53 SC-5 · STRIDE: Denial of Service |
| CWE mitigated | CWE-770 — Allocation of Resources Without Limits |
Before creating an in-memory zip archive, the sender sums the sizes of all uploaded files in the multipart request. If the aggregate total exceeds the 100 MB limit, the request is rejected with HTTP 400 before any MemoryStream allocation. This prevents zip-bomb-style attacks where many small files are uploaded to force a large allocation inside the zip-building process.
SSE Connection Limit
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | NIST SP 800-53 SC-5 · STRIDE: Denial of Service |
| CWE mitigated | CWE-770 — Allocation of Resources Without Limits |
The sender enforces a maximum of 10 concurrent SSE (Server-Sent Events) connections. Requests beyond this limit receive HTTP 429. Each accepted connection uses a bounded Channel<string>(capacity: 100) — slow or unresponsive clients cannot cause unbounded memory growth. Slow writers drop events rather than blocking the broadcaster.
Queue Size Limit
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | NIST SP 800-53 SC-5 · STRIDE: Denial of Service |
| CWE mitigated | CWE-770 — Allocation of Resources Without Limits |
The in-memory queue is capped at 50 items. Upload attempts when the queue is full return HTTP 429. Each file item can be up to 100 MB of base64-encoded ciphertext in memory (~133 MB per item); without a cap, a malicious authenticated sender could exhaust process memory.
Separate SSE HttpClient
| Property | Detail |
|---|---|
| Component | Receiver |
| Standard | NIST CSF PR.DS · STRIDE: Denial of Service |
| CWE mitigated | CWE-400 — Uncontrolled Resource Consumption |
The receiver uses two distinct HttpClient instances:
| Client | Timeout | Used for |
|---|---|---|
http | 30 seconds | All API calls (/queue, /receive, /item, /delete) |
httpSse | InfiniteTimeSpan | SSE stream (/events) only, managed by CancellationToken |
This prevents slow-loris attacks on API calls (30-second hard timeout) while allowing the long-lived SSE connection to remain open indefinitely — which is a requirement of the text/event-stream protocol.
HTTP Security Headers
Headers applied by middleware to every HTTP response from both local servers.
| Header | Value | Protection |
|---|---|---|
Content-Security-Policy | default-src 'self' cdn.jsdelivr.net; script-src 'self' cdn.jsdelivr.net 'unsafe-inline'; style-src 'self' cdn.jsdelivr.net 'unsafe-inline' | Blocks injection of scripts from unauthorised origins (OWASP A05:2021, CWE-1021) |
X-Frame-Options | DENY | Prevents clickjacking via <iframe> embedding (OWASP A05:2021) |
X-Content-Type-Options | nosniff | Prevents MIME-sniffing attacks (OWASP A05:2021, CWE-693) |
Referrer-Policy | no-referrer | Suppresses Referer header — prevents local URL leakage to CDN requests (STRIDE: Information Disclosure) |
CORS — Localhost-Only Origin
| Property | Detail |
|---|---|
| Component | Sender · Receiver |
| Standard | OWASP A01:2021 · OWASP A05:2021 |
| CWE mitigated | CWE-942 — Permissive Cross-domain Policy |
Both servers apply a strict CORS policy permitting only requests from their own localhost:<port> origin. This blocks DNS-rebinding attacks, where a malicious website temporarily resolves a domain to 127.0.0.1 and then issues cross-origin requests to steal content from the local server.
Secret & Credential Hygiene
Controls preventing unintentional disclosure of secrets and credentials.
Secret Masked in Console
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | OWASP A09:2021 · NIST SP 800-53 IA-5 · STRIDE: Information Disclosure |
| CWE mitigated | CWE-532 — Insertion of Sensitive Information into Log File |
The secret token is displayed as ****<last-4-chars> in the sender’s startup table by default. The full value is revealed only when the --show-secret flag is passed. This protects against shoulder-surfing, screen recordings, and screenshots inadvertently shared with the secret visible.
Session File Permissions
| Property | Detail |
|---|---|
| Component | CloudShare.Core |
| Standard | NIST SP 800-53 AC-3 · AC-6 |
| CWE mitigated | CWE-732 — Incorrect Permission Assignment for Critical Resource |
receiver-session.json (which stores the last-used tunnel URL and session secret) is written with chmod 600 (UserRead | UserWrite) on Linux and macOS immediately after creation. This prevents other local accounts from reading saved credentials, which is especially important on shared or multi-user systems.
CLOUDSHARE_SECRET Environment Variable
| Property | Detail |
|---|---|
| Component | Sender · Receiver |
| Standard | OWASP A09:2021 · NIST SP 800-53 IA-5 · STRIDE: Information Disclosure |
| CWE mitigated | CWE-214 — Invocation of Process Using Visible Sensitive Information |
Both tools read the CLOUDSHARE_SECRET environment variable at startup:
- Sender: if set, its value overrides the randomly generated secret, allowing scripted or CI sessions to use a known secret without passing it as a command-line flag.
- Receiver: if set, the interactive startup prompt is skipped entirely — the tool connects immediately without user input.
Using an environment variable keeps the secret out of:
- The shell history file (
.bash_history,.zsh_history) - The process argument list (visible to
ps aux, Task Manager, and/proc/<pid>/cmdline) - Automated build logs where command output is captured
# Safer than: cloud-share-receiver --secret aB3kP9mQ2rTs
export CLOUDSHARE_SECRET="aB3kP9mQ2rTs"
cloud-share-receiver
Key Separation: UI Token vs Encryption Secret
| Property | Detail |
|---|---|
| Component | Sender |
| Standard | NIST SP 800-53 SC-12 · OWASP A02:2021 |
| CWE mitigated | CWE-321 — Use of Hard-coded Cryptographic Key |
The _uiToken (web UI access control) and _secret (AES key material) are independently generated random values that serve entirely different purposes. The UI token is never used in cryptographic operations; the encryption secret is never used as an authentication token. Reusing one value for both purposes would create a transitive secret exposure risk — compromising either value would compromise both functions.
Supply Chain & CI/CD
Controls verifying the integrity of third-party components and the delivery pipeline.
Subresource Integrity (SRI) Hashes
| Property | Detail |
|---|---|
| Component | Sender · Receiver (web UI) |
| Standard | OWASP A06:2021 · NIST CSF PR.DS · STRIDE: Tampering |
| CWE mitigated | CWE-494 — Download of Code Without Integrity Check |
Every <script> and <link> tag loading a CDN resource carries an integrity= attribute (SRI hash) and crossorigin="anonymous". The browser verifies the cryptographic hash of each downloaded asset before executing or applying it. If the CDN delivers a tampered or substituted file, the browser rejects it outright.
All CDN versions are pinned exactly — no floating @3.x.x wildcards:
| Resource | Version | Hash algorithm |
|---|---|---|
| Bootstrap CSS | 5.3.8 | SHA-384 |
| Bootstrap JS bundle | 5.3.8 | SHA-384 |
| Bootstrap Icons CSS | 1.11.3 | SHA-256 |
| Alpine.js | 3.15.8 | SHA-256 |
Security Event Logging (SECURITY_EVENT:)
| Property | Detail |
|---|---|
| Component | Sender · Receiver |
| Standard | NIST SP 800-53 AU-2 · NIST CSF DE.AE · OWASP A09:2021 |
| CWE mitigated | CWE-778 — Insufficient Logging of Security-Relevant Events |
Security-significant events are prefixed with SECURITY_EVENT: in the console log. This prefix allows log aggregators, SIEM tools, and shell grep queries to reliably identify and alert on security incidents without parsing unstructured text.
Events logged with this prefix include:
| Event | Trigger |
|---|---|
SECURITY_EVENT: Decryption failure | AES-GCM auth tag rejected or decryption threw |
SECURITY_EVENT: Hash mismatch | SHA-512 post-decryption hash does not match sender’s stored hash |
SECURITY_EVENT: Response too large | ReadCappedAsync exceeded 1 MB (queue) or 150 MB (item) |
SECURITY_EVENT: Malformed SSE message | SSE data: line failed JSON parse or contained unknown type |
SECURITY_EVENT: Brute-force lockout | 10 consecutive auth failures triggered 60-second lockout |
curl|bash Install Notice (DependencyManager)
| Property | Detail |
|---|---|
| Component | CloudShare.Core |
| Standard | OWASP A06:2021 · NIST CSF ID.SC |
| CWE mitigated | CWE-494 — Download of Code Without Integrity Check |
On Linux, the DevTunnels CLI is installed via curl | bash. Before executing this command, DependencyManager prints a security notice reminding users that curl | bash executes remote code without local signature verification, and provides a link to the GitHub releases page for manual SHA-256 verification as an alternative.
Vulnerable Package Scanning
| Property | Detail |
|---|---|
| Component | CI/CD |
| Standard | OWASP A06:2021 · NIST CSF ID.RA · NIST SP 800-53 SA-11 |
| CWE mitigated | CWE-1104 — Use of Unmaintained Third Party Components |
Every CI build runs dotnet list package --vulnerable --include-transitive. If any direct or transitive NuGet dependency has a known CVE, the build step fails and the issue must be resolved before the pipeline can continue.
Dependabot — Automated Dependency Updates
| Property | Detail |
|---|---|
| Component | CI/CD |
| Standard | OWASP A06:2021 · NIST CSF ID.RA-1 |
| CWE mitigated | CWE-1104 — Use of Unmaintained Third Party Components |
.github/dependabot.yml is configured for weekly NuGet dependency scanning. GitHub Dependabot automatically raises pull requests when outdated or vulnerable packages are detected, ensuring the dependency tree stays current without manual monitoring.
Summary
| Category | Controls |
|---|---|
| Transit & Cryptography | AES-256-GCM · HKDF-SHA256 · SHA-512 hash · TLS 1.3 · Bearer auth · Memory zeroing |
| Authentication & Access Control | UI token · Local session token · Brute-force lockout · Constant-time comparisons · HTTPS-only |
| Anti-CSRF | Origin/Referer header validation (HTTP 403) |
| Input Validation | File size limits · Kestrel body limit · Filename sanitisation (×2) · ID validation · Error suppression · Zip aggregate input check |
| Resource & DoS Protection | SSE connection limit · Queue size limit · Split SSE/API HttpClients · Rate limiting (30 req/10s) · Response size caps (1MB/150MB) · SSE message validation |
| HTTP Security Headers | CSP · X-Frame-Options · X-Content-Type-Options · Referrer-Policy · CORS |
| Secret & Credential Hygiene | Console masking · Session file permissions · Key separation · CLOUDSHARE_SECRET env var |
| Supply Chain & CI/CD | Vulnerable package scan · Dependabot · SRI hashes (CDN) · curl|bash notice |
| Observability | SECURITY_EVENT: structured logging prefix |
| Total controls | 41 |
See also: Encryption Architecture for the full cryptographic flow, and Best Practices for operational guidance.