java安全编码规范

简介: java安全编码规范

java安全编码规范
1输入验证和数据合法性校验
程序接受数据可能来源于未经验证的用户,网络连接和其他不受信任的来源,如果未对程序接受数据进行校验,则可能会引发安全问题。
1.1避免SQL注入
使用PreparedStatement预编译SQL,解决SQL注入问题,传递给PreparedStatement对象的参数可以被强制进行类型转换,确保在插入或查询数据时与底层的数据库格式匹配。
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();
1.2避免XML注入
通过StringBulider 或 StringBuffer 拼接XML文件时,需对输入数据进行合法性校验。 对数量quantity 进行合法性校验,控制只能传入0-9的数字:

if (!Pattern.matches("[0-9]+", quantity)) {

// Format violation

}
String xmlString = "\nWidget\n" +

                 "<price>500</price>\n" +
                 "<quantity>" + quantity + "</quantity></item>";

outStream.write(xmlString.getBytes());
outStream.flush();

1.3避免跨站点脚本(XSS)
对产生跨站的参数进行严格过滤,禁止传入

balance = deposit - 10; // Subtract processing fee

}
public static void main(String[] args) {

System.out.println("The account balance is: " + c.balance);

}
}

类加载时初始化指向Cycle类的静态变量c,而类Cycle的无参构造方法又依赖静态变量deposit,导致无法预期的结果。 正确的写法:
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);

}
}
3表达式
3.1不可忽略方法的返回值
忽略方法的放回值可能会导致无法预料的结果。
错误的写法:
public void deleteFile(){
File someFile = new File("someFileName.txt");
someFile.delete();
}
正确的写法:
public void deleteFile(){
File someFile = new File("someFileName.txt");
if (!someFile.delete()) {

// handle failure to delete the file

}
}
3.2不要引用空指针
当一个变量指向一个NULL值,使用这个变量的时候又没有检查,这时会导致。
NullPointerException。

在使用变量前一定要做是否为NULL值的校验。
3.3使用Arrays.equals()来比较数组的内容
数组没有覆盖的Object. equals()方法,调用Object. equals()方法实际上是比较数组的引用,而不是他们的内容。程序必须使用两个参数Arrays.equals()方法来比较两个数组的内容
public void arrayEqualsExample() {
int[] arr1 = new int[20]; // initialized to 0
int[] arr2 = new int[20]; // initialized to 0
Arrays.equals(arr1, arr2); // true
}
4数字类型和操作
4.1防止整数溢出
使用java.lang.Number. BigInteger类进行整数运算,防止整数溢出。
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(); 
}

}
4.2避免除法和取模运算分母为零
要避免因为分母为零而导致除法和取模运算出现异常。
if (num2 == 0) {
// handle error
} else {
result1= num1 /num2;
result2= num1 % num2;
}

5类和方法操作
5.1数据成员声明为私有,提供可访问的包装方法
攻击者可以用意想不到的方式操纵public或protected的数据成员,所以需要将数据成员为private,对外提供可控的包装方法访问数据成员。
5.2敏感类不允许复制
包含私人的,机密或其他敏感数据的类是不允许被复制的,解决的方法有两种:
1、类声明为final
final class SensitiveClass {
// ...
}
2、Clone 方法抛出CloneNotSupportedException异常
class SensitiveClass {
// ...
public final SensitiveClass clone() throws CloneNotSupportedException {

throw new CloneNotSupportedException();

}
}
5.3比较类的正确做法
如果由同一个类装载器装载,它们具有相同的完全限定名称,则它们是两个相同的类。 不正确写法:
// 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) {
// ...
}
5.4不要硬编码敏感信息
硬编码的敏感信息,如密码,服务器IP地址和加密密钥,可能会泄露给攻击者。
敏感信息均必须存在在配置文件或数据库中。
5.5验证方法参数
验证方法的参数,可确保操作方法的参数产生有效的结果。不验证方法的参数可能会导致不正确的计算,运行时异常,违反类的不变量,对象的状态不一致。 对于跨信任边界接收参数的方法,必须进行参数合法性校验
private Object myState = null;
//对于修改myState 方法的入参,进行非空和合法性校验
void setState(Object state) {
if (state == null) {

// Handle null state

}
if (isInvalidState(state)) {

// Handle invalid state

}
myState = state;
}
5.6不要使用过时、陈旧或低效的方法
在程序代码中使用过时的、陈旧的或低效的类或方法可能会导致错误的行为。
5.7数组引用问题
某个方法返回一个对敏感对象的内部数组的引用,假定该方法的调用程序不改变这些对象。即使数组对象本身是不可改变的,也可以在数组对象以外操作数组的内容,这种操作将反映在返回该数组的对象中。如果该方法返回可改变的对象,外部实体可以改变在那个类中声明的 public 变量,这种改变将反映在实际对象中。
不正确的写法:
public class XXX {

private String[] xxxx;
public String[] getXXX() {
        return xxxx;
}

}
正确的写法:
public class XXX {

private String[] xxxx;
public String[] getXXX() {
        String temp[] = Arrays.copyof(…);  // 或其他数组复制方法
        return temp;
}

}
5.8不要产生内存泄露
垃圾收集器只收集不可达的对象,因此,存在未使用的可到达的对象,仍然表示内存管理不善。过度的内存泄漏可能会导致内存耗尽,拒绝服务(DoS)。
6异常处理
6.1不要忽略捕获的异常
对于捕获的异常要进行相应的处理,不能忽略已捕获的异常
不正确写法
class Foo implements Runnable {
public void run() {

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // 此处InterruptedException被忽略
}

}
}
正确写法:
class Foo implements Runnable {
public void run() {

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt(); // Reset interrupted status
}

}
}
6.2不允许暴露异常的敏感信息
没有过滤敏感信息的异常堆栈往往会导致信息泄漏,
不正确的写法:
try {
FileInputStream fis =

  new FileInputStream(System.getenv("APPDATA") + args[0]);

} catch (FileNotFoundException e) {
// Log the exception
throw new IOException("Unable to retrieve file", e);
}
正确的写法:
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;
}

}
}
6.3不允许抛出RuntimeException, Exception,Throwable
不正确的写法:
boolean isCapitalized(String s) {
if (s == null) {

throw new RuntimeException("Null String");

}
}

