[2021 kcdc] lessons learned from enterprise cloud securty programs

This post is my live blog from KCDC. For more, see the 2021 KCDC live blog TOC

Speaker:Karl Ots

Twitter @karlgots

Book: Azure Security Handbook

————————-

Cloud Security

  • Need cloud native security – specific to cloud provider using
  • ”Cloud can be as safe or unsafe as what you did before”

Shared responsiblity

  • Customer responsiblity: data, endpoints
  • Shared responsiblity: identity, application, os/middleware, network
  • Cloud provider responsiblity: physical

Security Program

  • Need to balance speed/“shadow cloud” (someone signing up for their own cloud account) with existing security requirements/eisting EA, lack of security awareness
  • Phases – cloud straegy, governance model, security, guidelines (ex: implementation guidelines and refernee architecture)
  • Cloud security framework defines architecture, policies and controls to secure cloud envrionment
  • Don’t be a generic list of controls. Just be tailored. Doesn’t make sense to apply anything. Cloud security alliance has a list of about 200 sample controls. Don’t just go thru a list of controls in Excel.
  • Terraform (or other tools) typically built by subject matter experts. Ex: database expert writes terraform module for database security
  • Certified products/platform concept – if you use products/components/tools that are pre-approved, can get through security faster. Vetted already

Identity and Access Management (IAM)

  • Integrate with existing IAM processes
  • DevSecOps CI/CD deloys using Azure AD ideneity to application resources. Other identities to actuall run (but not deploy and change things).
  • Need to be able to provide the CI/CD credentials not available/used by others
  • Create a vending machine type system so have to request things. ex: give me a X. Makes automated to request things

Detection and Monitoring

  • Need to enforce logging across landing zone and anything deployed
  • Centralize logs.
  • Ok to have temporary copy as well to focus on new info. Also some alerts verbose and ony want to monior key ones
  • Can build custom alerts, but doesn’t scale. If 10K Azure resources all with own logs, can be unmanagable.
  • Integrate with your SIEM an SOC
  • Separate resource logs (ex: who accessed X) and application logs (what developers log)

Network security

  • Cross subscription, cross region and cross cloud
  • Traffic from platform and infrastructure as a service + app level
  • Can grant access through
    • RBAC in the subnet – fast to do, but dev has to do it
    • Pre-provisioned NICs – medium, thru centralized cloud operations
    • Outside Azure/cloud – slow, thru centralized ops

Supply chain

  • New attack with pre-provisioned agent https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure

My take

I’ve learned/used AWS. The speaker is an Azure expert. He tried to make the presentation as cloud agnostic as possible. It was realy good for me to see how much is common across clould providers. It was goo to understand how things I’m doing fit into the bigger picture and something I wish we did differently

why java records aren’t quite immutable

A new type called a “record” was previewed in Java 14 and released in Java 16. One of the benefits is that it creates immutable objects. Kind of.

An immutable record

This is record is an immutable object. Since it is a record, it is automatically final and has no setters. All is good.

public record Book (String title, int numPages) { }

A mutable record

There there is this record. Do you see why it is mutable?

public record Book (String title, int numPages, List<String> chapters) { }

The problem is that records use shallow immutability. The caller can’t change the “chapters” object to a different reference. The caller can change the values in the “chapters” to their heart’s content. That means this object is still mutable.

Here’s an example showing that the code prints [1, 2, 3] and therefore changes the list of chapters.

List<String> chapters = new ArrayList<>();
chapters.add("1");

Book book = new Book("Breaking and entering", 289, chapters);

chapters.add("2");
book.chapters().add("3");
System.out.println(book.chapters());

Making the record actually be immutable

It’s pretty easy to make the Book record actually be immutable. In fact, it only requires three extra lines! Records have a compact constructor which takes care of the setting fields for you. However, you can choose to change that behavior. In this example, I rely on the default set of “title” and “numPages”. However, for “chapters”, I choose to make an immutable copy to prevent changing the list.

public record Book (String title, int numPages, List<String> chapters) {

    public Book {
        chapters = List.copyOf(chapters);
    }
}

Now the test program fails with an UnsupportedOperationException. Much better. A real immutable record.

Supplemental Material: doPrivileged()

Privileged access was added to the Security objective for Oracle’s Java 11 1Z0-819 Exam, but was not part of the objectives for the now retired 1Z0-816 Exam. We created this Supplemental Material and are releasing it free of charge for all those readers who purchased one of our Java 11 Study Guides. You should read this section carefully before taking the 1Z0-819 Exam. See other changes on The 1Z0-819 Exam page.

Overview

Some Java programs run in an environment where the user does not have full control over the program. In other words, the program runs a privileged action on behalf of the user. The idea for the developer is straight-forward:

  • I have a privileged action I need run for a user
  • I need to verify the user has the proper permission before running the action
  • I need to make sure they are limited in what actions they can run
  • I need to make sure they don’t using caching or other tricks to skip the permission check

Java performs a privileged action using the AccessController.doPrivileged() method found in the java.security package. While using this method is not commonly used by most Java developers, it is required knowledge for the 1Z0-819 Exam.

1. The doPrivileged() method

A common example Oracle likes to use is reading a system property. The idea is that the programmer is only allowed to read a specific predefined system property.

