0
# Security & Authentication
1
2
This documentation covers comprehensive security features in the Azure Storage Blob Java SDK, including authentication methods, SAS (Shared Access Signature) tokens, encryption, and access control.
3
4
## Authentication Methods
5
6
### Azure Identity (Recommended)
7
8
Modern authentication using Azure Active Directory and managed identities.
9
10
```java
11
import com.azure.identity.*;
12
import com.azure.storage.blob.BlobServiceClient;
13
import com.azure.storage.blob.BlobServiceClientBuilder;
14
15
// Default Azure Credential (tries multiple methods automatically)
16
DefaultAzureCredential defaultCredential = new DefaultAzureCredentialBuilder()
17
.build();
18
19
BlobServiceClient serviceClient = new BlobServiceClientBuilder()
20
.endpoint("https://myaccount.blob.core.windows.net")
21
.credential(defaultCredential)
22
.buildClient();
23
24
// Managed Identity (for Azure-hosted applications)
25
ManagedIdentityCredential managedIdentityCredential = new ManagedIdentityCredentialBuilder()
26
.clientId("user-assigned-identity-client-id") // Optional for user-assigned identity
27
.build();
28
29
BlobServiceClient managedIdentityClient = new BlobServiceClientBuilder()
30
.endpoint("https://myaccount.blob.core.windows.net")
31
.credential(managedIdentityCredential)
32
.buildClient();
33
34
// Service Principal (for applications)
35
ClientSecretCredential servicePrincipalCredential = new ClientSecretCredentialBuilder()
36
.clientId("application-client-id")
37
.clientSecret("application-client-secret")
38
.tenantId("azure-tenant-id")
39
.build();
40
41
BlobServiceClient spClient = new BlobServiceClientBuilder()
42
.endpoint("https://myaccount.blob.core.windows.net")
43
.credential(servicePrincipalCredential)
44
.buildClient();
45
46
// Interactive authentication (for user applications)
47
InteractiveBrowserCredential interactiveCredential = new InteractiveBrowserCredentialBuilder()
48
.clientId("application-client-id")
49
.redirectUrl("http://localhost:8080/auth/callback")
50
.build();
51
52
BlobServiceClient interactiveClient = new BlobServiceClientBuilder()
53
.endpoint("https://myaccount.blob.core.windows.net")
54
.credential(interactiveCredential)
55
.buildClient();
56
57
// Azure CLI credential (for development)
58
AzureCliCredential cliCredential = new AzureCliCredentialBuilder()
59
.build();
60
61
BlobServiceClient cliClient = new BlobServiceClientBuilder()
62
.endpoint("https://myaccount.blob.core.windows.net")
63
.credential(cliCredential)
64
.buildClient();
65
```
66
67
### Shared Key Authentication
68
69
Traditional authentication using storage account access keys.
70
71
```java
72
import com.azure.storage.common.StorageSharedKeyCredential;
73
74
// Create shared key credential
75
StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(
76
"mystorageaccount",
77
"base64-encoded-account-key-here"
78
);
79
80
BlobServiceClient sharedKeyClient = new BlobServiceClientBuilder()
81
.endpoint("https://mystorageaccount.blob.core.windows.net")
82
.credential(sharedKeyCredential)
83
.buildClient();
84
85
// Using connection string (contains account name and key)
86
String connectionString = "DefaultEndpointsProtocol=https;" +
87
"AccountName=mystorageaccount;" +
88
"AccountKey=base64-encoded-key;" +
89
"EndpointSuffix=core.windows.net";
90
91
BlobServiceClient connectionStringClient = new BlobServiceClientBuilder()
92
.connectionString(connectionString)
93
.buildClient();
94
95
// Key rotation example
96
public class StorageAccountKeyManager {
97
private volatile StorageSharedKeyCredential currentCredential;
98
private final String accountName;
99
100
public StorageAccountKeyManager(String accountName, String initialKey) {
101
this.accountName = accountName;
102
this.currentCredential = new StorageSharedKeyCredential(accountName, initialKey);
103
}
104
105
public void rotateKey(String newKey) {
106
StorageSharedKeyCredential newCredential = new StorageSharedKeyCredential(accountName, newKey);
107
108
// Test new credential
109
BlobServiceClient testClient = new BlobServiceClientBuilder()
110
.endpoint("https://" + accountName + ".blob.core.windows.net")
111
.credential(newCredential)
112
.buildClient();
113
114
try {
115
// Verify new credential works
116
testClient.getAccountInfo();
117
118
// Switch to new credential
119
this.currentCredential = newCredential;
120
System.out.println("Successfully rotated to new storage key");
121
122
} catch (Exception ex) {
123
System.err.println("Key rotation failed, keeping current key: " + ex.getMessage());
124
throw ex;
125
}
126
}
127
128
public StorageSharedKeyCredential getCurrentCredential() {
129
return currentCredential;
130
}
131
}
132
```
133
134
## SAS (Shared Access Signature) Tokens
135
136
### Service-Level SAS
137
138
Generate SAS tokens for service-level access.
139
140
```java
141
import com.azure.storage.blob.sas.*;
142
import com.azure.storage.common.sas.*;
143
import java.time.OffsetDateTime;
144
145
// Account-level SAS (access to multiple services)
146
OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
147
OffsetDateTime startTime = OffsetDateTime.now();
148
149
AccountSasSignatureValues accountSasValues = new AccountSasSignatureValues(
150
expiryTime,
151
AccountSasPermission.parse("rwdlacup"), // read, write, delete, list, add, create, update, process
152
AccountSasService.parse("bfqt"), // blob, file, queue, table services
153
AccountSasResourceType.parse("sco") // service, container, object
154
)
155
.setStartTime(startTime)
156
.setProtocol(SasProtocol.HTTPS_ONLY)
157
.setSasIpRange(SasIpRange.parse("192.168.1.0/24"));
158
159
// Generate account SAS
160
String accountSas = serviceClient.generateAccountSas(accountSasValues);
161
System.out.println("Account SAS token: " + accountSas);
162
163
// Use account SAS to create new client
164
String accountSasUrl = serviceClient.getAccountUrl() + "?" + accountSas;
165
BlobServiceClient sasServiceClient = new BlobServiceClientBuilder()
166
.endpoint(accountSasUrl)
167
.buildClient();
168
169
// Test account SAS permissions
170
try {
171
PagedIterable<BlobContainerItem> containers = sasServiceClient.listBlobContainers();
172
System.out.println("Account SAS is valid - can list containers");
173
} catch (Exception ex) {
174
System.err.println("Account SAS validation failed: " + ex.getMessage());
175
}
176
```
177
178
### Container-Level SAS
179
180
Generate SAS tokens for container access.
181
182
```java
183
// Container SAS with specific permissions
184
BlobContainerSasPermission containerPermission = new BlobContainerSasPermission()
185
.setReadPermission(true)
186
.setListPermission(true)
187
.setAddPermission(true)
188
.setCreatePermission(true)
189
.setWritePermission(false) // No write permission
190
.setDeletePermission(false); // No delete permission
191
192
BlobServiceSasSignatureValues containerSasValues = new BlobServiceSasSignatureValues(
193
OffsetDateTime.now().plusHours(6), // 6 hour expiry
194
containerPermission
195
)
196
.setStartTime(OffsetDateTime.now())
197
.setProtocol(SasProtocol.HTTPS_ONLY)
198
.setCacheControl("no-cache")
199
.setContentDisposition("attachment")
200
.setContentType("application/octet-stream")
201
.setContentLanguage("en-US")
202
.setContentEncoding("gzip");
203
204
// Generate container SAS
205
String containerSas = containerClient.generateSas(containerSasValues);
206
System.out.println("Container SAS token: " + containerSas);
207
208
// Create container URL with SAS
209
String containerSasUrl = containerClient.getBlobContainerUrl() + "?" + containerSas;
210
BlobContainerClient sasContainerClient = new BlobContainerClientBuilder()
211
.endpoint(containerSasUrl)
212
.buildClient();
213
214
// Test container SAS
215
try {
216
PagedIterable<BlobItem> blobs = sasContainerClient.listBlobs();
217
System.out.println("Container SAS is valid - can list blobs");
218
219
// Try to create a new blob
220
BlobClient sasBlob = sasContainerClient.getBlobClient("test-sas-blob.txt");
221
sasBlob.upload(BinaryData.fromString("Content created with SAS"), true);
222
System.out.println("Successfully created blob with container SAS");
223
224
} catch (Exception ex) {
225
System.err.println("Container SAS operation failed: " + ex.getMessage());
226
}
227
```
228
229
### Blob-Level SAS
230
231
Generate SAS tokens for individual blob access.
232
233
```java
234
// Blob SAS with read-only permission
235
BlobSasPermission blobPermission = new BlobSasPermission()
236
.setReadPermission(true)
237
.setAddPermission(false)
238
.setCreatePermission(false)
239
.setWritePermission(false)
240
.setDeletePermission(false);
241
242
BlobServiceSasSignatureValues blobSasValues = new BlobServiceSasSignatureValues(
243
OffsetDateTime.now().plusMinutes(30), // Short-lived token
244
blobPermission
245
)
246
.setStartTime(OffsetDateTime.now())
247
.setProtocol(SasProtocol.HTTPS_ONLY)
248
.setSasIpRange(SasIpRange.parse("203.0.113.0/24"));
249
250
// Generate blob SAS
251
String blobSas = blobClient.generateSas(blobSasValues);
252
System.out.println("Blob SAS token: " + blobSas);
253
254
// Create blob URL with SAS for sharing
255
String blobSasUrl = blobClient.getBlobUrl() + "?" + blobSas;
256
System.out.println("Shareable blob URL: " + blobSasUrl);
257
258
// Create client using blob SAS
259
BlobClient sasBlobClient = new BlobClientBuilder()
260
.endpoint(blobSasUrl)
261
.buildClient();
262
263
// Test blob SAS
264
try {
265
BinaryData content = sasBlobClient.downloadContent();
266
System.out.println("Successfully downloaded blob with SAS: " + content.getLength() + " bytes");
267
} catch (Exception ex) {
268
System.err.println("Blob SAS download failed: " + ex.getMessage());
269
}
270
271
// Advanced blob SAS with custom headers
272
BlobServiceSasSignatureValues advancedBlobSas = new BlobServiceSasSignatureValues(
273
OffsetDateTime.now().plusDays(7),
274
new BlobSasPermission().setReadPermission(true)
275
)
276
.setContentType("image/jpeg") // Force specific content type
277
.setContentDisposition("attachment; filename=photo.jpg") // Force download
278
.setCacheControl("public, max-age=86400") // Cache for 1 day
279
.setContentEncoding("identity")
280
.setContentLanguage("en-US");
281
282
String advancedBlobSasToken = blobClient.generateSas(advancedBlobSas);
283
```
284
285
### User Delegation SAS
286
287
Generate SAS using Azure AD credentials (more secure than account key SAS).
288
289
```java
290
// Get user delegation key (requires Azure AD authentication)
291
OffsetDateTime keyStart = OffsetDateTime.now();
292
OffsetDateTime keyExpiry = keyStart.plusDays(7);
293
294
try {
295
UserDelegationKey userDelegationKey = serviceClient.getUserDelegationKey(keyStart, keyExpiry);
296
297
// Create user delegation SAS
298
BlobServiceSasSignatureValues userDelegationSasValues = new BlobServiceSasSignatureValues(
299
OffsetDateTime.now().plusHours(2),
300
new BlobSasPermission().setReadPermission(true).setListPermission(true)
301
)
302
.setStartTime(OffsetDateTime.now())
303
.setProtocol(SasProtocol.HTTPS_ONLY);
304
305
// Generate user delegation SAS (more secure)
306
String userDelegationSas = serviceClient.generateUserDelegationSas(
307
userDelegationSasValues,
308
userDelegationKey
309
);
310
311
System.out.println("User delegation SAS: " + userDelegationSas);
312
313
// User delegation SAS for specific blob
314
BlobServiceSasSignatureValues blobUserDelegationSas = new BlobServiceSasSignatureValues(
315
OffsetDateTime.now().plusMinutes(30),
316
new BlobSasPermission().setReadPermission(true)
317
)
318
.setStartTime(OffsetDateTime.now())
319
.setProtocol(SasProtocol.HTTPS_ONLY);
320
321
String blobUserDelegationSasToken = blobClient.generateUserDelegationSas(
322
blobUserDelegationSas,
323
userDelegationKey
324
);
325
326
System.out.println("Blob user delegation SAS: " + blobUserDelegationSasToken);
327
328
} catch (Exception ex) {
329
System.err.println("User delegation key operation failed: " + ex.getMessage());
330
System.err.println("Ensure client is authenticated with Azure AD, not account key");
331
}
332
```
333
334
### SAS with Stored Access Policies
335
336
Use stored access policies for easier SAS management and revocation.
337
338
```java
339
import java.util.List;
340
import java.util.ArrayList;
341
342
// Create stored access policy
343
BlobSignedIdentifier readOnlyPolicy = new BlobSignedIdentifier()
344
.setId("ReadOnlyPolicy")
345
.setAccessPolicy(new BlobAccessPolicy()
346
.setPermissions("r") // read only
347
.setStartsOn(OffsetDateTime.now())
348
.setExpiresOn(OffsetDateTime.now().plusDays(30)));
349
350
BlobSignedIdentifier fullAccessPolicy = new BlobSignedIdentifier()
351
.setId("FullAccessPolicy")
352
.setAccessPolicy(new BlobAccessPolicy()
353
.setPermissions("racwdl") // read, add, create, write, delete, list
354
.setStartsOn(OffsetDateTime.now())
355
.setExpiresOn(OffsetDateTime.now().plusDays(7)));
356
357
List<BlobSignedIdentifier> policies = List.of(readOnlyPolicy, fullAccessPolicy);
358
359
// Set access policies on container
360
containerClient.setAccessPolicy(null, policies); // null = no public access
361
362
System.out.println("Stored access policies created");
363
364
// Generate SAS using stored policy (no expiry needed in SAS)
365
BlobServiceSasSignatureValues storedPolicySas = new BlobServiceSasSignatureValues()
366
.setIdentifier("ReadOnlyPolicy"); // Reference stored policy
367
368
String storedPolicySasToken = containerClient.generateSas(storedPolicySas);
369
System.out.println("SAS with stored policy: " + storedPolicySasToken);
370
371
// Update stored policy (affects all existing SAS tokens using this policy)
372
readOnlyPolicy.getAccessPolicy().setExpiresOn(OffsetDateTime.now().plusDays(15)); // Extend expiry
373
containerClient.setAccessPolicy(null, List.of(readOnlyPolicy, fullAccessPolicy));
374
375
System.out.println("Stored policy updated - existing SAS tokens now expire in 15 days");
376
377
// Revoke access by removing policy
378
containerClient.setAccessPolicy(null, List.of(fullAccessPolicy)); // Remove ReadOnlyPolicy
379
System.out.println("ReadOnlyPolicy revoked - existing SAS tokens using it are now invalid");
380
```
381
382
## Encryption
383
384
### Customer-Provided Keys (CPK)
385
386
Use customer-managed encryption keys for blob operations.
387
388
```java
389
import com.azure.storage.blob.models.CustomerProvidedKey;
390
import com.azure.storage.blob.models.EncryptionAlgorithmType;
391
import javax.crypto.KeyGenerator;
392
import javax.crypto.SecretKey;
393
import java.util.Base64;
394
395
// Generate or use existing encryption key
396
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
397
keyGen.init(256); // 256-bit key
398
SecretKey secretKey = keyGen.generateKey();
399
String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());
400
401
// Create customer-provided key
402
CustomerProvidedKey cpk = new CustomerProvidedKey(base64Key)
403
.setEncryptionAlgorithm(EncryptionAlgorithmType.AES256);
404
405
// Create service client with CPK
406
BlobServiceClient encryptedServiceClient = new BlobServiceClientBuilder()
407
.connectionString(connectionString)
408
.customerProvidedKey(cpk)
409
.buildClient();
410
411
BlobClient encryptedBlobClient = encryptedServiceClient
412
.getBlobContainerClient("encrypted-data")
413
.getBlobClient("sensitive-document.pdf");
414
415
// Upload encrypted content
416
String sensitiveContent = "This is highly sensitive information that must be encrypted";
417
encryptedBlobClient.upload(BinaryData.fromString(sensitiveContent), true);
418
419
System.out.println("Content uploaded with customer-provided encryption");
420
421
// Download requires the same key
422
BinaryData decryptedContent = encryptedBlobClient.downloadContent();
423
System.out.println("Decrypted content: " + decryptedContent.toString());
424
425
// Key management example
426
public class EncryptionKeyManager {
427
private final Map<String, CustomerProvidedKey> keys = new ConcurrentHashMap<>();
428
429
public CustomerProvidedKey generateKey(String keyId) {
430
try {
431
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
432
keyGen.init(256);
433
SecretKey secretKey = keyGen.generateKey();
434
String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());
435
436
CustomerProvidedKey cpk = new CustomerProvidedKey(base64Key)
437
.setEncryptionAlgorithm(EncryptionAlgorithmType.AES256);
438
439
keys.put(keyId, cpk);
440
441
// In production, securely store the key mapping
442
storeKeySecurely(keyId, base64Key);
443
444
return cpk;
445
} catch (Exception ex) {
446
throw new RuntimeException("Failed to generate encryption key", ex);
447
}
448
}
449
450
public CustomerProvidedKey getKey(String keyId) {
451
CustomerProvidedKey key = keys.get(keyId);
452
if (key == null) {
453
// In production, retrieve from secure store
454
String base64Key = retrieveKeySecurely(keyId);
455
if (base64Key != null) {
456
key = new CustomerProvidedKey(base64Key)
457
.setEncryptionAlgorithm(EncryptionAlgorithmType.AES256);
458
keys.put(keyId, key);
459
}
460
}
461
return key;
462
}
463
464
private void storeKeySecurely(String keyId, String key) {
465
// Implement secure key storage (Azure Key Vault, etc.)
466
System.out.println("Storing key " + keyId + " securely");
467
}
468
469
private String retrieveKeySecurely(String keyId) {
470
// Implement secure key retrieval
471
System.out.println("Retrieving key " + keyId + " securely");
472
return null; // Return actual key from secure storage
473
}
474
}
475
```
476
477
### Encryption Scope
478
479
Use server-side encryption scopes for automatic encryption management.
480
481
```java
482
// Create service client with default encryption scope
483
BlobServiceClient scopedServiceClient = new BlobServiceClientBuilder()
484
.connectionString(connectionString)
485
.encryptionScope("production-encryption-scope")
486
.buildClient();
487
488
// All blobs created through this client will use the encryption scope
489
BlobClient scopedBlob = scopedServiceClient
490
.getBlobContainerClient("scoped-container")
491
.getBlobClient("auto-encrypted-file.txt");
492
493
scopedBlob.upload(BinaryData.fromString("This content is automatically encrypted"), true);
494
495
// Container-level encryption scope enforcement
496
BlobContainerEncryptionScope containerScope = new BlobContainerEncryptionScope()
497
.setDefaultEncryptionScope("container-specific-scope")
498
.setPreventEncryptionScopeOverride(true); // Enforce scope for all blobs
499
500
BlobContainerCreateOptions scopedContainerOptions = new BlobContainerCreateOptions()
501
.setEncryptionScope(containerScope)
502
.setMetadata(Map.of("encryption", "enforced"));
503
504
containerClient.createWithResponse(scopedContainerOptions, Duration.ofMinutes(1), Context.NONE);
505
506
// Check encryption scope in blob properties
507
BlobProperties properties = scopedBlob.getProperties();
508
System.out.println("Encryption scope: " + properties.getEncryptionScope());
509
System.out.println("Server encrypted: " + properties.isServerEncrypted());
510
```
511
512
## Access Control and Permissions
513
514
### Conditional Access
515
516
Use request conditions for secure operations.
517
518
```java
519
// Conditional upload (only if blob doesn't exist)
520
BlobRequestConditions createOnlyConditions = new BlobRequestConditions()
521
.setIfNoneMatch("*"); // Only if no ETag exists (blob doesn't exist)
522
523
try {
524
blobClient.uploadWithResponse(
525
new BlobParallelUploadOptions(BinaryData.fromString("New content"))
526
.setRequestConditions(createOnlyConditions),
527
Duration.ofMinutes(5),
528
Context.NONE
529
);
530
System.out.println("Blob created successfully");
531
} catch (BlobStorageException ex) {
532
if (ex.getStatusCode() == 412) { // Precondition Failed
533
System.out.println("Blob already exists - upload skipped");
534
} else {
535
throw ex;
536
}
537
}
538
539
// Conditional update (only if blob hasn't changed)
540
BlobProperties currentProps = blobClient.getProperties();
541
String currentETag = currentProps.getETag();
542
543
BlobRequestConditions updateConditions = new BlobRequestConditions()
544
.setIfMatch(currentETag); // Only if ETag matches (no concurrent modifications)
545
546
try {
547
blobClient.uploadWithResponse(
548
new BlobParallelUploadOptions(BinaryData.fromString("Updated content"))
549
.setRequestConditions(updateConditions),
550
Duration.ofMinutes(5),
551
Context.NONE
552
);
553
System.out.println("Blob updated successfully");
554
} catch (BlobStorageException ex) {
555
if (ex.getStatusCode() == 412) {
556
System.out.println("Blob was modified by another process - update skipped");
557
} else {
558
throw ex;
559
}
560
}
561
562
// Time-based conditions
563
BlobRequestConditions timeConditions = new BlobRequestConditions()
564
.setIfModifiedSince(OffsetDateTime.now().minusHours(1)) // Only if modified in last hour
565
.setIfUnmodifiedSince(OffsetDateTime.now()); // Only if not modified since now
566
567
// Tag-based conditions
568
BlobRequestConditions tagConditions = new BlobRequestConditions()
569
.setTagsConditions("\"environment\" = 'staging' AND \"ready\" = 'true'");
570
```
571
572
### Lease-Based Concurrency Control
573
574
Use blob leases for exclusive access control.
575
576
```java
577
import com.azure.storage.blob.specialized.BlobLeaseClient;
578
import com.azure.storage.blob.specialized.BlobLeaseClientBuilder;
579
580
// Create lease client
581
BlobLeaseClient leaseClient = new BlobLeaseClientBuilder()
582
.blobClient(blobClient)
583
.buildClient();
584
585
try {
586
// Acquire exclusive lease
587
String leaseId = leaseClient.acquireLease(60); // 60 second lease
588
System.out.println("Acquired lease: " + leaseId);
589
590
// Perform exclusive operations with lease
591
BlobRequestConditions leaseConditions = new BlobRequestConditions()
592
.setLeaseId(leaseId);
593
594
// Update blob metadata exclusively
595
Map<String, String> exclusiveMetadata = Map.of(
596
"locked-by", "process-12345",
597
"lock-time", OffsetDateTime.now().toString(),
598
"operation", "exclusive-update"
599
);
600
601
blobClient.setMetadataWithResponse(
602
exclusiveMetadata,
603
leaseConditions,
604
Duration.ofSeconds(30),
605
Context.NONE
606
);
607
608
// Upload new content exclusively
609
blobClient.uploadWithResponse(
610
new BlobParallelUploadOptions(BinaryData.fromString("Exclusively updated content"))
611
.setRequestConditions(leaseConditions),
612
Duration.ofMinutes(2),
613
Context.NONE
614
);
615
616
System.out.println("Exclusive operations completed");
617
618
// Renew lease if more time needed
619
leaseClient.renewLease();
620
System.out.println("Lease renewed");
621
622
} finally {
623
try {
624
// Always release lease when done
625
leaseClient.releaseLease();
626
System.out.println("Lease released");
627
} catch (Exception ex) {
628
System.err.println("Failed to release lease: " + ex.getMessage());
629
}
630
}
631
632
// Automatic lease management
633
public class AutoLease implements AutoCloseable {
634
private final BlobLeaseClient leaseClient;
635
private final String leaseId;
636
637
public AutoLease(BlobClient blobClient, int durationSeconds) {
638
this.leaseClient = new BlobLeaseClientBuilder()
639
.blobClient(blobClient)
640
.buildClient();
641
this.leaseId = leaseClient.acquireLease(durationSeconds);
642
}
643
644
public String getLeaseId() {
645
return leaseId;
646
}
647
648
public void renewLease() {
649
leaseClient.renewLease();
650
}
651
652
@Override
653
public void close() {
654
try {
655
leaseClient.releaseLease();
656
} catch (Exception ex) {
657
System.err.println("Failed to release lease in auto-close: " + ex.getMessage());
658
}
659
}
660
}
661
662
// Usage with try-with-resources
663
try (AutoLease autoLease = new AutoLease(blobClient, 300)) { // 5 minute lease
664
BlobRequestConditions leaseConditions = new BlobRequestConditions()
665
.setLeaseId(autoLease.getLeaseId());
666
667
// Perform operations with automatic lease cleanup
668
blobClient.setMetadata(
669
Map.of("locked", "true"),
670
leaseConditions
671
);
672
673
// Lease automatically released when leaving try block
674
}
675
```
676
677
## Network Security
678
679
### IP Restrictions and Protocols
680
681
Configure network-level security for blob access.
682
683
```java
684
// SAS with IP restrictions
685
SasIpRange ipRange = SasIpRange.parse("192.168.1.0/24"); // Allow only from specific subnet
686
687
BlobServiceSasSignatureValues restrictedSas = new BlobServiceSasSignatureValues(
688
OffsetDateTime.now().plusHours(1),
689
new BlobSasPermission().setReadPermission(true)
690
)
691
.setProtocol(SasProtocol.HTTPS_ONLY) // Force HTTPS
692
.setSasIpRange(ipRange);
693
694
String restrictedSasToken = blobClient.generateSas(restrictedSas);
695
696
// Multiple IP ranges
697
SasIpRange multipleRanges = SasIpRange.parse("192.168.1.0/24,10.0.0.0/8");
698
699
// Single IP address
700
SasIpRange singleIp = SasIpRange.parse("203.0.113.42");
701
702
// HTTP client configuration with security settings
703
HttpClient secureHttpClient = new NettyAsyncHttpClientBuilder()
704
.connectionTimeout(Duration.ofSeconds(30))
705
.responseTimeout(Duration.ofMinutes(2))
706
.build();
707
708
BlobServiceClient secureClient = new BlobServiceClientBuilder()
709
.endpoint("https://myaccount.blob.core.windows.net") // Always use HTTPS
710
.credential(new DefaultAzureCredentialBuilder().build())
711
.httpClient(secureHttpClient)
712
.buildClient();
713
```
714
715
## Security Monitoring and Auditing
716
717
### Request Tracking and Logging
718
719
Implement comprehensive security logging and monitoring.
720
721
```java
722
import com.azure.core.util.Context;
723
724
// Security context for request tracking
725
public class SecurityContext {
726
private final String userId;
727
private final String sessionId;
728
private final String operationId;
729
private final OffsetDateTime timestamp;
730
private final String clientIp;
731
732
public SecurityContext(String userId, String sessionId, String clientIp) {
733
this.userId = userId;
734
this.sessionId = sessionId;
735
this.operationId = UUID.randomUUID().toString();
736
this.timestamp = OffsetDateTime.now();
737
this.clientIp = clientIp;
738
}
739
740
public Context toAzureContext() {
741
return Context.NONE
742
.addData("user-id", userId)
743
.addData("session-id", sessionId)
744
.addData("operation-id", operationId)
745
.addData("client-ip", clientIp)
746
.addData("timestamp", timestamp);
747
}
748
749
public void logSecurityEvent(String operation, String resource, boolean success, String details) {
750
String logEntry = String.format(
751
"[SECURITY] %s | User: %s | Session: %s | Operation: %s | Resource: %s | Success: %s | IP: %s | Details: %s",
752
timestamp,
753
userId,
754
sessionId,
755
operation,
756
resource,
757
success,
758
clientIp,
759
details
760
);
761
762
// In production, use proper logging framework
763
System.out.println(logEntry);
764
765
// Send to security monitoring system
766
sendToSecurityMonitoring(logEntry);
767
}
768
769
private void sendToSecurityMonitoring(String logEntry) {
770
// Implement security monitoring integration
771
// e.g., Azure Monitor, Splunk, etc.
772
}
773
}
774
775
// Secure blob operations with auditing
776
public class SecureBlobOperations {
777
private final BlobServiceClient serviceClient;
778
779
public SecureBlobOperations(BlobServiceClient serviceClient) {
780
this.serviceClient = serviceClient;
781
}
782
783
public boolean secureUpload(String containerName, String blobName,
784
BinaryData content, SecurityContext securityContext) {
785
try {
786
BlobClient blobClient = serviceClient
787
.getBlobContainerClient(containerName)
788
.getBlobClient(blobName);
789
790
// Add security metadata
791
Map<String, String> securityMetadata = Map.of(
792
"uploaded-by", securityContext.userId,
793
"upload-session", securityContext.sessionId,
794
"upload-time", securityContext.timestamp.toString(),
795
"client-ip", securityContext.clientIp
796
);
797
798
// Upload with security context
799
Response<BlockBlobItem> response = blobClient.uploadWithResponse(
800
new BlobParallelUploadOptions(content)
801
.setMetadata(securityMetadata)
802
.setRequestConditions(new BlobRequestConditions()
803
.setIfNoneMatch("*")), // Prevent overwriting
804
Duration.ofMinutes(5),
805
securityContext.toAzureContext()
806
);
807
808
securityContext.logSecurityEvent(
809
"UPLOAD",
810
containerName + "/" + blobName,
811
true,
812
"File uploaded successfully, size: " + content.getLength() + " bytes"
813
);
814
815
return true;
816
817
} catch (Exception ex) {
818
securityContext.logSecurityEvent(
819
"UPLOAD",
820
containerName + "/" + blobName,
821
false,
822
"Upload failed: " + ex.getMessage()
823
);
824
825
throw ex;
826
}
827
}
828
829
public BinaryData secureDownload(String containerName, String blobName,
830
SecurityContext securityContext) {
831
try {
832
BlobClient blobClient = serviceClient
833
.getBlobContainerClient(containerName)
834
.getBlobClient(blobName);
835
836
// Download with security context
837
BinaryData content = blobClient.downloadContentWithResponse(
838
new BlobDownloadOptions(),
839
Duration.ofMinutes(5),
840
securityContext.toAzureContext()
841
).getValue();
842
843
securityContext.logSecurityEvent(
844
"DOWNLOAD",
845
containerName + "/" + blobName,
846
true,
847
"File downloaded successfully, size: " + content.getLength() + " bytes"
848
);
849
850
return content;
851
852
} catch (Exception ex) {
853
securityContext.logSecurityEvent(
854
"DOWNLOAD",
855
containerName + "/" + blobName,
856
false,
857
"Download failed: " + ex.getMessage()
858
);
859
860
throw ex;
861
}
862
}
863
}
864
865
// Usage with security context
866
SecurityContext userContext = new SecurityContext("user123", "session456", "192.168.1.100");
867
SecureBlobOperations secureOps = new SecureBlobOperations(serviceClient);
868
869
// Secure upload
870
BinaryData uploadContent = BinaryData.fromString("Sensitive document content");
871
secureOps.secureUpload("secure-docs", "document.txt", uploadContent, userContext);
872
873
// Secure download
874
BinaryData downloadedContent = secureOps.secureDownload("secure-docs", "document.txt", userContext);
875
```
876
877
## Advanced Security Features
878
879
### Immutability Policies and Legal Holds
880
881
Implement compliance features for data retention.
882
883
```java
884
import com.azure.storage.blob.models.BlobImmutabilityPolicy;
885
import com.azure.storage.blob.models.BlobImmutabilityPolicyMode;
886
887
// Set immutability policy (WORM - Write Once Read Many)
888
OffsetDateTime immutabilityExpiry = OffsetDateTime.now().plusYears(7);
889
890
BlobImmutabilityPolicy immutabilityPolicy = new BlobImmutabilityPolicy()
891
.setExpiryTime(immutabilityExpiry)
892
.setPolicyMode(BlobImmutabilityPolicyMode.UNLOCKED); // Can be modified
893
894
try {
895
Response<BlobImmutabilityPolicy> policyResponse = blobClient.setImmutabilityPolicyWithResponse(
896
immutabilityPolicy,
897
new BlobRequestConditions(),
898
Duration.ofSeconds(30),
899
Context.NONE
900
);
901
902
System.out.println("Immutability policy set, expiry: " + policyResponse.getValue().getExpiryTime());
903
904
// Lock the policy (cannot be modified once locked)
905
Response<BlobImmutabilityPolicy> lockedPolicy = blobClient.setImmutabilityPolicyWithResponse(
906
new BlobImmutabilityPolicy()
907
.setExpiryTime(immutabilityExpiry)
908
.setPolicyMode(BlobImmutabilityPolicyMode.LOCKED),
909
new BlobRequestConditions(),
910
Duration.ofSeconds(30),
911
Context.NONE
912
);
913
914
System.out.println("Immutability policy locked");
915
916
} catch (Exception ex) {
917
System.err.println("Failed to set immutability policy: " + ex.getMessage());
918
}
919
920
// Set legal hold
921
try {
922
Response<BlobLegalHoldResult> legalHoldResponse = blobClient.setLegalHoldWithResponse(
923
true, // Set legal hold
924
Duration.ofSeconds(30),
925
Context.NONE
926
);
927
928
System.out.println("Legal hold set: " + legalHoldResponse.getValue().hasLegalHold());
929
930
// Remove legal hold when appropriate
931
blobClient.setLegalHoldWithResponse(
932
false, // Remove legal hold
933
Duration.ofSeconds(30),
934
Context.NONE
935
);
936
937
} catch (Exception ex) {
938
System.err.println("Failed to manage legal hold: " + ex.getMessage());
939
}
940
941
// Check compliance status
942
BlobProperties complianceProps = blobClient.getProperties();
943
System.out.println("Has legal hold: " + complianceProps.hasLegalHold());
944
945
BlobImmutabilityPolicy currentPolicy = complianceProps.getImmutabilityPolicy();
946
if (currentPolicy != null) {
947
System.out.println("Immutability expiry: " + currentPolicy.getExpiryTime());
948
System.out.println("Policy mode: " + currentPolicy.getPolicyMode());
949
}
950
```
951
952
## Security Best Practices
953
954
### Comprehensive Security Implementation
955
956
```java
957
public class SecureBlobStorageManager {
958
private final BlobServiceClient serviceClient;
959
private final EncryptionKeyManager keyManager;
960
private final SecurityAuditor auditor;
961
962
public SecureBlobStorageManager(String connectionString) {
963
// Use Azure Identity for authentication
964
this.serviceClient = new BlobServiceClientBuilder()
965
.endpoint(extractEndpointFromConnectionString(connectionString))
966
.credential(new DefaultAzureCredentialBuilder().build())
967
.buildClient();
968
969
this.keyManager = new EncryptionKeyManager();
970
this.auditor = new SecurityAuditor();
971
}
972
973
public void secureUploadWithComprehensiveSecurity(
974
String containerName,
975
String blobName,
976
byte[] content,
977
String userId,
978
Map<String, String> classifications) {
979
980
SecurityContext context = new SecurityContext(userId, UUID.randomUUID().toString(), getClientIp());
981
982
try {
983
// 1. Generate or retrieve encryption key
984
CustomerProvidedKey cpk = keyManager.getOrGenerateKey(blobName);
985
986
// 2. Create secure client with encryption
987
BlobClient secureClient = serviceClient
988
.getBlobContainerClient(containerName)
989
.getBlobClient(blobName)
990
.getCustomerProvidedKeyClient(cpk);
991
992
// 3. Set comprehensive metadata including security classifications
993
Map<String, String> securityMetadata = new HashMap<>();
994
securityMetadata.put("uploaded-by", userId);
995
securityMetadata.put("upload-time", OffsetDateTime.now().toString());
996
securityMetadata.put("encryption-key-id", keyManager.getKeyId(blobName));
997
securityMetadata.put("content-hash", calculateHash(content));
998
securityMetadata.putAll(classifications);
999
1000
// 4. Upload with security conditions
1001
BlobRequestConditions securityConditions = new BlobRequestConditions()
1002
.setIfNoneMatch("*") // Prevent overwriting
1003
.setTagsConditions(buildTagsCondition(classifications));
1004
1005
Response<BlockBlobItem> response = secureClient.uploadWithResponse(
1006
new BlobParallelUploadOptions(BinaryData.fromBytes(content))
1007
.setMetadata(securityMetadata)
1008
.setTags(classifications)
1009
.setRequestConditions(securityConditions)
1010
.setHeaders(new BlobHttpHeaders()
1011
.setContentType(detectContentType(blobName))
1012
.setCacheControl("private, no-cache")),
1013
Duration.ofMinutes(10),
1014
context.toAzureContext()
1015
);
1016
1017
// 5. Set compliance policies if required
1018
if (requiresCompliance(classifications)) {
1019
setCompliancePolicies(secureClient, classifications);
1020
}
1021
1022
// 6. Generate secure access SAS for authorized users
1023
String authorizedSas = generateAuthorizedSas(secureClient, userId, classifications);
1024
1025
// 7. Log security event
1026
auditor.logSecureUpload(userId, containerName, blobName, content.length, classifications);
1027
1028
System.out.println("Secure upload completed successfully");
1029
System.out.println("Authorized access SAS: " + authorizedSas);
1030
1031
} catch (Exception ex) {
1032
auditor.logSecurityFailure(userId, "SECURE_UPLOAD", containerName + "/" + blobName, ex);
1033
throw new SecurityException("Secure upload failed", ex);
1034
}
1035
}
1036
1037
private void setCompliancePolicies(BlobClient client, Map<String, String> classifications) {
1038
String retentionYears = classifications.get("retention-years");
1039
if (retentionYears != null) {
1040
int years = Integer.parseInt(retentionYears);
1041
OffsetDateTime expiry = OffsetDateTime.now().plusYears(years);
1042
1043
client.setImmutabilityPolicy(new BlobImmutabilityPolicy()
1044
.setExpiryTime(expiry)
1045
.setPolicyMode(BlobImmutabilityPolicyMode.UNLOCKED));
1046
}
1047
1048
if ("true".equals(classifications.get("legal-hold"))) {
1049
client.setLegalHold(true);
1050
}
1051
}
1052
1053
private String generateAuthorizedSas(BlobClient client, String userId, Map<String, String> classifications) {
1054
BlobSasPermission permission = new BlobSasPermission().setReadPermission(true);
1055
1056
// Adjust permissions based on classification
1057
if ("public".equals(classifications.get("classification"))) {
1058
permission.setListPermission(true);
1059
}
1060
1061
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(
1062
OffsetDateTime.now().plusHours(1), // Short-lived token
1063
permission
1064
)
1065
.setStartTime(OffsetDateTime.now())
1066
.setProtocol(SasProtocol.HTTPS_ONLY);
1067
1068
return client.generateSas(sasValues);
1069
}
1070
1071
private String calculateHash(byte[] content) {
1072
try {
1073
MessageDigest digest = MessageDigest.getInstance("SHA-256");
1074
byte[] hash = digest.digest(content);
1075
return Base64.getEncoder().encodeToString(hash);
1076
} catch (Exception ex) {
1077
throw new RuntimeException("Failed to calculate content hash", ex);
1078
}
1079
}
1080
1081
// Additional helper methods...
1082
}
1083
```
1084
1085
## Related Documentation
1086
1087
- [← Back to Overview](index.md)
1088
- [← Configuration Options](options.md)
1089
- [Service Client Operations →](service-client.md)
1090
- [Streaming & Advanced I/O →](streaming.md)