private void doSomething() throws Exception {
//...
}
正确写法:
boolean isCapitalized(String s) {
if (s == null) {

throw new NullPointerException();

}
}

private void doSomething() throws IOException {
//...
}
6.4不要捕获NullPointerException或其他父类异常
不正确的写法:
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;

}
}
正确的写法:
boolean isName(String s) / throws NullPointerException / {
String names[] = s.split(" ");
if (names.length != 2) {

return false;

}
return (isCapitalized(names[0]) && isCapitalized(names[1]));
}
7多线程编程
7.1确保共享变量的可见性
对于共享变量,要确保一个线程对它的改动对其他线程是可见的。 线程可能会看到一个陈旧的共享变量的值。为了共享变量是最新的,可以将变量声明为volatile或同步读取和写入操作。 将共享变量声明为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;

}
}
同步读取和写入操作:
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;

}
}
7.2确保共享变量的操作是原子的
除了要确保共享变量的更新对其他线程可见的,还需要确保对共享变量的操作是原子的,这时将共享变量声明为volatile往往是不够的。需要使用同步机制或Lock 同步读取和写入操作:
final class Flag {
private volatile boolean flag = true;
public synchronized void toggle() {

flag ^= true; // Same as flag = !flag;

}
public boolean getFlag() {

return flag;

}
}
//使用读取锁确保读取和写入操作的原子性
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();
}

}
}
7.3不要调用Thread.run(),不要使用Thread.stop()以终止线程
7.4确保执行阻塞操作的线程可以终止
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();

}
}
7.5相互依存的任务不要在一个有限的线程池执行
有限线程池指定可以同时执行在线程池中的线程数量的上限。程序不得使用有限线程池线程执行相互依赖的任务。可能会导致线程饥饿死锁,所有的线程池执行的任务正在等待一个可用的线程中执行一个内部队列阻塞
8输入输出
8.1程序终止前删除临时文件
8.2检测和处理文件相关的错误
Java的文件操作方法往往有一个返回值,而不是抛出一个异常,表示失败。因此,忽略返回值文件操作的程序,往往无法检测到这些操作是否失败。Java程序必须检查执行文件I / O方法的返回值。
不正确的写法:
File file = new File(args[0]);
file.delete();
正确的写法:
File file = new File("file");
if (!file.delete()) {
log.error("Deletion failed");
}
8.3及时释放资源
垃圾收集器无法释放非内存资源,如打开的文件描述符与数据库的连接。因此,不释放资源,可能导致资源耗尽攻击。
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
}
9序列化
9.1不要序列化未加密的敏感数据
序列化允许一个对象的状态被保存为一个字节序列,然后重新在稍后的时间恢复,它没有提供任何机制来保护序列化的数据。敏感的数据不应该被序列化的例子包括加密密钥,数字证书。 解决方法:

  1. 对于数据成员可以使用transient ,声明该数据成员是瞬态的。
  2. 重写序列化相关方法writeObject、readObject、readObjectNoData,防止被子类恶意重写

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();

}
}
9.2在序列化过程中避免内存和资源泄漏
不正确的写法:
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();
  }
}

}
}
正确写法:
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();
  }
}

}
}
9.3反序列化要在程序最小权限的安全环境中

相关文章
|
14天前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
43 11
|
15天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
38 11
|
30天前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
1月前
|
Java
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
1月前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
1月前
|
安全 Java 测试技术
深入探讨Java安全编程的最佳实践,帮助开发者保障应用的安全性
在网络安全日益重要的今天,确保Java应用的安全性成为了开发者必须面对的课题。本文介绍Java安全编程的最佳实践,包括利用FindBugs等工具进行代码审查、严格验证用户输入以防攻击、运用输出编码避免XSS等漏洞、实施访问控制确保授权访问、采用加密技术保护敏感数据等。此外,还强调了使用最新Java版本、遵循最小权限原则及定期安全测试的重要性。通过这些实践,开发者能有效提升Java应用的安全防护水平。
31 1
|
20天前
|
前端开发 开发者 安全
JSF支付功能大揭秘:探索如何在Java世界中实现安全无缝的在线支付体验
【8月更文挑战第31天】在电子商务和在线交易日益普及的今天,实现在线支付功能已成为许多Web应用的必备需求。JavaServer Faces (JSF) 作为一种流行的Java Web框架,提供了丰富的组件和工具来构建用户界面,包括与支付网关集成以实现在线支付。支付网关是处理信用卡和借记卡支付的系统,在商家和银行之间起到桥梁作用。本文将探讨如何使用JSF与支付网关集成,以及实现在线支付功能时需要考虑的关键点
32 0
|
1月前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
50 1
|
2月前
|
安全 Java 开发者
Java并发编程:理解并发安全与性能优化
在当今软件开发中,Java作为一种广泛使用的编程语言,其并发编程能力显得尤为重要。本文深入探讨了Java中的并发编程,包括如何确保并发安全性以及优化并发程序的性能。通过分析常见的并发问题和解决方案,读者将能够更好地理解如何利用Java的并发工具包来构建可靠和高效的多线程应用程序。 【7月更文挑战第10天】
|
2月前
|
Oracle Java 关系型数据库
Java中的编码规范与代码审查实践
Java中的编码规范与代码审查实践