Disclaimer: This article is not the author’s original, originally reprinted from: github.com/SecurityPap…

 

1 Input verification and data validity verification

Application acceptance data may come from unauthenticated users, network connections, and other untrusted sources. Failure to verify application acceptance data may cause security issues.

1.1 Avoid SQL injection

PreparedStatement is used to precompile SQL and solve SQL injection problems. Parameters passed to PreparedStatement objects can be cast to ensure that data is inserted or queried to match the underlying database format.

String sqlString = "select * from db_user where username=? and password=?" ; PreparedStatement stmt = connection.prepareStatement(sqlString); stmt.setString(1, username); stmt.setString(2, pwd); ResultSet rs = stmt.executeQuery();Copy the code

1.2 Avoid XML injection

When a StringBulider or StringBuffer is used to concatenate XML files, the validity of the input data must be verified. Verify the validity of quantity, control can only pass in numbers 0-9:

if (! Pattern.matches("[0-9]+", quantity)) { // Format violation } String xmlString = "<item>\n<description>Widget</description>\n" + "<price>500</price>\n" + "<quantity>" + quantity + "</quantity></item>"; outStream.write(xmlString.getBytes()); outStream.flush();Copy the code

1.3 Avoiding Cross-site Scripting (XSS)

Strictly filters the parameters that generate cross-site, disallowing the passing of

// Define the field string to filter

String s = "\uFE64" + "script" + "\uFE65";

// Filter string normalization

s = Normalizer.normalize(s, Form.NFKC);

// Use a regular expression to match the presence of inputStr.

Pattern pattern = Pattern.compile(inputStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()) {
  // Found black listed tag
  throw new IllegalStateException();
} else {
  // ...
}
Copy the code

2 Declaration and initialization

2.1 Avoid class initialization interdependence

Ex. :

Wrong way to write:

public class Cycle { private final int balance; private static final Cycle c = new Cycle(); private static final int deposit = (int) (Math.random() * 100); // Random deposit public Cycle() { balance = deposit - 10; // Subtract processing fee } public static void main(String[] args) { System.out.println("The account balance is: " + c.balance); }}Copy the code

The static variable c pointing to the Cycle class is initialized when the class is loaded, and the parameterless constructor of the Cycle class relies on the static variable deposit, resulting in unexpected results. Correct way to write it:

public class Cycle { private final int balance; private static final int deposit = (int) (Math.random() * 100); // Random deposit private static final Cycle c = new Cycle(); // Inserted after initialization of required fields public Cycle() { balance = deposit - 10; // Subtract processing fee } public static void main(String[] args) { System.out.println("The account balance is: " + c.balance); }}Copy the code

3 expression

3.1 The return value of a method cannot be ignored

Ignoring a method’s put back value can result in unexpected results.

Wrong way to write:

public void deleteFile(){
  File someFile = new File("someFileName.txt");
   someFile.delete();
}
Copy the code

Correct way to write it:

public void deleteFile(){ File someFile = new File("someFileName.txt"); if (! someFile.delete()) { // handle failure to delete the file } }Copy the code

3.2 Do not reference null Pointers

This occurs when a variable refers to a NULL value and is not checked when the variable is used. NullPointerException.

Verify that a variable is NULL before using it.

3.3 Compare the contents of Arrays using arrays.equals ()

Arrays do not override object.equals (). Calling object.equals () actually compares arrays’ references, not their contents. The program must use two parameter arrays.equals () methods to compare the contents of two Arrays

public void arrayEqualsExample() {
  int[] arr1 = new int[20]; // initialized to 0
  int[] arr2 = new int[20]; // initialized to 0
  Arrays.equals(arr1, arr2); // true
}
Copy the code

4 Number type and operation

4.1 Preventing Integer Overflow

Use the java.lang.number. BigInteger class for integer arithmetic to prevent integer overflow.

public class BigIntegerUtil {

    private static final BigInteger bigMaxInt = BigInteger.valueOf(Integer.MAX_VALUE);
    private static final BigInteger bigMinInt = BigInteger.valueOf(Integer.MIN_VALUE);

    public static BigInteger intRangeCheck(BigInteger val) throws ArithmeticException {
        if (val.compareTo(bigMaxInt) == 1 || val.compareTo(bigMinInt) == -1) {
            throw new ArithmeticException("Integer overflow");
        }
        return val;
    }

    public static int addInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.add(b2));
        return res.intValue(); 
    }
    
    public static int subInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.subtract(b2));
        return res.intValue(); 
    }
    
    public static int multiplyInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.multiply(b2));
        return res.intValue(); 
    }
    
    public static int divideInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.divide(b2));
        return res.intValue(); 
    }
}
Copy the code

