{"slug":"object-storage-security","title":"Object Storage Security","description":"Comprehensive security features to protect your data in DanubeData Object Storage.","section":"Features","url":"https://docs.danubedata.ro/object-storage-security","markdown_url":"https://docs.danubedata.ro/object-storage-security.md","breadcrumbs":[{"title":"Features","slug":null},{"title":"Storage","slug":"storage-overview"},{"title":"Object Storage Security","slug":"object-storage-security"}],"headings":[{"level":1,"title":"Object Storage Security","id":"object-storage-security"},{"level":2,"title":"Overview","id":"overview"},{"level":2,"title":"Encryption","id":"encryption"},{"level":3,"title":"Encryption at Rest","id":"encryption-at-rest"},{"level":3,"title":"Encryption in Transit","id":"encryption-in-transit"},{"level":2,"title":"Access Control","id":"access-control"},{"level":3,"title":"Access Keys","id":"access-keys"},{"level":4,"title":"Creating Access Keys","id":"creating-access-keys"},{"level":4,"title":"Permission Levels","id":"permission-levels"},{"level":4,"title":"Best Practices for Access Keys","id":"best-practices-for-access-keys"},{"level":3,"title":"Public Access Control","id":"public-access-control"},{"level":4,"title":"Enabling Public Access","id":"enabling-public-access"},{"level":4,"title":"Public Access Warning","id":"public-access-warning"},{"level":3,"title":"Bucket Policies","id":"bucket-policies"},{"level":4,"title":"Example: Allow Read from Specific IP","id":"example-allow-read-from-specific-ip"},{"level":4,"title":"Example: Deny Delete Operations","id":"example-deny-delete-operations"},{"level":2,"title":"Object Lock (WORM)","id":"object-lock-worm"},{"level":3,"title":"Retention modes","id":"retention-modes"},{"level":3,"title":"Enabling Object Lock","id":"enabling-object-lock"},{"level":3,"title":"Per-object controls","id":"per-object-controls"},{"level":2,"title":"SFTP Access","id":"sftp-access"},{"level":2,"title":"CORS Configuration","id":"cors-configuration"},{"level":3,"title":"Why CORS?","id":"why-cors"},{"level":3,"title":"Configuring CORS","id":"configuring-cors"},{"level":3,"title":"CORS Rule Options","id":"cors-rule-options"},{"level":3,"title":"CORS Best Practices","id":"cors-best-practices"},{"level":2,"title":"Presigned URLs","id":"presigned-urls"},{"level":3,"title":"Download URL","id":"download-url"},{"level":3,"title":"Upload URL","id":"upload-url"},{"level":3,"title":"Presigned URL Security","id":"presigned-url-security"},{"level":2,"title":"Security Best Practices","id":"security-best-practices"},{"level":3,"title":"1. Principle of Least Privilege","id":"1-principle-of-least-privilege"},{"level":3,"title":"2. Enable Versioning for Critical Data","id":"2-enable-versioning-for-critical-data"},{"level":3,"title":"3. Implement Lifecycle Rules","id":"3-implement-lifecycle-rules"},{"level":3,"title":"4. Monitor Access Key Usage","id":"4-monitor-access-key-usage"},{"level":3,"title":"5. Use Separate Buckets for Sensitivity","id":"5-use-separate-buckets-for-sensitivity"},{"level":3,"title":"6. Audit and Logging","id":"6-audit-and-logging"},{"level":2,"title":"Compliance","id":"compliance"},{"level":3,"title":"GDPR Compliance","id":"gdpr-compliance"},{"level":3,"title":"Data Retention","id":"data-retention"},{"level":2,"title":"Troubleshooting","id":"troubleshooting"},{"level":3,"title":"\"Access Denied\" Errors","id":"access-denied-errors"},{"level":3,"title":"CORS Errors in Browser","id":"cors-errors-in-browser"},{"level":3,"title":"Presigned URL Not Working","id":"presigned-url-not-working"},{"level":2,"title":"Next Steps","id":"next-steps"}],"format":"markdown","word_count":1789,"content":"# Object Storage Security\n\nComprehensive security features to protect your data in DanubeData Object Storage.\n\n## Overview\n\nDanubeData Object Storage provides multiple layers of security to ensure your data is protected at rest and in transit. This guide covers encryption, access control, and security best practices.\n\n## Encryption\n\n### Encryption at Rest\n\nAll data stored in DanubeData Object Storage is automatically encrypted using **AES-256** encryption.\n\n- **Server-Side Encryption (SSE-S3)**: Enabled by default\n- **No configuration required**: Automatic for all objects\n- **Zero performance impact**: Hardware-accelerated encryption\n\n### Encryption in Transit\n\nAll connections use **TLS 1.3** for maximum security:\n\n- **HTTPS only**: HTTP connections are not accepted\n- **Strong cipher suites**: Modern TLS configuration\n- **Certificate validation**: Automatic certificate management\n\n## Access Control\n\n### Access Keys\n\nAccess keys are the primary method for authenticating to your buckets via the S3 API.\n\n#### Creating Access Keys\n\n**Via Dashboard:**\n1. Navigate to your bucket\n2. Click **Access Keys** tab\n3. Click **Create Access Key**\n4. Configure name and permissions\n5. Copy the secret key immediately (shown only once)\n\n**Via API:**\n```bash\ncurl -X POST https://api.danubedata.ro/v1/storage/buckets/{bucket_id}/access-keys \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-app-key\",\n    \"permissions\": [\"read\", \"write\"]\n  }'\n```\n\n#### Permission Levels\n\n| Permission | Description | Operations |\n|------------|-------------|------------|\n| **read** | Read-only access | GetObject, ListObjects, HeadObject |\n| **write** | Create and update objects | PutObject, CopyObject |\n| **delete** | Remove objects | DeleteObject, DeleteObjects |\n| **admin** | Full bucket control | All operations including policy changes |\n\n#### Best Practices for Access Keys\n\n1. **Use least privilege**: Only grant permissions that are needed\n2. **Rotate regularly**: Create new keys and retire old ones periodically\n3. **Set expiration dates**: Use expiring keys for temporary access\n4. **Monitor usage**: Check last-used timestamps to identify unused keys\n5. **Never commit to code**: Use environment variables or secrets management\n\n### Public Access Control\n\nBy default, all buckets are private. You can enable public read access for specific use cases.\n\n#### Enabling Public Access\n\n**Via Dashboard:**\n1. Navigate to your bucket\n2. Click **Settings**\n3. Toggle **Public Access** to enabled\n4. Confirm the security warning\n\n**Via API:**\n```bash\ncurl -X PATCH https://api.danubedata.ro/v1/storage/buckets/{bucket_id} \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"public_access\": true\n  }'\n```\n\n#### Public Access Warning\n\nWhen public access is enabled:\n- Anyone can read objects in your bucket\n- Objects are accessible via direct URL\n- Egress traffic will count against your quota\n- Use for static websites, public assets only\n\n### Bucket Policies\n\nFor fine-grained access control, you can apply bucket policies from the bucket's **Settings** tab. The most common use is an **IP allow-list** — restricting a bucket so its objects are only reachable from source IP ranges you trust (office, VPN, or application servers), expressed in CIDR notation.\n\nYou can build a policy two ways:\n\n- **Visual editor** — Choose an effect (Allow or Deny), actions, resources, and source-IP conditions without hand-writing JSON.\n- **JSON editor** — Paste or write policy statements directly, with live validation as you type.\n\nCustom statements merge automatically with the bucket's public-access setting and any scoped access keys, so you never have to restate them. Built-in guardrails reject any policy that would stop you (or DanubeData) from managing the bucket's own policy, so you can always recover.\n\n> The policy editor is available on buckets hosted on the current high-durability storage endpoint.\n\n#### Example: Allow Read from Specific IP\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": \"*\",\n      \"Action\": [\"s3:GetObject\"],\n      \"Resource\": [\"arn:aws:s3:::my-bucket/*\"],\n      \"Condition\": {\n        \"IpAddress\": {\n          \"aws:SourceIp\": \"192.168.1.0/24\"\n        }\n      }\n    }\n  ]\n}\n```\n\n#### Example: Deny Delete Operations\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Principal\": \"*\",\n      \"Action\": [\"s3:DeleteObject\", \"s3:DeleteBucket\"],\n      \"Resource\": [\n        \"arn:aws:s3:::my-bucket\",\n        \"arn:aws:s3:::my-bucket/*\"\n      ]\n    }\n  ]\n}\n```\n\n## Object Lock (WORM)\n\nObject Lock provides write-once-read-many (WORM) protection. When a bucket has Object Lock enabled, an object version can be locked so that it cannot be deleted or overwritten until its retention period expires — useful for regulatory compliance, ransomware protection, and tamper-proof audit trails.\n\n### Retention modes\n\n| Mode | Who can remove or shorten the lock |\n|------|------------------------------------|\n| **Governance** | Normal requests cannot delete or overwrite a locked version; a user with elevated permissions can shorten or remove the lock when genuinely needed. |\n| **Compliance** | No one — including the bucket owner — can delete a locked version or shorten its retention until the retention period expires. |\n\n### Enabling Object Lock\n\n- **Enable at bucket creation.** Object Lock can only be turned on when the bucket is created — it cannot be added to an existing bucket. Enabling it automatically enables versioning, which Object Lock requires.\n- **Default retention (optional).** Set a default mode and a retention period (1–36,500 days) that applies automatically to every new object version.\n\n### Per-object controls\n\n- **Retention** — Apply or extend a retention date on an individual object version with `PutObjectRetention`.\n- **Legal hold** — Place an indefinite hold on a specific object version with `PutObjectLegalHold`, independent of any retention period. The version cannot be deleted until you explicitly remove the hold.\n\n```bash\n# Apply Compliance-mode retention until a specific date\naws --endpoint-url https://s3.danubedata.ro s3api put-object-retention \\\n  --bucket my-bucket \\\n  --key important.log \\\n  --retention '{\"Mode\":\"COMPLIANCE\",\"RetainUntilDate\":\"2027-01-01T00:00:00Z\"}'\n\n# Place a legal hold on an object\naws --endpoint-url https://s3.danubedata.ro s3api put-object-legal-hold \\\n  --bucket my-bucket \\\n  --key important.log \\\n  --legal-hold '{\"Status\":\"ON\"}'\n```\n\n> Because a locked object version consumes storage until its retention expires (and delete markers don't reclaim it), pair Object Lock with a realistic retention period to keep costs predictable.\n\n## SFTP Access\n\nIn addition to the S3 API, you can reach a bucket over **SFTP** — handy for legacy tools, batch jobs, or users that speak SFTP but not S3.\n\n```\nHost: sftp.new-s3.danubedata.ro\nPort: 2222\n```\n\n- Create SFTP users from the bucket's **SFTP** section in the dashboard.\n- Each SFTP user is bound to a single bucket and one of your S3 access keys, so its permissions follow that key (read-only, read-write, or full). This gives you per-key access control over SFTP, exactly as with the S3 API.\n- The dashboard shows the exact username to use; the password is that access key's secret. Revoking or rotating the underlying access key immediately revokes the SFTP login too.\n\n## CORS Configuration\n\nConfigure Cross-Origin Resource Sharing (CORS) to allow web applications to access your bucket.\n\n### Why CORS?\n\nBrowsers block cross-origin requests by default. If your web application needs to upload or download files directly from Object Storage, you must configure CORS.\n\n### Configuring CORS\n\n**Via Dashboard:**\n1. Navigate to your bucket\n2. Click **Settings** → **CORS**\n3. Add CORS rules\n4. Save changes\n\n**Via API:**\n```bash\ncurl -X PUT https://api.danubedata.ro/v1/storage/buckets/{bucket_id}/cors \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"cors_rules\": [\n      {\n        \"allowed_origins\": [\"https://myapp.com\", \"https://*.myapp.com\"],\n        \"allowed_methods\": [\"GET\", \"PUT\", \"POST\", \"DELETE\"],\n        \"allowed_headers\": [\"*\"],\n        \"expose_headers\": [\"ETag\", \"x-amz-meta-*\"],\n        \"max_age_seconds\": 3600\n      }\n    ]\n  }'\n```\n\n### CORS Rule Options\n\n| Field | Description | Example |\n|-------|-------------|---------|\n| `allowed_origins` | Domains allowed to make requests | `[\"https://myapp.com\"]` |\n| `allowed_methods` | HTTP methods allowed | `[\"GET\", \"PUT\"]` |\n| `allowed_headers` | Request headers allowed | `[\"Content-Type\", \"Authorization\"]` |\n| `expose_headers` | Response headers exposed to browser | `[\"ETag\"]` |\n| `max_age_seconds` | How long browser caches preflight | `3600` |\n\n### CORS Best Practices\n\n1. **Be specific with origins**: Avoid using `*` in production\n2. **Limit methods**: Only allow methods your app needs\n3. **Set appropriate max_age**: Balance security and performance\n4. **Test thoroughly**: Use browser dev tools to verify CORS\n\n## Presigned URLs\n\nGenerate temporary, secure URLs for sharing objects without exposing credentials.\n\n### Download URL\n\n```python\nimport boto3\n\ns3 = boto3.client(\n    's3',\n    endpoint_url='https://s3.danubedata.ro',\n    aws_access_key_id='YOUR_ACCESS_KEY',\n    aws_secret_access_key='YOUR_SECRET_KEY'\n)\n\n# Valid for 1 hour\nurl = s3.generate_presigned_url(\n    'get_object',\n    Params={'Bucket': 'my-bucket', 'Key': 'secret-file.pdf'},\n    ExpiresIn=3600\n)\n```\n\n### Upload URL\n\n```python\n# Generate upload URL\nupload_url = s3.generate_presigned_url(\n    'put_object',\n    Params={\n        'Bucket': 'my-bucket',\n        'Key': 'uploads/user-file.txt',\n        'ContentType': 'text/plain'\n    },\n    ExpiresIn=3600\n)\n\n# Client can upload using:\n# curl -X PUT -H \"Content-Type: text/plain\" --data-binary @file.txt \"$upload_url\"\n```\n\n### Presigned URL Security\n\n- **Time-limited**: URLs expire after the specified duration\n- **Operation-specific**: Each URL is valid for one operation\n- **Cannot be revoked**: Once generated, valid until expiry\n- **Audit trail**: Track which key generated the URL\n\n## Security Best Practices\n\n### 1. Principle of Least Privilege\n\nCreate separate access keys for different applications with only the permissions they need:\n\n```bash\n# Read-only key for analytics\ncurl -X POST .../access-keys -d '{\"name\": \"analytics\", \"permissions\": [\"read\"]}'\n\n# Write-only key for uploads\ncurl -X POST .../access-keys -d '{\"name\": \"uploader\", \"permissions\": [\"write\"]}'\n\n# Full access for backups\ncurl -X POST .../access-keys -d '{\"name\": \"backup\", \"permissions\": [\"read\", \"write\", \"delete\"]}'\n```\n\n### 2. Enable Versioning for Critical Data\n\nProtect against accidental deletion or overwrites:\n\n```bash\ncurl -X PATCH https://api.danubedata.ro/v1/storage/buckets/{bucket_id} \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -d '{\"versioning_enabled\": true}'\n```\n\n### 3. Implement Lifecycle Rules\n\nAutomatically delete temporary files and old versions:\n\n```json\n{\n  \"rules\": [\n    {\n      \"id\": \"delete-temp-files\",\n      \"prefix\": \"temp/\",\n      \"expiration_days\": 7\n    },\n    {\n      \"id\": \"delete-old-versions\",\n      \"noncurrent_version_expiration_days\": 30\n    }\n  ]\n}\n```\n\n### 4. Monitor Access Key Usage\n\nRegularly review access keys:\n\n1. Check last-used timestamps\n2. Delete unused keys\n3. Rotate keys periodically\n4. Set expiration dates for temporary access\n\n### 5. Use Separate Buckets for Sensitivity\n\nOrganize data by sensitivity level:\n\n- `production-public` - Public assets, CDN content\n- `production-private` - Application data, user uploads\n- `production-sensitive` - PII, financial data (most restricted)\n\n### 6. Audit and Logging\n\nEnable access logging to track all bucket operations:\n\n- Who accessed what objects\n- When operations occurred\n- Success/failure status\n- Source IP addresses\n\n## Compliance\n\n### GDPR Compliance\n\nDanubeData Object Storage is fully GDPR compliant:\n\n- **Data residency**: All data stored in Germany (EU)\n- **Encryption**: AES-256 at rest, TLS 1.3 in transit\n- **Access control**: Fine-grained permission management\n- **Data deletion**: Objects can be permanently deleted\n- **Audit trail**: Complete access logging available\n- **Data Processing Agreement**: Download a signed DPA (PDF) for your records from your account's legal settings\n- **Immutability**: Use [Object Lock](#object-lock-worm) in Compliance mode to enforce retention for records that must not be altered or deleted\n\n### Data Retention\n\nUse lifecycle rules to implement data retention policies:\n\n```json\n{\n  \"rules\": [\n    {\n      \"id\": \"gdpr-retention\",\n      \"prefix\": \"user-data/\",\n      \"expiration_days\": 365,\n      \"noncurrent_version_expiration_days\": 90\n    }\n  ]\n}\n```\n\n## Troubleshooting\n\n### \"Access Denied\" Errors\n\n1. **Verify credentials**: Check access key and secret\n2. **Check permissions**: Ensure key has required permissions\n3. **Bucket ownership**: Verify key belongs to bucket's team\n4. **Bucket policy**: Check for deny rules blocking access\n\n### CORS Errors in Browser\n\n1. **Check origin**: Ensure your domain is in allowed_origins\n2. **Check method**: Verify HTTP method is allowed\n3. **Check headers**: Ensure required headers are allowed\n4. **Browser cache**: Clear preflight cache and retry\n\n### Presigned URL Not Working\n\n1. **Check expiry**: URL may have expired\n2. **Clock sync**: Ensure server clock is accurate\n3. **URL encoding**: Don't manually modify the URL\n4. **Key revocation**: Original key may have been deleted\n\n## Next Steps\n\n- [Object Storage Product Overview](https://docs.danubedata.ro/object-storage) - Full feature documentation\n- [Object Storage Quick Start](https://docs.danubedata.ro/object-storage-quickstart) - Get started guide\n\n---\n\n**Questions?** Contact support at support@danubedata.ro\n","prev":{"title":"Object Storage Quick Start","slug":"object-storage-quickstart","url":"https://docs.danubedata.ro/object-storage-quickstart","markdown_url":"https://docs.danubedata.ro/object-storage-quickstart.md","json_url":"https://docs.danubedata.ro/object-storage-quickstart.json"},"next":{"title":"S3 Access Keys","slug":"object-storage-access-keys","url":"https://docs.danubedata.ro/object-storage-access-keys","markdown_url":"https://docs.danubedata.ro/object-storage-access-keys.md","json_url":"https://docs.danubedata.ro/object-storage-access-keys.json"},"index_url":"https://docs.danubedata.ro/index.json"}