import java.security.*;
public class MySecretReader {
   private static final String KEY = "secret.option";
   public String getSecret() {
      return AccessController.doPrivileged(
         new PrivilegedAction<String>() {
            public String run() {
               return System.getProperty(KEY);
            }
         });
   }
}

Here KEY is a constant that cannot be changed by the user. The idea here is that a user’s privilege is temporarily elevated so they can read the secret.option value within the system.

2. Ensure Principle of Least Privilege

When executing a privileged action, it is important to ensure that only the minimum access is granted. This is known as the principle of least privilege. Can you spot what’s wrong with the following example?

import java.security.*;
public class MySecretReader {
   public String getSecret(String magicWord) {
      return AccessController.doPrivileged(
         new PrivilegedAction<String>() {
            public String run() {
               return System.getProperty(magicWord);  // DON'T DO THIS!
            }
         });
   }
}

In this example, the caller is able to specify which value they want to read. This is considered a poor practice as it allows them to read any property within the system. Oracle refers this as a tainted input. Put simply, don’t trust anything the user provides when dealing with security. Also use a constant or predefined list to confirm they are accessing only what the original developer intended.

For the exam, be wary of any code that allows the user to access data that they specify, rather than the original programmer.

3. Don’t Expose Sensitive Information

Another important aspect of using doPrivileged() is ensuring sensitive data is protected. For example, can you spot the security risk in this code?

import java.security.*;
import java.util.*;
public class MySecretReader {
   private final List<Integer> codes = ...
   public List<Integer> getSecret() {
      return AccessController.doPrivileged(
         new PrivilegedAction<List<Integer>>() {
            public List<Integer> run() {
               return codes;  // DON'T DO THIS!
            }
         });
   }
}

Even though codes is marked final, the content can still be modified after the doPrivileged() is complete. This poses an unacceptable security risk. A much safer version would be to return an immutable copy of the list, such as:

      return AccessController.doPrivileged(
         new PrivilegedAction<List<Integer>>() {
            public List<Integer> run() {
               return Collections.unmodifiableList(codes);
            }
         });

Note that this works because Integer values are immutable. If the contents of the List were mutable, such as List<StringBuilder>, you want to copy them as well.

4. Don’t Elevate Permissions

Privilege elevation or escalation occurs when a user is mistakenly given access to a higher privilege than they should have access to. One way to prevent privilege elevation is to use to the AccessController.checkPermission() method before calling doPrivileged(), then execute the command with limited permissions.

import java.security.*;
public class MySecretReader {
   public void readData(Runnable task, String path) {
      // Check permission
      Permission permission = new java.io.FilePermission(path,"read");
      AccessController.checkPermission(permission);

      // Execute task with limited permission
      final var permissions = permission.newPermissionCollection();
      permissions.add(permission);
      AccessController.doPrivileged(
         new PrivilegedAction<Void>() {
            public Void run() {
               task.run();
               return null;
            }
         },
         // Using a limited context prevents privilege elevation
         new AccessControlContext(
            new ProtectionDomain[] {
               new ProtectionDomain(null, permissions)
            })
         );
   }
}

Don’t worry if you can’t write code like this, most developers never have to. For the exam, though, you should understand that the permission is being checked and the action is executed with a specific context.

5. Be wary of Permission Caching

The last rule you need to know for the exam is to be weary of cached permissions. It is perfectly acceptable to cache permission information, but the permission needs to be checked every time the user accesses it.

For example, assuming there’s a User class with appropriate attributes, methods and constructors, can you spot the problem in this code?

import java.security.*;
import java.util.*;
public class SecretFile {
   private static Map<String, User> data = new HashMap<>();
   public static SecretFile get(String key) {
      var cacheRecord = data.get(key);
      if (cacheRecord != null) {
         // DON'T DO THIS!
         return cacheRecord.getValue();
      }
      
      final var permission = Permission permission 
         = new PropertyPermission(key,"read");
      AccessController.checkPermission(permission);

      final var permissions = permission.newPermissionCollection();
      permissions.add(permission);
      var secret = AccessController.doPrivileged(
         new PrivilegedAction<SecretFile>() {
            public SecretFile run() {
               return new SecretFile();
            }
         }, new AccessControlContext(new ProtectionDomain[] {
               new ProtectionDomain(null, permissions) }));
      data.put(key, new User(secret, permission));
      return secret;
   }
}

Did you spot it? It might be hard to see, but there’s no permission check when the data is read from the cache! The permission is checked when the data is first read from the cache but not on subsequent calls. We can easily fix this though by checking the permission when it is read from the cache:

      var cacheRecord = data.get(key);
      if (cacheRecord != null) {
         AccessController.checkPermission(cacheRecord.getPermission());
         return cacheRecord.getValue();
      }

In this example, we see that cached permissions can be safe to use but we have to make sure the permission is validated when it is read the first time and on each request from the cache.

Conclusion

There, you’re done! This post covered the overall topics around Privileged Access that you need to know for the 1Z0-819 Exam. The following bullet points summarize the kinds of things you should be watching for on the 1Z0-819 Exam:

  • Always validate user input and never allow it to grant access to arbitrary data
  • Never give the user unlimited access to the system
  • Prevent privilege elevation by validating security
  • Never return privileged objects directly or in a way that they can be modified
  • Ensure cached permissions are validated on every call

If you enjoyed this section, you can read more about it in Oracle’s Secure Coding Guidelines for Java SE document.