安装sonar并配置
docker安装sonarqube,sonarQube静态代码扫描 - Joson6350 - 博客园 (***blogs.***)
对webgoat进行sonar扫描
扫描结果
bugs
Change this condition so that it does not always evaluate to "false"
意思是这里的else if语句不会执行,因为ipAddressKnow为true,所以if 和else if的条件结果是一样的。
Use try-with-resources or close this "PreparedStatement" in a "finally" clause.
提示资源没有关闭,需要在finally中进行资源关闭,但是把资源关闭放到finally中由提示这样写不规范有异味。所以它推荐的写法是将创建资源流的代码放在try()中,这样系统会自动的关闭资源,不需要我们写.close()方法
try-with-resources
java 异常处理 | 菜鸟教程 (runoob.***)
JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。
try-with-resources 是一种异常处理机制,它可以简化资源管理代码的编写。
JDK7 之前所有被打开的系统资源,比如流、文件或者 Socket 连接等,都需要被开发者手动关闭,否则将会造成资源泄露。
try (resource declaration) { // 使用的资源 } catch (ExceptionType e1) { // 异常块 }
以上的语法中 try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。
注意:try-with-resources 语句关闭所有实现 AutoCloseable 接口的资源。
参考:Java 异常处理 | 菜鸟教程 (runoob.***)
【转】Sonar扫描bug修复 - 登风360 - 博客园 (***blogs.***)
合规方案 在try语句进行实例定义,以防失败。
private void readTheFile(String fileName) throws IOException {
Path path = Paths.get(fileName);
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
reader.readLine();
// ...
}
// ..
try (Stream<String> input = Files.lines("input.txt")) {
input.forEach(System.out::println);
}
}
private void doSomething() {
OutputStream stream = null;
try {
stream = new FileOutputStream("myfile.txt");
for (String property : propertyList) {
// ...
}
} catch (Exception e) {
// ...
} finally {
stream.close();
}
}
修复
try (var connection = dataSource.getConnection()) {
try(PreparedStatement statement =
connection.prepareStatement(
"select password from challenge_users where userid = '"
+ username_login
+ "' and password = '"
+ password_login
+ "'")) {
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return su***ess(this).feedback("challenge.solved").feedbackArgs(flags.getFlag(5)).build();
} else {
return failed(this).feedback("challenge.close").build();
}
}
}
"Random" objects should be reused
问题:在类的多个方法中使用了Random random = new Random();不应该在方法内创建random实例,而应该把random实例创建为类的属性,可以在多个地方调用,而且建议用 SecureRandom is preferred to Random。
解决:随机数实例定义为一个属性,然后在下面的方法里面生成随机值
private Random rand = SecureRandom.getInstanceStrong(); // SecureRandom is preferred to Random
public void doSomething***mon() {
int rValue = this.rand.nextInt();
//...
Use the "equals" method if value ***parison was intended.
这个误报了,因为这里!=是用来判断不为null,跟值比较用的是equals方法
合规的方案也是这样写的。
String firstName = getFirstName();
String lastName = getLastName();
if (firstName != null && firstName.equals(lastName)) { ... };
Do something with the "boolean" value returned by "createNewFile"
意思是这个方法可能失败,但是并没有失败处理。
***pliant Solution
public void doSomething(File file, Lock lock) {
if (!lock.tryLock()) {
// lock failed; take appropriate action
}
if (!file.delete()) {
// file delete failed; take appropriate action
}
}
Null pointers should not be dereferenced
代码中存在空指针解引用的问题。空指针解引用指的是当一个对象为空(null)时,尝试访问它的方法或属性。
为了修复这个问题,你需要确保在访问对象的方法或属性之前,先进行空指针检查
A "Map<WebGoatUser, ***ments>" cannot contain a "String" in a "WebGoatUser" type.
vulnerability
Don't use the default "PasswordEncoder" relying on plain-text.
不要用纯文本或者快速hash算法存储password ,应该使用安全的算法产生hash。原因
1、预防暴力破解攻击
2、预防撞库攻击
3、密码应该加salt来降低彩虹表攻击的风险。
The JWT signature (JWS) should be verified before using this token
JWT应该使用强加密算法签名和验证
1、不要使用none算法签名或验证token的有效性
2、不要在没有验证签名之前试用token。
这里就是在没有验证之前,就对token进行解析,如果攻击者将算法设置为none后加密,也可以通过。
Disable a***ess to external entities in XML parsing
禁止在XML解析中访问外部实体
XML解析可能导致XXE攻击
Security Hotspots
Security-sensitive code that requires manual review to assess whether or not a vulnerability exists.安全敏感代码需要人工审查来判断是否存在漏洞。
可以看到检测出好几类问题,包括认证,CSSRF,SQL注入,DOS,弱加密,不安全配置和其他。
authentication 认证
'PASSWORD' detected in this expression, review this potentially hard-coded credential.
问题:这里把密码硬编码在文件中。
由于很容易从应用程序源代码或二进制文件中提取字符串,因此不应对凭据进行硬编码。尤其如此 适用于分布式或开源应用程序。
建议方案
1、凭据存放在配置文件中
2、凭据存放在数据库中
3、如果密码通过源代码泄露,则修改密码。
CSRF 跨站点请求伪造
Make sure allowing safe and unsafe HTTP methods is safe here.
问题:对于同一个url,同时使用了get和post方法
HTTP 方法在用于执行只读操作(如检索信息)时是安全的。相反,不安全的 HTTP 方法用于 更改应用程序的状态,例如在 Web 应用程序上更新用户的配置文件。
常见的安全 HTTP 方法有 GET、HEAD 或 OPTIONS。
常见的不安全 HTTP 方法有 POST、PUT 和 DELETE。
允许安全和不安全的 HTTP 方法对 Web 应用程序执行特定操作可能会影响其安全性,例如 CSRF 大多数情况下,保护仅保护由不安全的 HTTP 方法执行的操作。
敏感代码示例
@RequestMapping("/delete_user") // Sensitive: by default all HTTP methods are allowed
public String delete1(String username) {
// state of the application will be changed here
}
@RequestMapping(path = "/delete_user", method = {RequestMethod.GET, RequestMethod.POST}) // Sensitive: both safe and unsafe methods are allowed
String delete2(@RequestParam("id") String id) {
// state of the application will be changed here
}
推荐的安全编码实践
对于应用程序的所有路由/控制器,应显式定义授权的 HTTP 方法,并且仅应定义安全的 HTTP 方法 用于执行只读操作。
合规解决方案
@RequestMapping("/delete_user", method = RequestMethod.POST) // ***pliant
public String delete1(String username) {
// state of the application will be changed here
}
@RequestMapping(path = "/delete_user", method = RequestMethod.POST) // ***pliant
String delete2(@RequestParam("id") String id) {
// state of the application will be changed here
}
有问题代码
这里没指定请求方法
Make sure disabling Spring Security's CSRF protection is safe here.
当攻击者可以强制 Web 应用程序的受信任用户执行敏感操作时,就会发生跨站点请求伪造 (CSRF) 攻击 他不打算执行的操作,例如更新他的个人资料或发送消息,更一般地说,任何可以改变状态的行为。 应用。
攻击者可以诱骗用户/受害者单击与特权操作相对应的链接,或访问嵌入 隐藏的 Web 请求,并且由于 Web 浏览器自动包含 cookie,因此可以对操作进行身份验证和敏感操作。
是否有风险?
问问自己是否
- Web 应用程序使用 Cookie 对用户进行身份验证。
- Web 应用程序中存在敏感操作,可以在对用户进行身份验证时执行这些操作。
- 例如,可以通过执行HTTP POST或HTTP DELETE请求来修改Web应用程序的状态/资源。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例
默认情况下,Spring Security 提供了一个 防止 CSRF 攻击,可以禁用:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // Sensitive: csrf protection is entirely disabled
// or
http.csrf().ignoringAntMatchers("/route/"); // Sensitive: csrf protection is disabled for specific routes
}
}
推荐的安全编码实践
- 强烈建议对 CSRF 攻击进行防护:
- 默认情况下,为所有不安全的 HTTP 激活 方法。
- 例如,使用不可猜测的 CSRF 令牌实现
- 当然,所有敏感操作都不应该使用安全的HTTP方法执行,例如设计为 仅用于信息检索。
GET
合规解决方案
Spring SecurityCSRF 保护是 默认启用,请勿禁用:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable(); // ***pliant
}
}
SQL注入
Make sure using a dynamically formatted SQL query is safe here.
确保使用动态的格式化SQL语句是安全的
格式化的 SQL 查询可能难以维护和调试,并且将不受信任的值连接到 查询
问问自己是否
- 查询的某些部分来自不受信任的值(如用户输入)。
- 查询在代码的其他部分重复/重复。
- 应用程序必须支持不同类型的关系数据库。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例 因为用户输入被直接拼接至SQL,会存在SQL注入问题。
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()"); // No issue; hardcoded query
stmt2 = con.createStatement();
ResultSet rs2 = stmt2.executeQuery("select FNAME, LNAME, SSN " +
"from USERS where UNAME=" + user); // Sensitive
pstmt = con.prepareStatement("select FNAME, LNAME, SSN " +
"from USERS where UNAME=" + user); // Sensitive
ResultSet rs3 = pstmt.executeQuery();
//...
}
public User getUserHibernate(org.hibernate.Session session, String data) {
org.hibernate.Query query = session.createQuery(
"FROM students where fname = " + data); // Sensitive
// ...
}
推荐的安全编码实践
- 使用参数化查询、预准备语句或存储 过程并将变量绑定到 SQL 查询参数。
- 如果需要一个抽象层来访问数据,请考虑使用 ORM 框架。
合规解决方案 采用预编译语句
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); // Good; PreparedStatements escape their inputs.
ResultSet rs2 = pstmt.executeQuery();
//...
}
}
public User getUserHibernate(org.hibernate.Session session, String data) {
org.hibernate.Query query = session.createQuery("FROM students where fname = ?");
query = query.setParameter(0,data); // Good; Parameter binding escapes all input
org.hibernate.Query query2 = session.createQuery("FROM students where fname = " + data); // Sensitive
// ...
- OWASP Top 10 2017 Category A1 - Injection
- MITRE, CWE-89 - Improper Neutralization of Special Elements used in an SQL ***mand
- MITRE, CWE-564 - SQL Injection: Hibernate
- MITRE, CWE-20 - Improper Input Validation
- MITRE, CWE-943 - Improper Neutralization of Special Elements in Data Query Logic
- CERT, IDS00-J. - Prevent SQL injection
- SANS Top 25 - Insecure Interaction Between ***ponents
- Derived from FindSecBugs rules Potential SQL/JPQL Injection (JPA), Potential SQL/JDOQL Injection (JDO), Potential SQL/HQL Injection (Hibernate)
Denial of Service(DOS) 拒绝服务攻击
Make sure the regex used here, which is vulnerable to polynomial runtime due to backtracking, cannot lead to denial of service.
需要审查是否由于使用了正则导致DOS
问问自己是否
- 输入由用户控制。
- 输入大小不限于少量字符。
- 没有超时来限制正则表达式评估时间。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
Weak Cryptography 弱加密
Make sure this weak hash algorithm is not used in a sensitive context here.
请确保此处的敏感上下文中未使用此弱哈希算法。
passwordEncoder()
方法返回一个NoOpPasswordEncoder
实例,这是一个实现了PasswordEncoder
接口但并没有实际操作的类,它通常用于开发过程中,在不处理密码的情况下进行测试或开发。
这段代码的具体含义是:创建一个不执行任何操作的密码编码器实例,以供Spring容器管理。这种实例通常在开发或测试阶段使用,而在生产环境中,你可能需要使用更安全的密码编码器来保护你的用户密码。
危险:
MD2、
MD4、
MD5、
MD6、
HAVAL-128、
HMAC-MD5、
DSA、
SHA-1、
RIPEMD、
RIPEMD-128、
RIPEMD-160、
HMACRIPEMD160、
SHA-1、
collisions
如图显示的这些加密算法不安全,不建议使用,因为可能由于不同的输入产生same hash
问问自己是否
哈希值用于安全上下文,例如:
- 用户密码存储。
- 生成安全令牌(用于在网站上注册时确认电子邮件、重置密码等)。
- 计算一些消息完整性。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例
MessageDigest md1 = MessageDigest.getInstance("SHA"); // Sensitive: SHA is not a standard name, for most security providers it's an alias of SHA-1
MessageDigest md2 = MessageDigest.getInstance("SHA1"); // Sensitive
推荐的安全编码实践
建议使用更安全的替代方案,例如 、 ,对于密码哈希,它甚至 最好使用计算速度不太“快”的算法,例如 、 ,或者因为它会变慢。SHA-256
SHA-512
SHA-3
bcrypt
scrypt
argon2
pbkdf2
合规解决方案
MessageDigest md1 = MessageDigest.getInstance("SHA-512"); // ***pliant
Make sure that using this pseudorandom number generator is safe here.
确保在这里使用伪随机数生成器是安全的
问题:这里用了Random类生成随机数,这个类被认为是伪随机生成器,不推荐用这个类,推荐用 secureRandom类
使用伪随机数生成器 (PRNG) 是安全敏感的。例如,它过去曾导致以下漏洞:
- CVE-2013-6386 漏洞CVE-<>-<>
- CVE-2006-3419 漏洞CVE-<>-<>
- CVE-2008-4102 漏洞CVE-<>-<>
当软件在需要不可预测性的上下文中生成可预测值时,攻击者可能会猜测下一个值 将生成,并使用此猜测来冒充其他用户或访问敏感信息。
由于该类依赖于伪随机数生成器,因此此类和相关方法不应用于安全关键型应用程序或保护敏感数据。在这种情况下,应使用依赖于加密强随机数生成器 (RNG)
问问自己是否
- 使用生成值的代码要求它是不可预测的。所有加密机制都是这种情况,或者当一个秘密值时,例如 作为密码,经过哈希处理。
- 您使用的函数生成一个可以预测的值(伪随机)。
- 生成的值被多次使用。
- 攻击者可以访问生成的值。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例
Random random = new Random(); // Sensitive use of Random byte bytes[] = new byte[20]; random.nextBytes(bytes); // Check if bytes is used for hashing, encryption, etc...
推荐的安全编码实践
- 使用加密性强随机数生成器 (RNG),如“java.security.SecureRandom”来代替此 PRNG。
- 生成的随机值仅使用一次。
- 不应公开生成的随机值。如果必须存储它,请确保数据库或文件是安全的。
合规解决方案
SecureRandom random = new SecureRandom(); // ***pliant for security-sensitive use cases byte bytes[] = new byte[20]; random.nextBytes(bytes);
SecureRandom
类和Random
类都是Java中用于生成随机数的类,但它们之间有一些重要的区别。
Random
类生成的是伪随机数,这意味着它基于一个种子值生成数字,如果知道种子值,就可以预测生成的随机数序列。这对于一些简单的随机数生成需求来说是足够的,但对于需要高度安全性的应用,如密码生成或加密,这种可预测性就不够了。
SecureRandom
类是专门为需要高度安全性的随机数生成需求设计的。它生成的是真正的随机数,来源于系统提供的随机性源,如操作系统的熵池(entropy pool)。这意味着即使知道当前的随机数生成位置,也无法预测下一个生成的随机数。这种真正的随机性对于密码学和加密学非常重要,因为它们需要无法预测的随机数来保证安全性。总的来说,
SecureRandom
类比Random
类生成的数更随机,因为它提供了真正的随机性,而不是伪随机性。这对于需要高度安全性的应用来说是至关重要的。
Insecure configuration不安全配置
Make sure this debug feature is deactivated before delivering the code in production.
问题:这里用了printStackTrace方法来调用堆栈。
问问自己是否
- 启用应用程序调试功能的代码或配置部署在生产服务器上。
- 默认情况下,应用程序在激活调试功能的情况下运行。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例
Throwable.printStackTrace(...)
将 Throwable 及其堆栈跟踪打印到(默认情况下),这并不容易 可解析,并可能暴露敏感信息:System.Err
try {
/* ... */
} catch(Exception e) {
e.printStackTrace(); // Sensitive
}
SpringFramework 的 EnableWebSecurity 注解,用于启用调试支持:debug
true
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@Configuration
@EnableWebSecurity(debug = true) // Sensitive
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
推荐的安全编码实践
不要在生产服务器上启用调试功能。
合规解决方案
应使用记录器(而不是 )来打印投掷物:printStackTrace
try {
/* ... */
} catch(Exception e) {
LOGGER.log("context", e); // ***pliant
}
SpringFramework 的 EnableWebSecurity 注解,用于禁用调试支持:debug
false
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@Configuration
@EnableWebSecurity(debug = false) // ***pliant
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
其他
Make sure publicly writable directories are used safely here.
确保在此处安全地使用可公开写入的目录。
问题:这里用了 createTempDirectory方法创建文件,可能导致文件被创建至临时文件夹
操作系统具有全局目录,任何用户都具有写入访问权限。这些文件夹主要用作临时存储区域,就像在基于 Linux 的系统中一样。操作这些文件夹中的文件的应用程序会暴露在文件名的争用条件下:恶意 用户可以尝试在应用程序之前创建具有可预测名称的文件。成功的攻击可导致其他文件被访问, 已修改、损坏或删除。如果应用程序使用提升的权限运行,则此风险甚至更高。/tmp
过去,它导致了以下漏洞:
- CVE-2012-2451 漏洞CVE-<>-<>
- CVE-2015-1838 漏洞CVE-<>-<>
每当此规则检测到指向可公开写入目录的硬编码路径时,都会引发问题,例如(请参阅下面的示例)。它 还检测对指向可公开写入目录的环境变量的访问,例如 和 ./tmp
TMP
TMPDIR
/tmp
/var/tmp
/usr/tmp
/dev/shm
/dev/mqueue
/run/lock
/var/run/lock
/Library/Caches
/Users/Shared
/private/tmp
/private/var/tmp
\Windows\Temp
\Temp
\TMP
问问自己是否
- 从可公开写入的文件夹中读取文件或将文件写入该文件夹
- 应用程序将具有可预测名称的文件创建到可公开写入的文件夹中
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例
new File("/tmp/myfile.txt"); // Sensitive
Paths.get("/tmp/myfile.txt"); // Sensitive
java.io.File.createTempFile("prefix", "suffix"); // Sensitive, will be in the default temporary-file directory.
java.nio.file.Files.createTempDirectory("prefix"); // Sensitive, will be in the default temporary-file directory.
Map<String, String> env = System.getenv();
env.get("TMP"); // Sensitive
推荐的安全编码实践
- 使用具有严格控制权限的专用子文件夹
- 使用设计安全的 API 创建临时文件。此类 API 将确保:
- 生成的文件名是不可预测的
- 该文件只能由创建用户 ID 读取和写入
- 文件描述符不由子进程继承
- 文件将在关闭后立即销毁
合规解决方案
new File("/myDirectory/myfile.txt");
File.createTempFile("prefix", "suffix", new File("/mySecureDirectory"));
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("w+"));
Files.createTempFile("prefix", "suffix", attr); // ***pliant, created with explicit attributes.
Make sure that expanding this archive file is safe here.
当应用程序在不控制扩展数据大小的情况下扩展不受信任的存档文件时,就会发生成功的 Zip Bomb 攻击,这可能会 导致拒绝服务。Zip 炸弹通常是几千字节压缩数据的恶意存档文件,但变成了千兆字节的 未压缩的数据。为了达到这种极端的压缩率,攻击者将 压缩不相关的数据(例如:一长串重复字节)。
问问自己是否
要扩展的存档不受信任,并且:
- 没有对存档中的条目数进行验证。
- 没有对未压缩数据的总大小进行验证。
- 没有验证压缩和未压缩存档条目之间的比率。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例
File f = new File("ZipBomb.zip");
ZipFile zipFile = new ZipFile(f);
Enumeration<? extends ZipEntry> entries = zipFile.entries(); // Sensitive
while(entries.hasMoreElements()) {
ZipEntry ze = entries.nextElement();
File out = new File("./output_onlyfortesting.txt");
Files.copy(zipFile.getInputStream(ze), out.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
推荐的安全编码实践
- 定义和控制压缩数据与未压缩数据之间的比例,一般来说,大多数合法档案的数据压缩比例是 1 到 3。
- 定义和控制未压缩数据的最大总大小的阈值。
- 计算从存档中提取的文件条目数,如果其数量大于预定义的阈值,则中止提取,在 特别是,不建议以递归方式扩展存档(存档的条目也可以是存档)。
合规解决方案
不要依赖 getsize 来检索 未压缩的条目,因为此方法返回存档标头中定义的内容,攻击者可以伪造这些内容,而不是计算实际 解压时的条目尺寸