Secure_Coding_Guidelines_for_Java
Secure_Coding_Guidelines_for_Java
0 Fundamentals
The Java security mechanism can also be used to implement the principle of
least privilege, although it does not provide protection as strong as lower-
level mechanisms. This can be implemented statically by restricting
permissions through policy files and dynamically with the use of
the java.security.AccessController.doPrivileged mechanism (see Section 9).
Note that when taking this approach, the security manager should be
installed as early as possible (ideally from the command-line). Delaying
installation may result in security-sensitive operations being performed
before the security manager is in place, which could reduce the effectiveness
of security checks or cause objects to be created with excessive permissions.
Rich Internet Applications (RIA) can specify their requested permissions via
an applet parameter or in the JNLP1. A signed JAR can also include a manifest
attribute that specifies whether it must run in a sandbox or with all
permissions (see [11]). If a sandboxed applet or application attempts to
execute security-sensitive code, the JRE will throw a security exception. RIAs
should follow the principle of least privilege, and should be configured to run
with the least amount of necessary permissions. Running a RIA with all
permissions should be avoided whenever possible.
For instance, a web browser is outside of the system for a web server.
Equally, a web server is outside of the system for a web browser. Therefore,
web browser and server software should not rely upon the behavior of the
other for security.
When auditing trust boundaries, there are some questions that should be
kept in mind. Are the code and data used sufficiently trusted? Could a library
be replaced with a malicious implementation? Is untrusted configuration data
being used? Is code calling with lower privileges adequately protected
against?
It is also important to understand the security model and best practices for
third-party software. Identify secure configuration options, any security-
related tasks performed by the code (e.g. cryptographic functions or
serialization), and any security considerations for APIs being used.
Understanding past security issues and attack patterns against the code can
also help to use it in a more secure manner. For example, if past security
issues have applied to certain functionality or configurations, avoiding those
may help to minimize exposure.
1 Denial of Service
Input into a system should be checked so that it will not cause excessive
resource consumption disproportionate to that used to request the service.
Common affected resources are CPU cycles, memory, disk space, and file
descriptors.
In rare cases it may not be practical to ensure that the input is reasonable. It
may be necessary to carefully combine the resource checking with the logic
of processing the data. In addition to attacks that cause excessive resource
consumption, attacks that result in persistent DoS, such as wasting
significant disk space, need be defended against. Server systems should be
especially robust against external attacks.
Guideline 1-1 / DOS-1: Beware of activities that may use
disproportionate resources
Examples of attacks include:
Requesting a large image size for vector graphics. For instance, SVG and
font files.
Integer overflow errors can cause sanity checking of sizes to fail.
An object graph constructed by parsing a text or binary stream may have
memory requirements many times that of the original data.
"Zip bombs" whereby a short file is very highly compressed. For instance,
ZIPs, GIFs and gzip encoded HTTP contents. When decompressing files, it is
better to set limits on the decompressed data size rather than relying upon
compressed size or meta-data.
"Billion laughs attack" whereby XML entity expansion causes an XML
document to grow dramatically during parsing. Set
the XMLConstants.FEATURE_SECURE_PROCESSING feature to enforce reasonable
limits.
Causing many keys to be inserted into a hash table with the same hash
code, turning an algorithm of around O(n) into O(n 2).
Regular expressions may exhibit catastrophic backtracking.
XPath expressions may consume arbitrary amounts of processor time.
Java deserialization and Java Beans XML deserialization of malicious data
may result in unbounded memory or CPU usage.
Detailed logging of unusual behavior may result in excessive output to log
files.
Infinite loops can be caused by parsing some corner case data. Ensure that
each iteration of a loop makes some progress.
Processing JARs from untrusted sources may lead to resource exhaustion
and/or unexpected runtime behavior.
Image files can contain excessively large values for dimensions that may
result in large memory allocations. When loading image files, one should
check the image dimensions via reading the header in addition to checking
the file/input size, before loading the images. The application can then
check for allowable image sizes based on the application's memory and use
cases.
When dealing with resource intensive scenarios, the stability of an
application can benefit from efforts to detect and prevent resource
exhaustion situations before they occur, instead of letting them occur and
silently handling the resulting Exception or Error. When a complex operation
causes memory to run low, it may have side effects in other threads, leading
to their failure and resulting in denial-of-service conditions. Also, high CPU or
IO use from a complex operation may cause other threads' responses to
clients to time out, affecting availability. Therefore, reasonable (and
configurable) thresholds that are applied before complex operations may
increase the overall responsiveness and robustness of an application.
Copy
Copied to Clipboard
Error: Could not Copy
long sum = readFileBuffered(InputStream in -> {
long current = 0;
for (;;) {
int b = in.read();
if (b == -1) {
return current;
}
current += b;
}
});
Copy
Copied to Clipboard
Error: Could not Copy
public R readFileBuffered(
InputStreamHandler handler
) throws IOException {
try (final InputStream in = Files.newInputStream(path)) {
handler.handle(new BufferedInputStream(in));
}
}
For resources without support for the enhanced feature, use the standard
resource acquisition and release. Attempts to rearrange this idiom typically
result in errors and makes the code significantly harder to follow.
Copy
Copied to Clipboard
Error: Could not Copy
public R locked(Action action) {
lock.lock();
try {
return action.run();
} finally {
lock.unlock();
}
}
Ensure that any output buffers are flushed in the case that output was
otherwise successful. If the flush fails, the code should exit via an exception.
Copy
Copied to Clipboard
Error: Could not Copy
public void writeFile(
OutputStreamHandler handler
) throws IOException {
try (final OutputStream rawOut = Files.newOutputStream(path)) {
final BufferedOutputStream out =
new BufferedOutputStream(rawOut);
handler.handle(out);
out.flush();
}
}
Copy
Copied to Clipboard
Error: Could not Copy
public void bufferedWriteGzipFile(
OutputStreamHandler handler
) throws IOException {
try (
final OutputStream rawOut = Files.newOutputStream(path);
final OutputStream compressedOut =
new GzipOutputStream(rawOut);
) {
final BufferedOutputStream out =
new BufferedOutputStream(compressedOut);
handler.handle(out);
out.flush();
}
}
Note, however, that in certain situations a try statement may never complete
running (either normally or abruptly). For example, code inside of the try
statement could indefinitely block while attempting to access a resource. If
the try statement calls into other code, that code could also indefinitely sleep
or block, preventing the cleanup code from being reached. As a result,
resources used in a try-with-resources statement may not be closed, or code
in a finally block may never be executed in these situations.
Guideline 1-3 / DOS-3: Resource limit checks should not suffer from
integer overflow
The Java language provides bounds checking on arrays which mitigates the
vast majority of integer overflow attacks. However, some operations on
primitive integral types silently overflow. Therefore, take care when checking
resource limits. This is particularly important on persistent resources, such as
disk space, where a reboot may not clear the problem.
Copy
Copied to Clipboard
Error: Could not Copy
private void checkGrowBy(long extra) {
BigInteger currentBig = BigInteger.valueOf(current);
BigInteger maxBig = BigInteger.valueOf(max);
BigInteger extraBig = BigInteger.valueOf(extra);
if (extra < 0 ||
currentBig.add(extraBig).compareTo(maxBig) > 0) {
throw new IllegalArgumentException();
}
}
The checkIndex, checkFromToIndex, and checkFromIndexSize methods from
the java.util.Objects class (available in Java 9 and later) can also be used to
avoid integer overflows when performing range and bounds checks. These
methods throw an IndexOutOfBoundsException if the index or sub-range being
checked is out of bounds. The following code
from java.io.OutputStream demonstrates this:
Copy
Copied to Clipboard
Error: Could not Copy
public void write(byte b[], int off, int len) throws IOException {
Objects.checkFromIndexSize(off, len, b.length);
// len == 0 condition implicitly handled by loop bounds
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
2 Confidential Information
Confidential data should be readable only within a limited context. Data that
is to be trusted should not be exposed to tampering. Privileged code should
not be executable through intended interfaces.
Use well-tested libraries instead of ad hoc code. There are many libraries for
creating XML. Creating XML documents using raw text is error-prone. For
unusual formats where appropriate libraries do not exist, such as
configuration files, create classes that cleanly handle all formatting and only
formatting code.
Copy
Copied to Clipboard
Error: Could not Copy
String sql = "SELECT * FROM User WHERE userId = ?";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, userId);
ResultSet rs = prepStmt.executeQuery();
Implementing correct data sanitization and encoding can be tricky and error
prone. Therefore, it is better to use a library to perform these tasks during
HTML or XML construction.
Guideline 3-4 / INJECT-4: Avoid any untrusted data on the command
line
When creating new processes, do not place any untrusted data on the
command line. Behavior is platform-specific, poorly documented, and
frequently surprising. Malicious data may, for instance, cause a single
argument to be interpreted as an option (typically a leading - on Unix or / on
Windows) or as two separate arguments. Any data that needs to be passed to
the new process should be passed either as encoded arguments (e.g.,
Base64), in a temporary file, or through a inherited channel.
if (Double.isInfinite(untrusted_double_value)){
// specific action for infinite case
}
There are command line options to open / export specific packages beyond
what the module configuration specifies. Minimizing the need for their usage
is also recommended.
// Hide constructor.
private SensitiveClass(Behavior behavior) {
this.behavior = behavior;
}
// Guarded construction.
public static SensitiveClass newSensitiveClass(
Behavior behavior
) {
// ... validate any arguments ...
For compatibility with versions of Java prior to JDK 6, check that the class has
been initialized before every sensitive operation and before trusting any
other instance of the class. It may be possible to see a partially initialized
instance, so any variable should have a safe interpretation for the default
value. For mutable classes, it is advisable to make an "initialized" flag volatile
to create a suitable happens-before relationship.
Copy
Copied to Clipboard
Error: Could not Copy
public class NonFinal {
// sole constructor
public NonFinal() {
securityManagerCheck();
Copied to Clipboard
Error: Could not Copy
Class Hierarchy Inherited Methods
----------------------- --------------------------
java.util.Hashtable put(key, val)
^ remove(key)
| extends
|
java.util.Properties
^
| extends
|
java.security.Provider put(key, val) // SecurityManager
remove(key) // checks for these
// methods
5 Input Validation
Note that input validation must occur after any defensive copying of that
input (see Guideline 6-2).
// validate input
// Note offset+len would be subject to integer overflow.
// For instance if offset = 1 and len = Integer.MAX_VALUE,
// then offset+len == Integer.MIN_VALUE which is lower
// than data.length.
// Further,
// loops of the form
// for (int i=offset; i<offset+len; ++i) { ... }
// would not throw an exception or cause native code to
// crash.
Additional steps may be required when using an API for input validation. It
might be necessary to perform context-specific checks (such as range
checks, allow/block list checks, etc.) in addition to the syntactic validation
performed by the API. The caller may also need to sanitize certain data, such
as meta-characters that identify macros or have other special meaning in the
given context, prior to passing the data to the API. It may not be sufficient to
use lower-level APIs for input validation, as they often provide additional
flexibility that could be problematic in a higher-level application context.
6 Mutability
Copy
Copied to Clipboard
Error: Could not Copy
public class CopyOutput {
private final java.util.Date date;
// ...
public java.util.Date getDate() {
return (java.util.Date)date.clone();
}
}
Copy
Copied to Clipboard
Error: Could not Copy
public final class CopyMutableInput {
private final Date date;
// java.util.Date is mutable
public CopyMutableInput(Date date) {
// create copy
this.date = new Date(date.getTime());
}
}
In rare cases it may be safe to call a copy method on the instance itself. For
instance, java.net.HttpCookie is mutable but final and provides a
public clone method for acquiring copies of its instances.
Copy
Copied to Clipboard
Error: Could not Copy
public final class CopyCookie {
// java.net.HttpCookie is mutable
public void copyMutableInput(HttpCookie cookie) {
// create copy
cookie = (HttpCookie)cookie.clone(); // HttpCookie is final
Copy
Copied to Clipboard
Error: Could not Copy
public class Window {
/* pp */ class PrivateKey {
// Optionally, refer to real object.
/* pp */ Window getWindow() {
return Window.this;
}
}
/* pp */ final PrivateKey privateKey = new PrivateKey();
Copy
Copied to Clipboard
Error: Could not Copy
private final byte[] data;
A common but difficult to spot case occurs when an input object is used as a
key. A collection's use of equality may well expose other elements to a
malicious input object on or after insertion.
Guideline 6-7 / MUTABLE-7: Treat output from untrusted object as
input
The above guidelines on input objects apply when returned from untrusted
objects. Appropriate copying and validation should be applied.
Copy
Copied to Clipboard
Error: Could not Copy
private final Date start;
private Date end;
Copy
Copied to Clipboard
Error: Could not Copy
public final class WrappedState {
// private immutable object
private String state;
// wrapper method
public String getState() {
return state;
}
// wrapper method
public void setState(final String newState) {
this.state = requireValidation(newState);
}
Make additional defensive copies in getState and setState if the internal state
is mutable, as described in Guideline 6-2.
Where possible make methods for operations that make sense in the context
of the interface of the class rather than merely exposing internal
implementation.
Copy
Copied to Clipboard
Error: Could not Copy
public class Files {
public static final String separator = "/";
public static final String pathSeparator = ":";
}
If using an interface instead of a class, the modifiers " public static final" can
be omitted to improve readability, as the constants are implicitly public,
static, and final. Constants can alternatively be defined using
an enum declaration.
Protected static fields suffer from the same problem as their public
equivalents but also tend to indicate confused design.
Copy
Copied to Clipboard
Error: Could not Copy
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
// ...
public static final List<String> names = unmodifiableList(asList(
"Fred", "Jim", "Sheila"
));
The of() and ofEntries() API methods, which were added in Java 9, can also
be used to create unmodifiable collections:
Copy
Copied to Clipboard
Error: Could not Copy
public static final List <String> names =
List.of("Fred", "Jim", "Sheila");
Copy
Copied to Clipboard
Error: Could not Copy
java.lang.ClassLoader.getSystemClassLoader
java.lang.System.clearProperty
java.lang.System.getProperties
java.lang.System.setErr
java.lang.System.setIn
java.lang.System.setOut
java.lang.System.setProperties
java.lang.System.setProperty
java.lang.System.setSecurityManager
java.lang.Thread.setDefaultUncaughtExceptionHandler
java.net.Authenticator.setDefault
java.net.CookieHandler.getDefault
java.net.CookieHandler.setDefault
java.net.Datagram.setDatagramSocketImplFactory
java.net.HttpURLConnection.setFollowRedirects
java.net.ProxySelector.setDefault
java.net.ResponseCache.getDefault
java.net.ResponseCache.setDefault
java.net.ServerSocket.setSocketFactory (deprecated)
java.net.Socket.setSocketImplFactory (deprecated)
java.net.URL.setURLStreamHandlerFactory
java.net.URLConnection.setContentHandlerFactory
java.net.URLConnection.setFileNameMap
java.rmi.server.RMISocketFactory.setFailureHandler
java.rmi.server.RMISocketFactory.setSocketFactory
java.rmi.activation.ActivationGroup.createGroup (deprecated)
java.rmi.activation.ActivationGroup.setSystem (deprecated)
java.rmi.server.RMIClassLoader.getDefaultProviderInstance
java.security.Policy.setPolicy (deprecated)
java.sql.DriverManager.deregisterDriver
java.sql.DriverManager.setLogStream (deprecated)
java.sql.DriverManager.setLogWriter
java.util.Locale.setDefault
java.util.TimeZone.setDefault
javax.naming.spi.NamingManager.setInitialContextFactoryBuilder
javax.naming.spi.NamingManager.setObjectFactoryBuilder
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory
javax.net.ssl.SSLContext.setDefault
javax.security.auth.login.Configuration.setConfiguration
javax.security.auth.login.Policy.setPolicy
javax.sql.rowset.spi.SyncFactory.setJNDIContext
javax.sql.rowset.spi.SyncFactory.setLogger
Java PlugIn and Java WebStart isolate certain global state within
an AppContext1. Often no security permissions are necessary to access this
state, so it cannot be trusted (other than for Same Origin Policy within PlugIn
and WebStart). While there are security checks, the state is still intended to
remain within the context. Objects retrieved directly or indirectly from
the AppContext should therefore not be stored in other variations of globals,
such as plain statics of classes in a shared class loader. Any library code
directly or indirectly using AppContext on behalf of an application should be
clearly documented. Users of AppContext include:
Copy
Copied to Clipboard
Error: Could not Copy
Extensively within AWT
Extensively within Swing
Extensively within JavaBeans Long Term Persistence
java.beans.Beans.setDesignTime
java.beans.Beans.setGuiAvailable
java.beans.Introspector.getBeanInfo
java.beans.PropertyEditorFinder.registerEditor
java.beans.PropertyEditorFinder.setEdiorSearchPath
javax.imageio.ImageIO.createImageInputStream
javax.imageio.ImageIO.createImageOutputStream
javax.imageio.ImageIO.getUseCache
javax.imageio.ImageIO.setCacheDirectory
javax.imageio.ImageIO.setUseCache
javax.print.StreamPrintServiceFactory.lookupStreamPrintServices
javax.print.PrintServiceLookup.lookupDefaultPrintService
javax.print.PrintServiceLookup.lookupMultiDocPrintServices
javax.print.PrintServiceLookup.lookupPrintServices
javax.print.PrintServiceLookup.registerService
javax.print.PrintServiceLookup.registerServiceProvider
static {
//For complex items requiring construction
Map<String, String> temp = new HashMap<>(2);
temp.put("first", "The first object");
temp.put("second", "Another object");
ITEMS = Collections.unmodifiableMap(temp);
}
private List<String> somethingStateful =
new ArrayList<>();
public List<String> getSomethingStateful() {
return Collections.unmodifiableList(
somethingStateful);
}
}
Arrays exposed via public variables or get methods can introduce similar
issues. For those cases, a copy of the internal array (created
using clone(), java.util.Arrays.copyOf(), etc.) should be exposed
instead. java.util.Arrays.asList() should not be used for exposing an internal
array, as this method creates a copy backed by the array, allowing two-way
modification of the contents.
Note that all of the collections in the previous example contain immutable
objects. If a collection or array contains mutable objects, then it is necessary
to expose a deep copy of it instead. See Guidelines 6-2 and 6-3 for additional
information on creating safe copies.
7 Object Construction
During construction objects are at an awkward stage where they exist but are
not ready for use. Such awkwardness presents a few more difficulties in
addition to those of ordinary methods.
For compatibility with older releases, a potential solution involves the use of
an initialized flag. Set the flag as the last operation in a constructor before
returning successfully. All methods providing a gateway to sensitive
operations must first consult the flag before proceeding:
Copy
Copied to Clipboard
Error: Could not Copy
public abstract class ClassLoader {
protected ClassLoader() {
this.impl = new ClassLoaderImpl();
}
protected final Class defineClass(...) {
return impl.defineClass(...);
}
}
/* pp */ class ClassLoaderImpl {
/* pp */ ClassLoaderImpl() {
// permission needed to create ClassLoader
securityManagerCheck();
init();
}
/* pp */ Class defineClass(...) {
// regular logic follows
// ...
}
}
public SecureName() {
// initialize name to default value
name = DEFAULT;
}
inputValidation(name);
this.name = name;
}
}
if (!DEFAULT.equals(name)) {
securityCheck();
inputValidation(name);
}
this.name = name;
}
return value;
}
9 Access Control
Copy
Copied to Clipboard
Error: Could not Copy
package xx.lib;
package yy.app;
class AppClass {
public static void main(String[] args) {
System.out.println(
xx.lib.LibClass.getOptions()
);
}
}
When the permission check is performed, the call stack will be as illustrated
below.
Copy
+--------------------------------+
| java.security.AccessController |
| .checkPermission(Permission) |
+--------------------------------+
| java.lang.SecurityManager |
| .checkPermission(Permission) |
+--------------------------------+
| java.lang.SecurityManager |
| .checkPropertyAccess(String) |
+--------------------------------+
| java.lang.System |
| .getProperty(String) |
+--------------------------------+
| xx.lib.LibClass |
| .getOptions() |
+--------------------------------+
| yy.app.AppClass |
| .main(String[]) |
+--------------------------------+
In the above example, if the AppClass frame does not have permission to read
a file but the LibClass frame does, then a security exception is still thrown. It
does not matter that the immediate caller of the privileged operation is fully
privileged, but that there is unprivileged code on the stack somewhere.
For library code to appear transparent to applications with respect to
privileges, libraries should be granted permissions at least as generous as
the application code that it is used with. For this reason, almost all the code
shipped in the JDK and extensions is fully privileged. It is therefore important
that there be at least one frame with the application's permissions on the
stack whenever a library executes security checked operations on behalf of
application code.
+--------------------------------+
| action |
| .run |
+--------------------------------+
| java.security.AccessController |
| .doPrivileged |
+--------------------------------+
| SomeClass |
| .someMethod |
+--------------------------------+
| OtherClass |
| .otherMethod |
+--------------------------------+
| |
In the above example, the privileges of the OtherClass frame are ignored for
security checks.
To avoid inadvertently performing such operations on behalf of unauthorized
callers, be very careful when invoking doPrivileged using caller-provided
inputs (tainted inputs):
Copy
Copied to Clipboard
Error: Could not Copy
package xx.lib;
import java.security.*;
By convention, instances
of PrivilegedAction and PrivilegedExceptionAction may be made available to
untrusted code, but doPrivileged must not be invoked with caller-provided
actions.
The two-argument overloads of doPrivileged allow changing of privileges to
that of a previous acquired context. A null context is interpreted as adding no
further restrictions. Therefore, before using stored contexts, make sure that
they are not null (AccessController.getContext never returns null).
Copy
Copied to Clipboard
Error: Could not Copy
if (acc == null) {
throw new SecurityException("Missing AccessControlContext");
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// ...
}
}, acc);
Copy
+--------------------------------+
| ActionImpl |
| .run |
+--------------------------------+
| |
| noPermissionsAcc |
+ - - - - - - - - - - - - - - - -+
| java.security.AccessController |
| .doPrivileged |
+--------------------------------+
| SomeClass |
| .someMethod |
+--------------------------------+
| OtherClass |
| .otherMethod |
+--------------------------------+
| |
Copy
Copied to Clipboard
Error: Could not Copy
private static void doWithURL(final Runnable task,
String knownURL) {
URLPermission urlPerm = new URLPermission(knownURL);
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
public Void run() {
task.run();
return null;
}},
someContext,
urlPerm
);
}
return value;
}
Copy
+--------------------------------+
| xx.lib.FileHandler |
| handle() |
+--------------------------------+
| xx.lib.Reactor.(anonymous) |
| run() |
+--------------------------------+ \ +--------------------------------+
| java.security.AccessController | ` | |
| .getContext() | +--> | acc |
+--------------------------------+ | + - - - - - - - - - - - - - - - -+
| xx.lib.Reactor | | | java.security.AccessController |
| .addHandler(Handler) | | | .doPrivileged(handler, acc) |
+--------------------------------+ | +--------------------------------+
| yy.app.App | | | xx.lib.Reactor |
| .main(String[] args) | , | .fire |
+--------------------------------+ / +--------------------------------+
| xx.lib.Reactor |
| .run |
+--------------------------------+
| |
class AppClass {
OtherClass appMethod() throws Exception {
return OtherClass.class.newInstance();
}
}
Copy
+--------------------------------+
| xx.lib.LibClass |
| .LibClass |
+--------------------------------+
| java.lang.Class |
| .newInstance |
+--------------------------------+
| yy.app.AppClass |<-- AppClass.class.getClassLoader
| .appMethod | determines check
+--------------------------------+
| |
Code has full access to its own class loader and any class loader that is a
descendant. In the case of Class.newInstance access to a class loader implies
access to classes in restricted packages (e.g., system classes prefixed
with sun.).
In the diagram below, classes loaded by B have access to B and its
descendants C, E, and D. Other class loaders, shown in grey strikeout font,
are subject to security checks.
Copy
+-------------------------+
| bootstrap loader | <--- null
+-------------------------+
^ ^
+------------------+ +---+
| extension loader | | A |
+------------------+ +---+
^
+------------------+
| system loader | <--- Class.getSystemClassLoader()
+------------------+
^ ^
+----------+ +---+
| B | | F |
+----------+ +---+
^ ^ ^
+---+ +---+ +---+
| C | | E | | G |
+---+ +---+ +---+
^
+---+
| D |
+---+
Methods such as these that vary their behavior according to the immediate
caller's class are considered to be caller-sensitive, and should be annotated
in code with the @CallerSensitive annotation [16]. Due to the security
implications described here and in subsequent guidelines, making a method
caller-sensitive should be avoided whenever possible.
Refrain from invoking the above methods on Class, ClassLoader,
or Thread instances that are received from untrusted code. If the respective
instances were acquired safely (or in the case of the
static ClassLoader.getSystemClassLoader method), do not invoke the above
methods using inputs provided by untrusted code. Also, do not propagate
objects that are returned by the above methods back to untrusted code.
Copy
Copied to Clipboard
Error: Could not Copy
java.lang.Class.forName
java.lang.ClassLoader.getPlatformClassLoader
java.lang.Package.getPackage(s) (deprecated)
java.lang.Runtime.load
java.lang.Runtime.loadLibrary
java.lang.System.load
java.lang.System.loadLibrary
java.sql.DriverManager.deregisterDriver
java.sql.DriverManager.getConnection
java.sql.DriverManager.getDriver(s)
java.security.AccessController.doPrivileged* (deprecated)
java.util.logging.Logger.getAnonymousLogger
java.util.logging.Logger.getLogger
java.util.ResourceBundle.getBundle
Methods such as these that vary their behavior according to the immediate
caller's class are considered to be caller-sensitive, and should be annotated
in code with the @CallerSensitive annotation [16].
For example, System.loadLibrary("/com/foo/MyLib.so") uses the immediate
caller's class loader to find and load the specified library. (Loading libraries
enables a caller to make native method invocations.) Do not invoke this
method on behalf of untrusted code, since untrusted code may not have the
ability to load the same library using its own class loader instance. Do not
invoke any of these methods using inputs provided by untrusted code, and
do not propagate objects that are returned by these methods back to
untrusted code.
Guideline 9-10 / ACCESS-10: Be aware of standard APIs that perform
Java language access checks against the immediate caller
When an object accesses fields or methods of another object, the JVM
performs access control checks to assert the valid visibility of the target
method or field. For example, it prevents objects from invoking private
methods in other objects.
Code may also call standard APIs (primarily in the java.lang.reflect package)
to reflectively access fields or methods in another object. The following
reflection-based APIs mirror the language checks that are enforced by the
virtual machine:
Copy
Copied to Clipboard
Error: Could not Copy
java.lang.Class.newInstance (deprecated)
java.lang.invoke.MethodHandles.lookup
java.lang.reflect.AccessibleObject.canAccess
java.lang.reflect.AccessibleObject.setAccessible
java.lang.reflect.AccessibleObject.tryAccessible
java.lang.reflect.Constructor.newInstance
java.lang.reflect.Constructor.setAccessible
java.lang.reflect.Field.get*
java.lang.reflect.Field.set*
java.lang.reflect.Method.invoke
java.lang.reflect.Method.setAccessible
java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater
java.util.concurrent.atomic.AtomicLongFieldUpdater.newUpdater
java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater
Methods such as these that vary their behavior according to the immediate
caller's class are considered to be caller-sensitive, and should be annotated
in code with the @CallerSensitive annotation [16].
Language checks are performed solely against the immediate caller, not
against each caller in the execution sequence. Because the immediate caller
may have capabilities that other code lacks (it may belong to a particular
package and therefore have access to its package-private members), do not
invoke the above APIs on behalf of untrusted code. Specifically, do not invoke
the above methods on Class, Constructor, Field, or Method instances that are
received from untrusted code. If the respective instances were acquired
safely, do not invoke the above methods using inputs that are provided by
untrusted code. Also, do not propagate objects that are returned by the
above methods back to untrusted code.
Guideline 9-11 / ACCESS-11: Be aware
java.lang.reflect.Method.invoke is ignored for checking the
immediate caller
Consider:
Copy
Copied to Clipboard
Error: Could not Copy
package xx.lib;
class LibClass {
void libMethod(
PrivilegedAction action
) throws Exception {
Method doPrivilegedMethod =
AccessController.class.getMethod(
"doPrivileged", PrivilegedAction.class
);
doPrivilegedMethod.invoke(null, action);
}
}
If Method.invoke was taken as the immediate caller, then the action would be
performed with all permissions. So, for the methods discussed in
Guidelines 9-8 through 9-10, the Method.invoke implementation is ignored
when determining the immediate caller.
Copy
+--------------------------------+
| action |
| .run |
+--------------------------------+
| java.security.AccessController |
| .doPrivileged |
+--------------------------------+
| java.lang.reflect.Method |
| .invoke |
+--------------------------------+
| xx.lib.LibClass | <--- Effective caller
| .libMethod |
+--------------------------------+
| |
Methods such as these that vary their behavior according to the immediate
caller's class are considered to be caller-sensitive, and should be annotated
in code with the @CallerSensitive annotation [16].
For example, Module::addExports uses the immediate caller's Module to decide
if a package should be exported. Do not invoke these methods on behalf of
untrusted code, since untrusted code may not have the ability to make the
same change.
Do not invoke any of these methods using inputs provided by untrusted code,
and do not propagate objects that are returned by these methods back to
untrusted code.
if (Proxy.getInvocationHandler(proxy) != this) {
throw new IllegalArgumentException("handler mismatch");
}
Conclusion
The Java Platform provides a robust basis for secure systems through
features such as memory-safety. However, the platform alone cannot prevent
flaws being introduced. This document details many of the common pitfalls.
The most effective approach to minimizing vulnerabilities is to have obviously
no flaws rather than no obvious flaws.
References
JNI-3: Expect that JNI code can violate Java visibility and isolation
rules
The Java runtime environment sometimes executes untrusted code, and
protection against access to unauthorized resources is a built-in feature. In
C/C++, private resources such as files (containing passwords and private
keys), system memory (private fields) and sockets are essentially just a
pointer away. Existing code may contain vulnerabilities that could be
instrumented by an attacker (on older operating systems simple shellcode
injection was sufficient, whereas advanced memory protections would
require the attacker to apply return-oriented programming techniques). This
means that C/C++ code, once successfully loaded, is not limited by the Java's
language access controls, visibility rules, or security policies 3.
The JRE does not block native code from registering native methods. This
allows JNI libraries to redefine the bindings to the entire set of native
methods. Programmers should be aware of this behavior. During
development they are advised to perform security reviews and scans to
check whether dependencies of their code introduce any security issues
(see FUNDAMENTALS-8 for additional information about third-party code
considerations). If any code running outside of the boot/platform loader tries
rebinding native methods in classes loaded by the boot/platform loader, this
may redirect the default control flows and by doing so subvert integrity of the
entire JRE process.
As stated in Guideline 5-3, native methods should be private and should only
be accessed through Java-based wrapper methods. This allows for
parameters to be validated by Java code before they are passed to native
code. The following example illustrates how to validate a pair of offset and
length values that are used when accessing a byte buffer. The Java-based
wrapper method validates the values and checks for integer overflow before
passing the values to a native method.
Copy
Copied to Clipboard
Error: Could not Copy
public final class NativeMethodWrapper {
// Further,
// loops of the form
// for (int i=offset; i < offset+len; ++i) { ... }
// would not throw an exception or cause native code to
// crash.
}
}
When required functions are statically linked to the shared library, certain
imported functions may introduce vulnerabilities over the lifetime of the
application, which could give attackers the opportunity to modify the control
flow or affect the application’s integrity in other ways. Consider the printf
function as an example. If the vulnerable code of libc is statically linked, it is
the responsibility of the application developer to update their libraries,
whereas referencing the system library dynamically could take advantage of
a system-level package update.
JNI-8: Expect and handle exceptions when calling from JNI into Java
Exceptions are an important construct of the Java language, because they
help to distinguish between the normal control flow and any exceptional
conditions that can occur during processing. This allows Java code to be
prepared for conditions that cause failure.
Native code has no direct support for Java exceptions, and any exceptions
thrown by Java code will not affect the control flow of native code. Therefore,
native code needs to explicitly check for exceptions after operations,
especially when calling into Java methods that may throw exceptions.
Exceptions may occur asynchronously, so it is necessary to check for
exceptions in long native loops, especially when calling back into Java code.
Be aware that many JNI API methods (e.g. GetFieldID) can return NULL or an
error code when an exception is thrown. Native code frequently needs to
return error values and the calling Java method should be prepared to handle
such error conditions accordingly.
Unexpected input and error conditions may cause native code to behave
unpredictably. An input allow-list limits the exposure of JNI code to a set of
expected values.
When building native libraries, some of the above techniques may not be
enabled by default and may require an explicit opt-in by the library bootstrap
code. In either case it is crucial to know and understand the secure
development practice for a given operating system, and adapt the compile
and build scripts accordingly [14][22][23].
Additionally, 32-/64-bit compatibility recommendations should be followed for
the given operating system (e.g. [18]). Whenever possible, pure 64-bit builds
should be used instead of relying on compatibility layers such as WOW.
JNI-10: Ensure that bundled JVMs and JREs meet Java's secure
baselines
Native applications may contain bundled JVMs and JREs for a variety of
purposes. These may expose vulnerabilities over time due to software decay.
Endnotes
Introduction¶
This cheat sheet is focused on providing developers with concentrated guidance
on building application logging mechanisms, especially related to security
logging.
Many systems enable network device, operating system, web server, mail server
and database server logging, but often custom application event logging is
missing, disabled or poorly configured. It provides much greater insight than
infrastructure logging alone. Web application (e.g. web site or web service)
logging is much more than having web server logs enabled (e.g. using Extended
Log File Format).
Purpose¶
Application logging should always be included for security events. Application
logs are invaluable data for both security and operational use cases.
Anti-automation monitoring
Identifying security incidents
Monitoring policy violations
Assisting non-repudiation controls (note that the trait non-repudiation is hard
to achieve for logs because their trustworthiness is often just based on the
logging party being audited properly while mechanisms like digital signatures
are hard to utilize here)
Audit trails e.g. data addition, modification and deletion, data exports
Compliance monitoring
Data for subsequent requests for information e.g. data subject access,
freedom of information, litigation, police and other regulatory investigations
Legally sanctioned interception of data e.g. application-layer wire-tapping
Contributing additional application-specific data for incident investigation
which is lacking in other log sources
Helping defend against vulnerability identification and exploitation through
attack detection
Process monitoring, audit, and transaction logs/trails etc. are usually collected for
different purposes than security event logging, and this often means they should
be kept separate.
For example a PCIDSS audit log will contain a chronological record of activities to
provide an independently verifiable trail that permits reconstruction, review and
examination to determine the original sequence of attributable transactions. It is
important not to log too much, or too little.
Use knowledge of the intended purposes to guide what, when and how much.
The remainder of this cheat sheet primarily discusses security event logging.
The application has the most information about the user (e.g. identity, roles,
permissions) and the context of the event (target, action, outcomes), and often
this data is not available to either infrastructure devices, or even closely-related
applications.
Client software e.g. actions on desktop software and mobile devices in local
logs or using messaging technologies, JavaScript exception handler via Ajax,
web browser such as using Content Security Policy (CSP) reporting
mechanism
Embedded instrumentation code
Network firewalls
Network and host intrusion detection systems (NIDS and HIDS)
Closely-related applications e.g. filters built into web server software, web
server URL redirects/rewrites to scripted custom error pages and handlers
Application firewalls e.g. filters, guards, XML gateways, database firewalls,
web application firewalls (WAFs)
Database applications e.g. automatic audit trails, trigger-based actions
Reputation monitoring services e.g. uptime or malware monitoring
Other applications e.g. fraud monitoring, CRM
Operating system e.g. mobile platform
Consider how the source can be verified, and how integrity and non-repudiation
can be enforced.
Your selected framework may limit the available choices. All types of applications
may send event data to remote systems (instead of or as well as more local
storage).
This could be a centralized log collection and management system (e.g. SIEM or
SEM) or another application elsewhere. Consider whether the application can
simply send its event stream, unbuffered, to stdout, for management by the
execution environment.
When using the file system, it is preferable to use a separate partition than
those used by the operating system, other application files and user
generated content
For file-based logs, apply strict permissions concerning which users can
access the directories, and the permissions of files within the directories
In web applications, the logs should not be exposed in web-accessible
locations, and if done so, should have restricted access and be
configured with a plain text MIME type (not HTML)
When using a database, it is preferable to utilize a separate database
account that is only used for writing log data and which has very restrictive
database, table, function and command permissions
Use standard formats over secure protocols to record and send event data, or
log files, to other systems e.g. Common Log File System (CLFS) or Common
Event Format (CEF) over syslog; standard formats facilitate integration with
centralised logging services
Consider separate files/tables for extended event information such as error stack
traces or a record of HTTP request and response headers and bodies.
There is no one size fits all solution, and a blind checklist approach can lead to
unnecessary "alarm fog" that means real problems go undetected.
Where possible, always log:
Sequencing failure
Excessive use
Data changes
Fraud and other criminal activities
Suspicious, unacceptable, or unexpected behavior
Modifications to configuration
Application code file and/or memory changes
Event attributes¶
Each log entry needs to include sufficient information for the intended subsequent
monitoring and analysis. It could be full content data, but is more likely to be an
extract or just summary properties.
The application logs must record "when, where, who and what" for each event.
The properties for these will be different depending on the architecture, class of
application and host system/device, but often include the following:
When
Log date and time (international format)
Event date and time - the event timestamp may be different to the time of
logging e.g. server logging where the client application is hosted on
remote device that is only periodically or intermittently online
Interaction identifier Note A
Where
Application identifier e.g. name and version
Application address e.g. cluster/hostname or server IPv4 or IPv6 address
and port number, workstation identity, local device identifier
Service e.g. name and protocol
Geolocation
Window/form/page e.g. entry point URL and HTTP method for a web
application, dialogue box name
Code location e.g. script name, module name
Who (human or machine user)
Source address e.g. user's device/machine identifier, user's IP address,
cell/RF tower ID, mobile telephone number
User identity (if authenticated or otherwise known) e.g. user database
table primary key value, user name, license number
What
Type of event Note B
For more information on these, see the "other" related articles listed at the end,
especially the comprehensive article by Anton Chuvakin and Gunnar Peterson.
Note A: The "Interaction identifier" is a method of linking all (relevant) events for
a single user interaction (e.g. desktop application form submission, web page
request, mobile app button click, web service call). The application knows all
these events relate to the same interaction, and this should be recorded instead
of losing the information and forcing subsequent correlation techniques to re-
construct the separate events. For example, a single SOAP request may have
multiple input validation failures and they may span a small range of times. As
another example, an output validation failure may occur much later than the input
submission for a long-running "saga request" submitted by the application to a
database server.
Data to exclude¶
Never log data unless it is legally sanctioned. For example, intercepting some
communications, monitoring employees, and collecting some data without
consent may all be illegal.
Never exclude any events from "known" users such as other internal systems,
"trusted" third parties, search engine robots, uptime/process and other remote
monitoring systems, pen testers, auditors. However, you may want to include a
classification flag for each of these in the recorded data.
The following should usually not be recorded directly in the logs, but instead
should be removed, masked, sanitized, hashed, or encrypted:
Application source code
Session identification values (consider replacing with a hashed value if
needed to track session specific events)
Access tokens
Sensitive personal data and some forms of personally identifiable information
(PII) e.g. health, government identifiers, vulnerable people
Authentication passwords
Database connection strings
Encryption keys and other primary secrets
Bank account or payment card holder data
Data of a higher security classification than the logging system is allowed to
store
Commercially-sensitive information
Information it is illegal to collect in the relevant jurisdictions
Information a user has opted out of collection, or not consented to e.g. use of
do not track, or where consent to collect has expired
Sometimes the following data can also exist, and whilst useful for subsequent
investigation, it may also need to be treated in some special manner before the
event is recorded:
File paths
Database connection strings
Internal network names and addresses
Non sensitive personal data (e.g. personal names, telephone numbers, email
addresses)
In some systems, sanitization can be undertaken post log collection, and prior to
log display.
Customizable logging¶
It may be desirable to be able to alter the level of logging (type of events based
on severity or threat level, amount of detail recorded). If this is implemented,
ensure that:
The default level must provide sufficient detail for business needs
It should not be possible to completely deactivate application logging or
logging of events that are necessary for compliance requirements
Alterations to the level/extent of logging must be intrinsic to the application
(e.g. undertaken automatically by the application based on an approved
algorithm) or follow change management processes (e.g. changes to
configuration data, modification of source code)
The logging level must be verified periodically
Event collection¶
If your development framework supports suitable logging mechanisms, use or
build upon that. Otherwise, implement an application-wide log handler which can
be called from other modules/components.
If possible create this log handler as a standard module that can be thoroughly
tested, deployed in multiple applications, and added to a list of approved and
recommended modules.
Perform input validation on event data from other trust zones to ensure it is in
the correct format (and consider alerting and not logging if there is an input
validation failure)
Perform sanitization on all event data to prevent log injection attacks e.g.
carriage return (CR), line feed (LF) and delimiter characters (and optionally to
remove sensitive data)
Encode data correctly for the output (logged) format
If writing to databases, read, understand, and apply the SQL injection cheat
sheet
Ensure failures in the logging processes/systems do not prevent the
application from otherwise running or allow information leakage
Synchronize time across all servers and devices Note C
Note C: This is not always possible where the application is running on a device
under some other party's control (e.g. on an individual's mobile phone, on a
remote customer's workstation which is on another corporate network). In these
cases, attempt to measure the time offset, or record a confidence level in the
event timestamp.
Verification¶
Logging functionality and systems must be included in code review, application
testing and security verification processes:
Network architecture¶
As an example, the diagram below shows a service that provides business
functionality to customers. We recommend creating a centralized system for
collecting logs. There may be many such services, but all of them must securely
collect logs in a centralized system.
For example, all external requests from users go through the API management
service, see application in MIDDLEWARE 2 segment.
As you can see in the image above, at the network level, the processes of saving
and downloading logs require opening different network accesses (ports), arrows
are highlighted in different colors. Also, saving and downloading are performed by
different applications.
Operation¶
Enable processes to detect whether logging has stopped, and to identify
tampering or unauthorized access and deletion (see protection below).
Protection¶
The logging mechanisms and collected event data must be protected from mis-
use such as tampering in transit, and unauthorized access, modification and
deletion once stored. Logs may contain personal and other sensitive information,
or the data may contain information regarding the application's code and logic.
In addition, the collected information in the logs may itself have business value (to
competitors, gossip-mongers, journalists and activists) such as allowing the
estimate of revenues, or providing performance information about employees.
Consider whether parts of the data may need to be excluded, masked, sanitized,
hashed, or encrypted during examination or extraction.
At rest:
Build in tamper detection so you know if a record has been modified or
deleted
Store or copy log data to read-only media as soon as possible
All access to the logs must be recorded and monitored (and may need prior
approval)
The privileges to read log data should be restricted and reviewed periodically
In transit:
If log data is sent over untrusted networks (e.g. for collection, for dispatch
elsewhere, for analysis, for reporting), use a secure transmission protocol
Consider whether the origin of the event data needs to be verified
Perform due diligence checks (regulatory and security) before sending event
data to third parties
See NIST SP 800-92 Guide to Computer Security Log Management for more
guidance.
Monitoring of events¶
The logged event data needs to be available to review and there are processes in
place for appropriate monitoring, alerting, and reporting:
Disposal of logs¶
Log data, temporary debug logs, and backups/copies/extractions, must not be
destroyed before the duration of the required data retention period, and must not
be kept beyond this time.
Confidentiality¶
Who should be able to read what? A confidentiality attack enables an
unauthorized party to access sensitive information stored in logs.
Logs contain PII of users. Attackers gather PII, then either release it or use it
as a stepping stone for further attacks on those users.
Logs contain technical secrets such as passwords. Attackers use it as a
stepping stone for deeper attacks.
Integrity¶
Which information should be modifiable by whom?
Availability¶
What downtime is acceptable?
An attacker floods log files in order to exhaust disk space available for non-
logging facets of system functioning. For example, the same disk used for log
files might be used for SQL storage of application data.
An attacker floods log files in order to exhaust disk space available for further
logging.
An attacker uses one log entry to destroy other log entries.
An attacker leverages poor performance of logging code to reduce application
performance
Accountability¶
Who is responsible for harm?
Related articles¶
OWASP ESAPI Documentation.
OWASP Logging Project.
IETF syslog protocol.
Mitre Common Event Expression (CEE) (as of 2014 no longer actively
developed).
NIST SP 800-92 Guide to Computer Security Log Management.
PCISSC PCI DSS v2.0 Requirement 10 and PA-DSS v2.0 Requirement 4.
W3C Extended Log File Format.
Other Build Visibility In, Richard Bejtlich, TaoSecurity blog.
Other Common Event Format (CEF), Arcsight.
Other Log Event Extended Format (LEEF), IBM.
Other Common Log File System (CLFS), Microsoft.
Other Building Secure Applications: Consistent Logging, Rohit Sethi & Nish
Bhalla, Symantec Connect.
Application Logging Vocabulary Cheat Sheet¶
This document proposes a standard vocabulary for logging security events. The
intent is to simplify monitoring and alerting such that, assuming developers trap
errors and log them using this vocabulary, monitoring and alerting would be
improved by simply keying on these terms.
Overview¶
Each year IBM Security commissions the Ponemon Institute to survey companies
around the world for information related to security breaches, mitigation, and the
associated costs; the result is called the Cost of a Data Breach Report.
In addition to the millions of dollars lost due to breaches the report finds that
the mean time to identify a breach continues to hover around 200 days. Clearly
our ability to monitor applications and alert on anomalous behavior would improve
our time to identify and mitigate an attack against our applications.
IBM Cost of a Data Breach Study 2020, Fig.34, pg.52,
[https://www.ibm.com/security/data-breach]
This logging standard would seek to define specific keywords which, when
applied consistently across software, would allow groups to simply monitor for
these events terms across all applications and respond quickly in the event of
attack.
Assumptions¶
Observability/SRE groups must support the use of this standard and
encourage developers to use it
Incident Response must either ingest this data OR provide a means by which
other monitoring teams can send a notification of alert, preferably
programmatically.
Architects must support, adopt, and contribute to this standard
Developers must embrace this standard and begin to implement (requires
knowledge and intent to understand potential attacks and trap those errors in
code).
Getting Started¶
As a reminder, the goal of logging is to be able to alert on specific security
events. Of course, the first step to logging these events is good error handling, if
you're not trapping the events, you don't have an event to log.
Identifying Events¶
In order to better understand security event logging a good high-level
understanding of threat modeling would be helpful, even if it's a simple approach
of:
The Vocabulary¶
What follows are the various event types that should be captured. For each event
type there is a prefix like "authn" and additional data that should be included for
that event.
Portions of the full logging format are included for example, but a complete event
log should follow the format above.
Authentication [AUTHN]¶
authn_login_success[:userid]¶
Description All login events should be recorded including success.
Level: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_login_success:joebob1",
"level": "INFO",
"description": "User joebob1 login successfully",
...
}
authn_login_successafterfail[:userid,retries]¶
Description The user successfully logged in after previously failing.
Level: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_login_successafterfail:joebob1,2",
"level": "INFO",
"description": "User joebob1 login successfully",
...
}
authn_login_fail[:userid]¶
Description All login events should be recorded including failure.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_login_fail:joebob1",
"level": "WARN",
"description": "User joebob1 login failed",
...
}
authn_login_fail_max[:userid,maxlimit(int)]¶
Description All login events should be recorded including failure.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_login_fail_max:joebob1,3",
"level": "WARN",
"description": "User joebob1 reached the login fail limit of 3",
...
}
authn_login_lock[:userid,reason]¶
Description When the feature exists to lock an account after x retries or other
condition, the lock should be logged with relevant data.
Level: WARN
Reasons:
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_login_lock:joebob1,maxretries",
"level": "WARN",
"description": "User joebob1 login locked because maxretries exceeded",
...
}
authn_password_change[:userid]¶
Description Every password change should be logged, including the userid that
it was for.
Level: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_password_change:joebob1",
"level": "INFO",
"description": "User joebob1 has successfully changed their password",
...
}
authn_password_change_fail[:userid]¶
Description An attempt to change a password that failed. May also trigger other
events such as authn_login_lock.
Level: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_password_change_fail:joebob1",
"level": "CRITICAL",
"description": "User joebob1 failed to change their password",
...
}
authn_impossible_travel[:userid,region1,region2]¶
Description When a user is logged in from one city and suddenly appears in
another, too far away to have traveled in a reasonable timeframe, this often
indicates a potential account takeover.
Level:: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_impossible_travel:joebob1,US-OR,CN-SH",
"level": "CRITICAL",
"description": "User joebob1 has accessed the application in two distant cities at the same time",
...
}
authn_token_created[:userid, entitlement(s)]¶
Description When a token is created for service access it should be recorded
Level:: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "aws.foobar.com",
"event": "authn_token_created:app.foobarapi.prod,create,read,update",
"level": "INFO",
"description": "A token has been created for app.foobarapi.prod with create,read,update",
...
}
authn_token_revoked[:userid,tokenid]¶
Description A token has been revoked for the given account.
Level:: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "aws.foobar.com",
"event": "authn_token_revoked:app.foobarapi.prod,xyz-abc-123-gfk",
"level": "INFO",
"description": "Token ID: xyz-abc-123-gfk was revoked for user app.foobarapi.prod",
...
}
authn_token_reuse[:userid,tokenid]¶
Description A previously revoked token was attempted to be reused.
Level:: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "aws.foobar.com",
"event": "authn_token_reuse:app.foobarapi.prod,xyz-abc-123-gfk",
"level": "CRITICAL",
"description": "User app.foobarapi.prod attempted to use token ID: xyz-abc-123-gfk which was previously
revoked",
...
}
authn_token_delete[:appid]¶
Description When a token is deleted it should be recorded
Level:: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authn_token_delete:foobarapi",
"level": "WARN",
"description": "The token for foobarapi has been deleted",
...
}
Authorization [AUTHZ]¶
authz_fail[:userid,resource]¶
Description An attempt was made to access a resource which was unauthorized
Level:: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authz_fail:joebob1,resource",
"level": "CRITICAL",
"description": "User joebob1 attempted to access a resource without entitlement",
...
}
authz_change[:userid,from,to]¶
Description The user or entity entitlements was changed
Level:: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authz_change:joebob1,user,admin",
"level": "WARN",
"description": "User joebob1 access was changed from user to admin",
...
}
authz_admin[:userid,event]¶
Description All activity by privileged users such as admin should be recorded.
Level:: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "authz_admin:joebob1,user_privilege_change",
"level": "WARN",
"description": "Administrator joebob1 has updated privileges of user foobarapi from user to admin",
...
}
Excessive Use [EXCESS]¶
excess_rate_limit_exceeded[userid,max]¶
Description Expected service limit ceilings should be established and alerted
when exceeded, even if simply for managing costs and scaling.
Level:: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "excess_rate_limit_exceeded:app.foobarapi.prod,100000",
"level": "WARN",
"description": "User app.foobarapi.prod has exceeded max:100000 requests",
...
}
Level:: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "upload_complete:joebob1,user_generated_content.png,PNG",
"level": "INFO",
"description": "User joebob1 has uploaded user_generated_content.png",
...
}
upload_stored[filename,from,to]¶
Description One step in good file upload validation is to move/rename the file
and when providing the content back to end users, never reference the original
filename in the download. This is true both when storing in a filesystem as well as
in block storage.
Level:: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "upload_stored:user_generated_content.png,kjsdhkrjhwijhsiuhdf000010202002",
"level": "INFO",
"description": "File user_generated_content.png was stored in the database with key
abcdefghijk101010101",
...
}
upload_validation[filename,(virusscan|imagemagick|...):(FAILED|
incomplete|passed)]¶
Description All file uploads should have some validation performed, both for
correctness (is in fact of file type x), and for safety (does not contain a virus).
Level:: INFO|CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "upload_validation:filename,virusscan:FAILED",
"level": "CRITICAL",
"description": "File user_generated_content.png FAILED virus scan and was purged",
...
}
upload_delete[userid,fileid]¶
Description When a file is deleted for normal reasons it should be recorded.
Level:: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "upload_delete:joebob1,",
"level": "INFO",
"description": "User joebob1 has marked file abcdefghijk101010101 for deletion.",
...
}
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "input_validation_fail:date_of_birth,joebob1",
"level": "WARN",
"description": "User joebob1 submitted data that failed validation.",
...
}
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "malicious_excess404:123.456.789.101,M@l1c10us-Hax0rB0t0-v1",
"level": "WARN",
"description": "A user at 123.456.789.101 has generated a large number of 404 requests.",
...
}
malicious_extraneous:[userid|IP,inputname,useragent]¶
Description When a user submits data to a backend handler that was not
expected it can indicate probing for input validation errors. If your backend
service receives data it does not handle or have an input for this is an indication
of likely malicious abuse.
Level: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "malicious_extraneous:[email protected],creditcardnum,Mozilla/5.0 (X11; Linux x86_64; rv:10.0)
Gecko/20100101 Firefox/10.0",
"level": "CRITICAL",
"description": "User [email protected] included field creditcardnum in the request which is not handled by this
service.",
...
}
malicious_attack_tool:[userid|IP,toolname,useragent]¶
Description When obvious attack tools are identified either by signature or by
user agent they should be logged.
For example, the tool "Nikto" leaves behind its user agent by default with a string
like "Mozilla/5.00 (Nikto/2.1.6) (Evasions:None) (Test:Port Check)"
Level: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "malicious_attack_tool:127.0.0.1,nikto,Mozilla/5.00 (Nikto/2.1.6) (Evasions:None) (Test:Port
Check)",
"level": "CRITICAL",
"description": "Attack traffic indicating use of Nikto coming from 127.0.0.1",
...
}
malicious_cors:[userid|IP,useragent,referer]¶
Description When attempts are made from unauthorized origins they should of
course be blocked, but also logged whenever possible. Even if we block an illegal
cross-origin request the fact that the request is being made could be an indication
of attack.
NOTE: Did you know that the word "referer" is misspelled in the original HTTP
specification? The correct spelling should be "referrer" but the original typo
persists to this day and is used here intentionally.
Level: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "malicious_cors:127.0.0.1,Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101
Firefox/10.0,attack.evil.com",
"level": "CRITICAL",
"description": "An illegal cross-origin request from 127.0.0.1 was referred from attack.evil.com"
...
}
malicious_direct_reference:[userid|IP, useragent]¶
Description A common attack against authentication and authorization is to
directly access an object without credentials or appropriate access authority.
Failing to prevent this flaw used to be one of the OWASP Top Ten
called Insecure Direct Object Reference. Assuming you've correctly prevented
this attack, logging the attempt is valuable to identify malicious users.
Level: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "malicious_direct:joebob1, Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0",
"level": "CRITICAL",
"description": "User joebob1 attempted to access an object to which they are not authorized",
...
}
privilege_permissions_changed:[userid,file|object,fromlevel,tolevel]¶
Description Tracking changes to objects to which there are access control
restrictions can uncover attempt to escalate privilege on those files by
unauthorized users.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "permissions_changed:joebob1, /users/admin/some/important/path,0511,0777",
"level": "WARN",
"description": "User joebob1 changed permissions on /users/admin/some/important/path",
...
}
Sensitive Data Changes [DATA]¶
It's not necessary to log or alert on changes to all files, but in the case of highly
sensitive files or data it is important that we monitor and alert on changes.
sensitive_create:[userid,file|object]¶
Description When a new piece of data is created and marked as sensitive or
placed into a directory/table/repository where sensitive data is stored, that
creation should be logged and reviewed periodically.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sensitive_create:joebob1, /users/admin/some/important/path",
"level": "WARN",
"description": "User joebob1 created a new file in /users/admin/some/important/path",
...
}
sensitive_read:[userid,file|object]¶
Description All data marked as sensitive or placed into a
directory/table/repository where sensitive data is stored should be have access
logged and reviewed periodically.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sensitive_read:joebob1, /users/admin/some/important/path",
"level": "WARN",
"description": "User joebob1 read file /users/admin/some/important/path",
...
}
sensitive_update:[userid,file|object]¶
Description All data marked as sensitive or placed into a
directory/table/repository where sensitive data is stored should be have updates
to the data logged and reviewed periodically.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sensitive_update:joebob1, /users/admin/some/important/path",
"level": "WARN",
"description": "User joebob1 modified file /users/admin/some/important/path",
...
}
sensitive_delete:[userid,file|object]¶
Description All data marked as sensitive or placed into a
directory/table/repository where sensitive data is stored should have deletions of
the data logged and reviewed periodically. The file should not be immediately
deleted but marked for deletion and an archive of the file should be maintained
according to legal/privacy requirements.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sensitive_delete:joebob1, /users/admin/some/important/path",
"level": "WARN",
"description": "User joebob1 marked file /users/admin/some/important/path for deletion",
...
}
Sequence Errors [SEQUENCE]¶
Also called a business logic attack, if a specific path is expected through a
system and an attempt is made to skip or change the order of that path it could
indicate malicious intent.
sequence_fail:[userid]¶
Description When a user reaches a part of the application out of sequence it
may indicate intentional abuse of the business logic and should be tracked.
Level: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sequence_fail:joebob1",
"level": "CRITICAL",
"description": "User joebob1 has reached a part of the application out of the normal application flow.",
...
}
Level: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "session_created:joebob1",
"level": "INFO",
"description": "User joebob1 has started a new session",
...
}
session_renewed:[userid]¶
Description When a user is warned of session to be expired/revoked and
chooses to extend their session that activity should be logged. Also, if the system
in question contains highly confidential data then extending a session may
require additional verification.
Level: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "session_renewed:joebob1",
"level": "INFO",
"description": "User joebob1 was warned of expiring session and extended.",
...
}
session_expired:[userid,reason]¶
Description When a session expires, especially in the case of an authenticated
session or with sensitive data, then that session expiry may be logged and
clarifying data included. The reason code may be any such as: logout, timeout,
revoked, etc. Sessions should never be deleted but rather expired in the case of
revocation requirement.
Level: INFO
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "session_expired:joebob1,revoked",
"level": "INFO",
"description": "User joebob1 session expired due to administrator revocation.",
...
}
session_use_after_expire:[userid]¶
Description In the case a user attempts to access systems with an expire
session it may be helpful to log, especially if combined with subsequent login
failure. This could identify a case where a malicious user is attempting a session
hijack or directly accessing another person's machine/browser.
Level: CRITICAL
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "session_use_after_expire:joebob1",
"level": "CRITICAL",
"description": "User joebob1 attempted access after session expired.",
...
}
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sys_startup:joebob1",
"level": "WARN",
"description": "User joebob1 spawned a new instance",
...
}
sys_shutdown:[userid]¶
Description When a system is shut down it can be valuable to log the event,
even if the system is serverless or a container, especially if possible to log the
user that initiated the system.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sys_shutdown:joebob1",
"level": "WARN",
"description": "User joebob1 stopped this instance",
...
}
sys_restart:[userid]¶
Description When a system is restarted it can be valuable to log the event, even
if the system is serverless or a container, especially if possible to log the user that
initiated the system.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sys_restart:joebob1",
"level": "WARN",
"description": "User joebob1 initiated a restart",
...
}
sys_crash[:reason]¶
Description If possible to catch an unstable condition resulting in the crash of a
system, logging that event could be helpful, especially if the event is triggered by
an attack.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sys_crash:outofmemory,
"level": "WARN",
"description": "The system crashed due to Out of Memory error.",
...
}
sys_monitor_disabled:[userid,monitor]¶
Description If your systems contain agents responsible for file integrity,
resources, logging, virus, etc. it is especially valuable to know if they are halted
and by whom.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sys_monitor_disabled:joebob1,crowdstrike",
"level": "WARN",
"description": "User joebob1 has disabled CrowdStrike",
...
}
sys_monitor_enabled:[userid,monitor]¶
Description If your systems contain agents responsible for file integrity,
resources, logging, virus, etc. it is especially valuable to know if they are started
again after being stopped, and by whom.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "sys_monitor_enabled:joebob1,crowdstrike",
"level": "WARN",
"description": "User joebob1 has enabled CrowdStrike",
...
}
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "user_created:joebob1,user1,admin:create,update,delete",
"level": "WARN",
"description": "User joebob1 created user1 with admin:create,update,delete privilege attributes",
...
}
user_updated:[userid,onuserid,attributes[one,two,three]]¶
Description When updating users, logging the specifics of the user update event
is helpful, especially if users can be updated with administration privileges.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "user_updated:joebob1,user1,admin:create,update,delete",
"level": "WARN",
"description": "User joebob1 updated user1 with attributes admin:create,update,delete privilege
attributes",
...
}
user_archived:[userid,onuserid]¶
Description It is always best to archive users rather than deleting, except where
required. When archiving users, logging the specifics of the user archive event is
helpful. A malicious user could use this feature to deny service to legitimate
users.
Level: WARN
Example:
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "user_archived:joebob1,user1",
"level": "WARN",
"description": "User joebob1 archived user1",
...
}
user_deleted:[userid,onuserid]¶
Description It is always best to archive users rather than deleting, except where
required. When deleting users, logging the specifics of the user delete event is
helpful. A malicious user could use this feature to deny service to legitimate
users.
Level: WARN
Example:
Expliquer
{
"datetime": "2019-01-01 00:00:00,000",
"appid": "foobar.netportal_auth",
"event": "user_deleted:joebob1,user1",
"level": "WARN",
"description": "User joebob1 has deleted user1",
...
}
Exclusions¶
As important as what you DO log is what you DON'T log. Private or secret
information, source code, keys, certs, etc. should never be logged.