4.2 Avoid division and modular operations with zero denominators

The division and modulo operations should be avoided because the denominator is zero.

if (num2 == 0) {
  // handle error
} else {
 result1= num1 /num2;
  result2= num1 % num2;
}
Copy the code

Class and method operations

5.1 Data members are declared private and provide accessible wrapping methods

An attacker can manipulate public or protected data members in unexpected ways, so you need to make your data members private and provide a controlled wrapper to access them externally.

5.2 Sensitive Classes cannot be copied

Classes that contain private, confidential, or other sensitive data are not allowed to be copied. There are two ways to solve this problem:

1. Declare the class final

final class SensitiveClass {
  // ...
}
Copy the code

2, throw CloneNotSupportedException abnormal Clone method

class SensitiveClass { // ... public final SensitiveClass clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }}Copy the code

5.3 The correct way to compare classes

If they are loaded by the same class loader and have the same fully qualified name, they are two identical classes. Incorrect writing:

// Determine whether object auth has required/expected class object if (auth.getClass().getName().equals( "com.application.auth.DefaultAuthenticationHandler")) { // ... }}  // Determine whether object auth has required/expected class name if (auth.getClass() == com.application.auth.DefaultAuthenticationHandler.class) { // ... }Copy the code

5.4 Do not hardcode sensitive information

Hard-coded sensitive information, such as passwords, server IP addresses and encryption keys, can be exposed to attackers.

Sensitive information must exist in configuration files or databases.

5.5 Verification method Parameters

Validate the parameters of the method to ensure that the parameters of the operation method produce valid results. Failing to validate method parameters can result in incorrect calculations, runtime exceptions, violations of class invariants, and inconsistent object states. For the method of receiving parameters across trust boundaries, parameter validity verification must be carried out

private Object myState = null; // To modify the myState method input parameter, Verify non-null and validity void setState(Object state) {if (state == null) {// Handle NULL State} if (isInvalidState(state)) {// Handle invalid state } myState = state; }Copy the code

5.6 Do not use outdated, obsolete, or inefficient methods

Using outdated, obsolete, or inefficient classes or methods in program code can lead to incorrect behavior.

5.7 Array Reference Problems

A method returns a reference to an internal array of sensitive objects, assuming that the method caller does not change those objects. Even if the array object itself is immutable, you can manipulate the contents of an array outside of the array object, and this operation will be reflected in the object that returns the array. If the method returns a mutable object, the external entity can change the public variable declared in that class, and the change will be reflected in the actual object.

Incorrect way of writing:

public class XXX { private String[] xxxx; public String[] getXXX() { return xxxx; }}Copy the code

Correct way to write it:

public class XXX { private String[] xxxx; Public String[] getXXX() {String temp[] = array.copyof (... ; // Or other array copy methods return temp; }}Copy the code

5.8 Do not leak memory

The garbage collector only collects unreachable objects, so the presence of unused reachable objects still indicates poor memory management. Excessive memory leaks can lead to memory exhaustion, denial of service (DoS).


6 Exception Handling

6.1 Do not ignore caught exceptions

The caught exceptions must be handled accordingly. Caught exceptions cannot be ignored

Incorrect writing:

class Foo implements Runnable { public void run() { try { Thread.sleep(1000); } Catch (InterruptedException e) {here InterruptedException is ignored}}Copy the code

Correct way to write:

class Foo implements Runnable {
  public void run() {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    }
  }
}
Copy the code

6.2 Abnormal sensitive information shall not be exposed

An exception stack without filtering sensitive information often leads to leaks,

Incorrect way of writing:

try {
  FileInputStream fis =
      new FileInputStream(System.getenv("APPDATA") + args[0]);
} catch (FileNotFoundException e) {
  // Log the exception
  throw new IOException("Unable to retrieve file", e);
}
Copy the code

Correct way to write it:

class ExceptionExample { public static void main(String[] args) { File file = null; try { file = new File(System.getenv("APPDATA") + args[0]).getCanonicalFile(); if (! file.getPath().startsWith("c:\\homepath")) { log.error("Invalid file"); return; } } catch (IOException x) { log.error("Invalid file"); return; } try { FileInputStream fis = new FileInputStream(file); } catch (FileNotFoundException x) { log.error("Invalid file"); return; }}}Copy the code

6.3 RuntimeException, Exception, and Throwable cannot be thrown

Incorrect way of writing:

boolean isCapitalized(String s) {
  if (s == null) {
    throw new RuntimeException("Null String");
  }
}

private void doSomething() throws Exception {
  //...
}
Copy the code

Correct way to write:

boolean isCapitalized(String s) {
  if (s == null) {
    throw new NullPointerException();
  }
}

private void doSomething() throws IOException {
  //...
}
Copy the code

6.4 Do not catch nullPointerExceptions or other superclass exceptions

Incorrect way of writing:

boolean isName(String s) {
  try {
    String names[] = s.split(" ");
    if (names.length != 2) {
      return false;
    }
    return (isCapitalized(names[0]) && isCapitalized(names[1]));
  } catch (NullPointerException e) {
    return false;
  }
}
Copy the code

Correct way to write it:

boolean isName(String s) /* throws NullPointerException */ { String names[] = s.split(" "); if (names.length ! = 2) { return false; } return (isCapitalized(names[0]) && isCapitalized(names[1])); }Copy the code

7 Multithreaded Programming

7.1 Ensure visibility of shared variables

For shared variables, make sure that changes made to them by one thread are visible to other threads. The thread may see the value of a stale shared variable. To keep shared variables up to date, you can declare them volatile or synchronize read and write operations. Declare the shared variable volatile:

final class ControlledStop implements Runnable {
  private volatile boolean done = false;
  @Override public void run() {
    while (!done) {
      try {
        // ...
        Thread.currentThread().sleep(1000); // Do something
      } catch(InterruptedException ie) { 
        Thread.currentThread().interrupt(); // Reset interrupted status
      } 
    }    
  }
  public void shutdown() {
    done = true;
  }
}
Copy the code

Synchronous read and write operations:

final class ControlledStop implements Runnable {
  private boolean done = false;
  @Override public void run() {
    while (!isDone()) {
      try {
        // ...
        Thread.currentThread().sleep(1000); // Do something
      } catch(InterruptedException ie) { 
        Thread.currentThread().interrupt(); // Reset interrupted status
      } 
    }    
  }
  public synchronized boolean isDone() {
    return done;
  }
  public synchronized void shutdown() {
    done = true;
  }
}
Copy the code

7.2 Ensure that operations on shared variables are atomic

In addition to ensuring that updates to shared variables are visible to other threads, you also need to ensure that operations on shared variables are atomic, and declaring shared variables volatile is often not sufficient. Need to synchronize read and write operations using a synchronization mechanism or Lock:

final class Flag {
  private volatile boolean flag = true;
  public synchronized void toggle() {
    flag ^= true; // Same as flag = !flag;
  }
  public boolean getFlag() {
    return flag;
  }
}
Copy the code

// Use read locks to ensure atomicity of read and write operations

final class Flag { private boolean flag = true; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); public void toggle() { writeLock.lock(); try { flag ^= true; // Same as flag = ! flag; } finally { writeLock.unlock(); } } public boolean getFlag() { readLock.lock(); try { return flag; } finally { readLock.unlock(); }}}Copy the code

7.3 Do not call thread.run () and do not terminate a Thread with thread.stop ()

7.4 Ensure that the blocking thread can be terminated

  public final class SocketReader implements Runnable {
  private final SocketChannel sc;
  private final Object lock = new Object();
  public SocketReader(String host, int port) throws IOException {
    sc = SocketChannel.open(new InetSocketAddress(host, port));
  }
  @Override public void run() {
    ByteBuffer buf = ByteBuffer.allocate(1024);
    try {
      synchronized (lock) {
        while (!Thread.interrupted()) {
          sc.read(buf);
          // ...
        }
      }
    } catch (IOException ie) {
      // Forward to handler
    }
  }
  public static void main(String[] args) 
                          throws IOException, InterruptedException {
    SocketReader reader = new SocketReader("somehost", 25);
    Thread thread = new Thread(reader);
    thread.start();
    Thread.sleep(1000);
    thread.interrupt();
  }
}
Copy the code

7.5 Interdependent tasks should not be executed in a limited thread pool

Finite thread pool specifies an upper limit on the number of threads that can execute simultaneously in the thread pool. Programs must not use finite pool threads to perform interdependent tasks. A thread starvation deadlock can result, where all thread pools execute tasks that are waiting for an available thread to execute an internal queue block


8 INPUT and Output

8.1 Delete temporary files before program termination

8.2 Detect and handle file related errors

Java file manipulation methods tend to have a return value rather than throw an exception indicating failure. Therefore, programs that ignore return value file operations are often unable to detect whether those operations have failed. Java programs must check the return value of the executable file I/O method.

Incorrect way of writing:

File file = new File(args[0]); file.delete(); Correct: File File = new File(" File "); if (! file.delete()) { log.error("Deletion failed"); }Copy the code

8.3 Releasing Resources in time

The garbage collector cannot free non-memory resources, such as open file descriptors and connections to databases. Therefore, failure to release resources may result in resource exhaustion attacks.

try { final FileInputStream stream = new FileInputStream(fileName); try { final BufferedReader bufRead = new BufferedReader(new InputStreamReader(stream)); String line; while ((line = bufRead.readLine()) ! = null) { sendLine(line); } } finally { if (stream ! = null) { try { stream.close(); } catch (IOException e) { // forward to handler } } } } catch (IOException e) { // forward to handler }Copy the code

9 serialization

9.1 Do not serialize unencrypted sensitive data

Serialization allows an object’s state to be saved as a sequence of bytes and then restored at a later time, without providing any mechanism to protect serialized data. Examples of sensitive data that should not be serialized include encryption keys, digital certificates. Solutions:

  1. You can use transient for data members, declaring that the data member is transient.
  2. Rewrite serialization methods writeObject, readObject, and readObjectNoData to prevent them from being maliciously overwritten by subclasses
class SensitiveClass extends Number { // ... protected final Object writeObject(java.io.ObjectOutputStream out) throws NotSerializableException { throw new NotSerializableException(); } protected final Object readObject(java.io.ObjectInputStream in) throws NotSerializableException { throw new NotSerializableException(); } protected final Object readObjectNoData(java.io.ObjectInputStream in) throws NotSerializableException { throw new NotSerializableException(); }}Copy the code

9.2 Avoid memory and resource leaks during serialization

Incorrect way of writing:

class SensorData implements Serializable {
  // 1 MB of data per instance!
   public static SensorData readSensorData() {...}
  public static boolean isAvailable() {...}
}
class SerializeSensorData {
  public static void main(String[] args) throws IOException {
    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(
          new BufferedOutputStream(new FileOutputStream("ser.dat")));
      while (SensorData.isAvailable()) {
        // note that each SensorData object is 1 MB in size
        SensorData sd = SensorData.readSensorData();
        out.writeObject(sd);
      }
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }
}
Copy the code

Correct way to write:

class SerializeSensorData { public static void main(String[] args) throws IOException { ObjectOutputStream out = null; try { out = new ObjectOutputStream( new BufferedOutputStream(new FileOutputStream("ser.dat"))); while (SensorData.isAvailable()) { // note that each SensorData object is 1 MB in size SensorData sd = SensorData.readSensorData(); out.writeObject(sd); out.reset(); // reset the stream } } finally { if (out ! = null) { out.close(); }}}}Copy the code

9.3 Deserialization should be conducted in a secure environment with minimal program permissions

(complete)