1、打印一个一次性文件及其堆栈跟踪到某个流。默认情况下,该stream System.Err可能会无意中公开敏感信息。
应该使用记录器来打印一次性文件,因为它们有许多优点:
用户可以轻松地检索日志。
日志消息的格式是统一的,允许用户轻松浏览日志。
如果使用printStackTrace时没有参数,即堆栈跟踪打印到默认流时,此规则会引发问题。
Noncompliant Code Example
try {
/* ... */
} catch(Exception e) {
e.printStackTrace(); // Noncompliant
}
Compliant Solution
try {
/* ... */
} catch(Exception e) {
LOGGER.log("context", e);
}
2、当函数调用的返回值包含操作状态代码时,应测试该值以确保操作成功完成。
当忽略以下返回值时,此规则会引发问题:
java.io.File operations that return a status code (except mkdirs)
Iterator.hasNext()
Enumeration.hasMoreElements()
Lock.tryLock()
non-void Condition.await* methods
CountDownLatch.await(long, TimeUnit)
Semaphore.tryAcquire
BlockingQueue: offer, remove, drainTo。
如:
public void doSomething(File file, Lock lock) {
file.delete(); // Noncompliant
// ...
lock.tryLock(); // Noncompliant
} 不合规
public void doSomething(File file, Lock lock) {
if (!lock.tryLock()) {
// lock failed; take appropriate action
}
if (!file.delete()) {
// file delete failed; take appropriate action
}
} 合规
3、代码中不应以硬编码方式出现密码,用户,ip等关键信息。
由于从已编译的应用程序中提取字符串很容易,因此不应硬编码凭据。这样做,它们几乎肯定会落入攻击者手中。这对于分布式应用程序尤其如此。
凭据应存储在代码之外的受强保护的加密配置文件或数据库中。
如
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=steve&password=blue"); // Noncompliant
String uname = "steve";
String password = "blue";
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=" + uname + "&password=" + password); // Noncompliant
java.net.PasswordAuthentication pa = new java.net.PasswordAuthentication("userName", "1234".toCharArray()); // Noncompliant不合规
try {
String uname = getEncryptedUser();
String password = getEncryptedPass();
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=" + uname + "&password=" + password);}合规
4、公共类变量字段不尊重封装原则,有三个主要缺点:
无法添加其他行为,如验证。
内部表示是公开的,以后不能更改。
成员值可能会在代码中的任何地方发生变化,并且可能不符合程序员的假设。
通过使用私有属性和访问器方法(set和get),可以防止未经授权的修改。
5、对字段声明 public 和 static 后 ,一定要加上 final 声明。
如
public class Greeter {
public static Foo foo = new Foo();
...
} 不合规
public class Greeter {
public static final Foo FOO = new Foo();
...
} 合规
6、枚举通常被认为是常量,但是带有公共字段或公共设置器的枚举不仅是非常量,而且容易受到恶意代码的攻击。
理想情况下,枚举中的字段是私有的,并在构造函数中设置,但如果这不可能,则应尽可能降低它们的可见性。
7、可变对象作为公共静态成员变量时,应定义为protected
没有充分的理由将可变对象作为接口的公共(默认情况下)静态成员。这些变量应该移入类中,并降低它们的可见性。
类似地,类和枚举的可变静态成员(直接访问而不是通过getter和setter访问)应该受到尽可能多的保护。这可以通过降低可见性或在适当的情况下使字段成为最终字段来完成。
注意,将可变字段(如数组)设为final将避免重新分配变量,但这样做不会影响数组内部状态的可变性(即,它无法实现目标)。
此规则引发公共静态数组、集合、日期和awt.Point成员的问题。
8、"YYYY"不应该被用于格式化年,应该使用"yyyy"。
9、不要将你的方法名命名为finalize,或者直接调用finalize()方法。
根据官方javadoc文档,当垃圾回收确定不再有对对象的引用时,垃圾回收器会对对象调用Object.finalize()。调用此方法显式违反了此约定,因此具有误导性。
10、不能基于String或装箱数据同步代码块
11、Iterator.hasNext()方法中不要调用next()方法。
12、Cookie应加secure属性,“secure”属性防止通过明文连接(如HTTP)发送cookie,因为在这些连接上,cookie很容易被窃听。相反,带有secure属性的cookies只通过加密的HTTPS连接发送。
Cookie c = new Cookie("Code","asda"); c.setSecure(true);
13、controller中@RequestMapping注解的方法应该用"public"。AOP代理不适用非公共方法。
14、NIO创建临时文件,JDK7+时,使用Files.createTempDirectory创建,使用File.createTempFile作为创建临时目录的第一步会导致争用情况,并且本质上是不可靠和不安全的。
15、"HttpServletRequest.getRequestedSessionId()"不应该被使用,返回客户端指定的会话ID。这可能与此请求的当前有效会话的ID不同。如果客户端未指定会话ID,则此方法返回空值。
它返回的会话ID要么在cookie中传输,要么在URL参数中传输,因此根据定义,没有什么可以阻止最终用户在HTTP请求中手动更新此会话ID的值。
由于最终用户能够手动更改该值,因此请求中的会话ID只能由servlet容器(例如Tomcat或Jetty)使用,以查看该值是否与现有会话的ID匹配。如果没有,则应认为该用户未经身份验证。此外,不应记录此会话ID以防止劫持活动会话。
16、"javax.crypto.NullCipher"不应用于除测试以外的任何其他用途。根据约定,NullCipher类提供一个“身份密码”,它不会以任何方式转换或加密明文。因此,密文与明文相同。所以这个类应该用于测试,而不是在生产代码中。
17、“SecureRandom”种子不应该是可预测的。security.SecureRandom类提供了一个适合于加密的强随机数生成器(RNG)。然而,用一个常数或另一个可预测的值来播种会大大削弱它。一般来说,依赖SecureRandom实现提供的种子更安全。
当使用以下任一种子调用SecureRandom.setSeed()或SecureRandom(byte[])时,此规则会引发问题:1、常数,2、System.currentTimeMillis();
如:SecureRandom sr = new SecureRandom();
sr.setSeed(123456L); // Noncompliant
int v = sr.next(32);
sr = new SecureRandom("abcdefghijklmnop".getBytes("us-ascii")); // Noncompliant
应该
SecureRandom sr = new SecureRandom();
int v = sr.next(32);
18、不应该动态加载类。动态加载的类可能包含由静态类初始值设定项执行的恶意代码。一、 你甚至不必实例化或显式调用这些类上的方法就容易受到攻击。
对于每次使用动态类加载,此规则都会引发一个问题。
如:
String className = System.getProperty("messageClassName");
Class clazz = Class.forName(className); // Noncompliant
19、加密RSA算法应始终包含OAEP(最佳非对称加密填充)。如果RSA加密中没有OAEP,攻击者解密数据或从密文推断模式所需的工作就更少。一旦文本值以RSA/NONE开头,此规则就会记录一个问题。
Cipher rsa = javax.crypto.Cipher.getInstance("RSA/NONE/NoPadding");//Noncompliant
Cipher rsa = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");//Compliant
20、定义得拦截器应该被使用。web.xml文件中定义的每个过滤器都应该在
Noncompliant Code Example
Compliant Solution
21、未能捕获servlet中的异常可能会使系统处于易受攻击的状态,可能导致拒绝服务攻击或敏感信息的泄漏,因为当servlet抛出异常时,servlet容器通常会将调试信息发送回用户。对于攻击者来说,这些信息可能非常有价值。
22、HTTP请求中的字段是攻击者掌握的,您不能依赖它们来告诉您任何事情的真相。虽然在这些值被中和后存储这些值可能是安全的,但决不能基于它们的内容做出决定。
23、应指定成员变量可见性。未能显式声明成员变量的可见性可能会导致其具有您不期望的可见性,并可能使其对其他类的意外修改保持打开状态。
24、默认情况下,Spring@Controllers、@Services和@Repositorys是单例的,这意味着应用程序中只实例化了类的一个实例。通常,这样的类可能有一些静态成员,比如记录器,但所有非静态成员都应该由Spring管理。也就是说,它们应该具有以下注释之一:@Resource、@Inject、@Autowired或@Value。
在其中一个类中包含未注入的成员可能表示有人试图管理状态。因为它们是单例的,这样的尝试几乎可以保证最终将来自User1会话的数据公开给User2。
当singleton@Controller、@Service或@Repository具有非静态成员,而这些成员未使用以下之一进行注释时,此规则会引发问题。
25、可变对象不能直接存储或者返回,可变对象是那些状态可以更改的对象。例如,数组是可变的,但字符串不是可变的。不应将可变类成员返回给调用方或直接接受和存储。这样做会使您容易受到类状态的意外更改的影响。
26、不应使用DES(数据加密标准)或DESede(3DES)。根据美国国家标准与技术研究所(NIST),数据加密标准(DES)不再被认为是安全的。
Noncompliant Code Example
Cipher c = Cipher.getInstance("DESede/ECB/PKCS5Padding");
Compliant Solution
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
27、只能使用标准加密算法。使用非标准算法是危险的,因为确定的攻击者可能会攻破该算法并破坏任何受保护的数据。标准算法,如SHA-256、SHA-384、SHA-512。。。应该被使用。
28、伪随机数生成器(prng)不应在安全上下文中使用。当软件在需要不可预测性的上下文中生成可预测值时,攻击者可能会猜测将生成的下一个值,并使用此猜测来模拟其他用户或访问敏感信息。
由于java.util.Random类依赖于伪随机数生成器,因此该类和相关的java.lang.Math.Random()方法不应用于安全关键应用程序或保护敏感数据。在这种情况下,应该使用java.security.SecureRandom类,该类依赖于加密强随机数生成器(RNG)。
29、不应使用SHA-1和消息摘要哈希算法。MD5算法及其后续的SHA-1不再被认为是安全的,因为它很容易与它们产生散列冲突。也就是说,只需很少的计算工作,就可以产生相同的MD5或SHA-1哈希值的不同输入,并且使用新的、相同的哈希值可以让攻击者获得与原始哈希值相同的访问权限。这同样适用于其他消息摘要算法:MD2、MD4、MD6、RIPEMD160。
30、应用中执行SQL命令时应该使外部的参数值无效,不这样做的话会被攻击者更改命令,从而执行意外命令,或者暴露敏感数据。此规则检查来自不同框架的各种方法,如果使用不当,这些方法易受SQL注入的影响。涵盖的框架有Java JDBC、JPA、JDO、Hibernate和Spring。
Noncompliant Code Example
public User getUser(Connection con, String user) throws SQLException {
Statement stmt1 = null;
Statement stmt2 = null;
PreparedStatement pstmt;
try {
stmt1 = con.createStatement();
ResultSet rs1 = stmt1.executeQuery("GETDATE()"); // Compliant; parameters not used here
stmt2 = con.createStatement();
ResultSet rs2 = stmt2.executeQuery("select FNAME, LNAME, SSN " +
"from USERS where UNAME=" + user); // Noncompliant; parameter concatenated directly into query
pstmt = con.prepareStatement("select FNAME, LNAME, SSN " +
"from USERS where UNAME=" + user); // Noncompliant; parameter concatenated directly into query
ResultSet rs3 = pstmt.executeQuery();
//...
}
public User getUserHibernate(org.hibernate.Session session, String userInput) {
org.hibernate.Query query = session.createQuery( // Compliant
"FROM students where fname = " + userInput); // Noncompliant; parameter binding should be used instead
// ...
}
Compliant Solution
public User getUser(Connection con, String user) throws SQLException {
Statement stmt1 = null;
PreparedStatement pstmt = null;
String query = "select FNAME, LNAME, SSN " +
"from USERS where UNAME=?"
try {
stmt1 = con.createStatement();
ResultSet rs1 = stmt1.executeQuery("GETDATE()");
pstmt = con.prepareStatement(query);
pstmt.setString(1, user); // Compliant; PreparedStatements escape their inputs.
ResultSet rs2 = pstmt.executeQuery();
//...
}
}
public User getUserHibernate(org.hibernate.Session session, String userInput) {
org.hibernate.Query query = session.createQuery("FROM students where fname = ?");
query = query.setParameter(0,userInput); // Parameter binding escapes all input
// ...
31、不受信任的数据不应被保存在session中。web会话中的数据被认为在“信任边界”内。也就是说,它被认为是值得信赖的。但是存储未经验证的用户的未经验证的数据会违反信任边界,并可能导致该数据被不适当地使用。
32、web应用程序中不应该有"main"方法。