or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

blob-client.mdcontainer-client.mdindex.mdmodels.mdoptions.mdsecurity.mdservice-client.mdspecialized-clients.mdstreaming.md

security.mddocs/

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)