ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java代码审计手册(1)

2020-12-23 16:00:13  阅读:256  来源: 互联网

标签:审计 XML Java 实体 错误模式 手册 SHA XXE org


此文为转载翻译文章(可能会出现错误)

目录

可预测的伪随机数生成器(PREDICTABLE_RANDOM)
可预测的伪随机数生成器(Scala)(PREDICTABLE_RANDOM_SCALA)
不受信任的servlet参数(SERVLET_PARAMETER)
不受信任的Content-Type标头(SERVLET_CONTENT_TYPE)
不受信任的主机名标头(SERVLET_SERVER_NAME)
不受信任的会话Cookie值(SERVLET_SESSION_ID)
不受信任的查询字符串(SERVLET_QUERY_STRING)
HTTP标头不受信任(SERVLET_HEADER)
不受信任的引荐标头(SERVLET_HEADER_REFERER)
不受信任的用户代理标头(SERVLET_HEADER_USER_AGENT)
Cookie中的潜在敏感数据(COOKIE_USAGE)
潜在的路径遍历(读取文件)(PATH_TRAVERSAL_IN)
潜在的路径遍历(文件写入)(PATH_TRAVERSAL_OUT)
使用Scala API的潜在路径遍历(读取文件)(SCALA_PATH_TRAVERSAL_IN)
潜在的命令注入(COMMAND_INJECTION)
潜在命令注入(Scala)(SCALA_COMMAND_INJECTION)
FilenameUtils不过滤空字节(WEAK_FILENAMEUTILS)
接受任何证书的TrustManager(WEAK_TRUST_MANAGER)
接受任何签名证书的主机名验证程序(WEAK_HOSTNAME_VERIFIER)
找到了JAX-WS SOAP端点(JAXWS_ENDPOINT)
找到了JAX-RS REST端点(JAXRS_ENDPOINT)
找到挂毯页面(TAPESTRY_ENDPOINT)
找到了Wicket网页(WICKET_ENDPOINT)
MD2,MD4和MD5是弱哈希函数(WEAK_MESSAGE_DIGEST_MD5)
SHA-1是弱散列函数(WEAK_MESSAGE_DIGEST_SHA1)
具有默认构造函数的DefaultHttpClient与TLS 1.2(DEFAULT_HTTP_CLIENT)不兼容
弱SSLContext(SSL_CONTEXT)
消息摘要是自定义的(CUSTOM_MESSAGE_DIGEST)
读取的文件名被污染(FILE_UPLOAD_FILENAME)
正则表达式DOS(ReDOS)(重做)
XML解析容易受到XXE(XMLStreamReader)(XXE_XMLSTREAMREADER)的攻击
XML解析容易受到XXE(XPathExpression)(XXE_XPATH)的攻击
XML解析容易受到XXE(SAXParser)(XXE_SAXPARSER)的攻击
XML解析容易受到XXE(XMLReader)(XXE_XMLREADER)的攻击
XML分析容易受到XXE(DocumentBuilder)(XXE_DOCUMENT)的攻击
XML解析容易受到XXE(TransformerFactory)(XXE_DTD_TRANSFORM_FACTORY)的攻击
XSLT解析容易受到XXE(TransformerFactory)(XXE_XSLT_TRANSFORM_FACTORY)的攻击

可预测的伪随机数生成器

错误模式:PREDICTABLE_RANDOM

当在某些安全关键的上下文中使用可预测的随机值时,可能会导致漏洞。例如,当该值用作:

CSRF令牌:可预测的令牌可能导致CSRF攻击,因为攻击者将知道令牌的值
密码重置令牌(通过电子邮件发送):可预测的密码令牌可能会导致帐户被接管,因为攻击者会猜测“更改密码”表单的URL
任何其他秘密值
一种快速的解决方法是java.util.Random 用诸如的更强的功能 代替对的使用 java.security.SecureRandom;

脆弱代码:

String generateSecretToken() {
    Random r = new Random();
    return Long.toHexString(r.nextLong());
}

解决方案:

import org.apache.commons.codec.binary.Hex;

String generateSecretToken() {
    SecureRandom secRandom = new SecureRandom();

    byte[] result = new byte[32];
    secRandom.nextBytes(result);
    return Hex.encodeHexString(result);
}

破解随机数生成器的参考
CERT:MSC02-J。生成强随机数
CWE-330:使用不足的随机值来
预测Struts CSRF令牌(现实中的漏洞和利用示例)

可预测的伪随机数生成器(Scala)

错误模式:PREDICTABLE_RANDOM_SCALA

当在某些安全关键的上下文中使用可预测的随机值时,可能会导致漏洞。例如,当该值用作:

CSRF令牌:可预测的令牌可能导致CSRF攻击,因为攻击者将知道令牌的值
密码重置令牌(通过电子邮件发送):可预测的密码令牌可能会导致帐户被接管,因为攻击者会猜测“更改密码”表单的URL
任何其他秘密值
一个快速的解决方法是java.util.Random 用更强大的功能(例如 java.security.SecureRandom)代替对的使用 ;

脆弱代码:

import scala.util.Random

def generateSecretToken() {
    val result = Seq.fill(16)(Random.nextInt)
    return result.map("%02x" format _).mkString
}

解决方案:

import java.security.SecureRandom

def generateSecretToken() {
    val rand = new SecureRandom()
    val value = Array.ofDim[Byte](16)
    rand.nextBytes(value)
    return value.map("%02x" format _).mkString
}

破解随机数生成器的参考
CERT:MSC02-J。生成强随机数
CWE-330:使用不足的随机值来
预测Struts CSRF令牌(现实中的漏洞和利用示例)

不受信任的servlet参数

错误模式:SERVLET_PARAMETER

Servlet可以从各种方法读取GET和POST参数。获得的值应该被认为是不安全的。您可能需要先验证或清除这些值,然后再将其传递给敏感的API,例如:

SQL查询(可能导致SQL注入)
文件打开(可能导致路径遍历)
命令执行(潜在的命令注入)
HTML构造(潜在的XSS)
等等…

参考
CWE-20:输入验证不正确

不受信任的Content-Type标头

错误模式:SERVLET_CONTENT_TYPE

HTTP标头Content-Type可以由客户端控制。因此,其值不应在任何对安全性至关重要的决策中使用。

参考
CWE-807:安全决策中的不受信任的输入

不受信任的主机名标头

错误模式:SERVLET_SERVER_NAME

主机名头可以由客户端控制。因此,其值不应在任何对安全性至关重要的决策中使用。两者 ServletRequest.getServerName() 和 HttpServletRequest.getHeader(“Host”) 具有相同的行为,即提取 Host 标头。

GET /testpage HTTP/1.1
Host: www.example.com
[...]

默认情况下,为您的应用程序提供服务的Web容器可以将请求重定向到您的应用程序。这将允许恶意用户在Host标头中放置任何值。建议您在针对请求做出的任何安全决策中都不信任此值。

参考
CWE-807:安全决策中的不受信任的输入

不受信任的会话Cookie值

错误模式:SERVLET_SESSION_ID

该方法 HttpServletRequest.getRequestedSessionId() 通常返回cookie的值 JSESSIONID。此值通常仅由会话管理逻辑访问,而不能由常规开发人员代码访问。

传递给客户端的值通常是字母数字值(例如 JSESSIONID=jp6q31lq2myn)。但是,客户端可以更改该值。以下HTTP请求说明了可能的修改。

GET /somePage HTTP/1.1
Host: yourwebsite.com
User-Agent: Mozilla/5.0
Cookie: JSESSIONID=Any value of the user's choice!!??'''">

因此,JSESSIONID仅应用于查看其值是否与现有会话ID相匹配。如果不是,则应将用户视为未经身份验证的用户。此外,会话ID值永远不应记录。如果是,则日志文件可能包含有效的活动会话ID,从而使内部人员可以劫持ID已被记录且仍处于活动状态的所有会话。

参考
OWASP:会话管理速查表
CWE-20:输入验证不正确

不受信任的查询字符串

错误模式:SERVLET_QUERY_STRING

查询字符串是GET参数名称和值的串联。可以传入非预期参数。

对于URL请求 /app/servlet.htm?a=1&b=2,查询字符串提取将为 a=1&b=2

就像通过方法检索的单个参数值一样HttpServletRequest.getParameter(),HttpServletRequest.getQueryString() 应该认为从中获取 的值 是不安全的。您可能需要验证或清除从查询字符串中提取的任何内容,然后再将其传递给敏感的API。

参考
CWE-20:输入验证不正确

HTTP标头不受信任

错误模式:SERVLET_HEADER

请求标头可以由发出请求的用户轻松更改。通常,不应假定该请求来自常规浏览器而未经攻击者修改。因此,建议您在针对请求做出的任何安全决策中都不信任该值。

参考
CWE-807:安全决策中的不受信任的输入

不受信任的引荐标头

错误模式:SERVLET_HEADER_REFERER

行为:
如果请求来自恶意用户,则可以将任何值分配给此标头。
如果请求是从另一个安全的来源(HTTPS)发起的,则“ Referer”将不存在。

建议:
任何访问控制都不应基于此标头的值。
任何CSRF保护都不应仅基于此值(因为它是可选的)。

参考
CWE-807:安全决策中的不受信任的输入

不受信任的用户代理标头

错误模式:SERVLET_HEADER_USER_AGENT

标题“ User-Agent”很容易被客户端欺骗。不建议基于用户代理(对于搜寻器UA)采用不同的行为。

参考
CWE-807:安全决策中的不受信任的输入

Cookie中的潜在敏感数据

错误模式:COOKIE_USAGE

自定义cookie中存储的信息不应与会话敏感或相关。在大多数情况下,敏感数据应仅存储在会话中,并由用户的会话cookie引用。参见HttpSession(HttpServletRequest.getSession())

自定义cookie可以用于需要生存时间超过特定会话且与特定会话无关的信息。

参考
CWE-315:Cookie中敏感信息的明文存储

潜在的路径遍历(读取文件)

错误模式:PATH_TRAVERSAL_IN

打开文件以读取其内容。文件名来自 输入 参数。如果将未经过滤的参数传递给此文件API,则可以从任意文件系统位置读取文件。

该规则标识 潜在的 路径遍历漏洞。在许多情况下,构造的文件路径不能由用户控制。如果是这种情况,则报告的实例为假阳性。

脆弱代码:

@GET
@Path("/images/{image}")
@Produces("images/*")
public Response getImage(@javax.ws.rs.PathParam("image") String image) {
    File file = new File("resources/images/", image); //Weak point

    if (!file.exists()) {
        return Response.status(Status.NOT_FOUND).build();
    }

    return Response.ok().entity(new FileInputStream(file)).build();
}

解决方案:

import org.apache.commons.io.FilenameUtils;

@GET
@Path("/images/{image}")
@Produces("images/*")
public Response getImage(@javax.ws.rs.PathParam("image") String image) {
    File file = new File("resources/images/", FilenameUtils.getName(image)); //Fix

    if (!file.exists()) {
        return Response.status(Status.NOT_FOUND).build();
    }

    return Response.ok().entity(new FileInputStream(file)).build();
}

参考
WASC:路径遍历
OWASP:路径遍历
CAPEC-126:路径遍历
CWE-22:路径名到受限目录的不正确限制(“路径遍历”)

潜在的路径遍历(文件写入)

错误模式:PATH_TRAVERSAL_OUT

打开文件以写入其内容。文件名来自 输入 参数。如果将未经过滤的参数传递给此文件API,则可以修改任意文件系统位置的文件。

该规则标识 潜在的 路径遍历漏洞。在许多情况下,构造的文件路径不能由用户控制。如果是这种情况,则报告的实例为假阳性。

参考
WASC-33:路径遍历
OWASP:路径遍历
CAPEC-126:路径遍历
CWE-22:路径名对受限目录的不正确限制(“路径遍历”)

潜在Scala API的路径遍历(读取文件)

错误模式:SCALA_PATH_TRAVERSAL_IN

打开文件以读取其内容。文件名来自 输入 参数。如果将未经过滤的参数传递给此文件API,则可以从任意文件系统位置读取文件。

该规则标识 潜在的 路径遍历漏洞。在许多情况下,构造的文件路径不能由用户控制。如果是这种情况,则报告的实例为假阳性。

脆弱代码:

def getWordList(value:String) = Action {
  if (!Files.exists(Paths.get("public/lists/" + value))) {
    NotFound("File not found")
  } else {
    val result = Source.fromFile("public/lists/" + value).getLines().mkString // Weak point
    Ok(result)
  }
}

解决方案:

import org.apache.commons.io.FilenameUtils;

def getWordList(value:String) = Action {
  val filename = "public/lists/" + FilenameUtils.getName(value)

  if (!Files.exists(Paths.get(filename))) {
    NotFound("File not found")
  } else {
    val result = Source.fromFile(filename).getLines().mkString // Fix
    Ok(result)
  }
}

参考
WASC:路径遍历
OWASP:路径遍历
CAPEC-126:路径遍历
CWE-22:路径名到受限目录的不正确限制(“路径遍历”)

潜在的命令注入

错误模式:COMMAND_INJECTION

高亮显示的API用于执行系统命令。如果未过滤的输入传递到此API,则可能导致任意命令执行。

脆弱代码:

import java.lang.Runtime;

Runtime r = Runtime.getRuntime();
r.exec("/bin/sh -c some_tool" + input);

参考资料
OWASP:命令注入
OWASP:十大2013-A1-注入
CWE-78:OS命令中使用的特殊元素的不适当中和(“ OS命令注入”)

潜在命令注入(标量)

错误模式:SCALA_COMMAND_INJECTION

高亮显示的API用于执行系统命令。如果未过滤的输入传递到此API,则可能导致任意命令执行。

脆弱代码:

def executeCommand(value:String) = Action {
    val result = value.!
    Ok("Result:\n"+result)
}

参考资料
OWASP:命令注入
OWASP:十大2013-A1-注入
CWE-78:OS命令中使用的特殊元素的不适当中和(“ OS命令注入”)

FilenameUtils不过滤空字节

错误模式:WEAK_FILENAMEUTILS

有些 FilenameUtils’ 方法不过滤NULL字节(0x00)。

如果将空字节注入文件名中,如果将此文件名传递给基础操作系统,则检索到的文件将是在NULL字节之前指定的文件名,因为在操作系统级别,所有字符串均以终止一个空字节,即使Java本身并不关心空字节或不对它们进行特殊处理。此OS行为可用于绕过文件名验证,该验证将查看文件名的末尾(例如,以结尾 “.log”),以确保它是可安全访问的文件。

要解决此问题,建议两件事:
升级到Java 7 update 40或更高版本,或者升级到Java 8+,因为在这些版本中,文件名中的NULL字节注入是固定的。
强烈验证不受信任的用户提供的任何文件名,以确保它们是有效的(即,不包含null,不包含路径字符等)。
如果您知道您正在使用不受NULL字节注入影响的现代Java版本,则可以禁用此规则。

参考文献
WASC-28:空字节注入
CWE-158:空字节或NUL字符的不正确中和

接受任何证书的TrustManager

错误模式:WEAK_TRUST_MANAGER

空的TrustManager实现通常用于轻松连接到未经根证书颁发机构签名的主机。因此,这很容易受到中间人攻击,因为客户端将信任任何证书。

应该构建允许特定证书(例如基于TrustStore)的TrustManager。

脆弱代码:

class TrustAllManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        //Trust any client connecting (no certificate validation)
    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        //Trust any remote server (no certificate validation)
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

解决方案(基于密钥库的TrustMangager):

KeyStore ks = //Load keystore containing the certificates trusted

SSLContext sc = SSLContext.getInstance("TLS");

TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);

sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);

参考
WASC-04:传输层保护不足
CWE-295:证书验证不正确

接受任何签名证书的HostnameVerifier

错误模式:WEAK_HOSTNAME_VERIFIER

HostnameVerifier 由于许多主机上都重复使用了证书,因此经常使用接受任何主机的A。 因此,这很容易受到中间人攻击,因为客户端将信任任何证书。

应该构建允许特定证书(例如基于信任库)的TrustManager。应创建通配符证书,以在多个子域上重用。

脆弱代码:

public class AllHosts implements HostnameVerifier {
    public boolean verify(final String hostname, final SSLSession session) {
        return true;
    }
}

解决方案(基于密钥库的TrustMangager):

KeyStore ks = //Load keystore containing the certificates trusted

SSLContext sc = SSLContext.getInstance("TLS");

TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);

sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);

参考文献
WASC-04:传输层保护不足
CWE-295:证书验证不正确

找到了JAX-WS SOAP端点

错误模式:JAXWS_ENDPOINT

此方法是SOAP Web服务(JSR224)的一部分。

应该分析此Web服务的安全性。例如:

如果强制执行身份验证,则应进行测试。
访问控制(如果强制执行)应进行测试。
应该跟踪输入的潜在漏洞。
理想情况下,通信应通过SSL。

参考
OWASP:Web服务安全备忘单
CWE-20:输入验证不正确

找到JAX-RS REST端点

错误模式:JAXRS_ENDPOINT

此方法是REST Web服务(JSR311)的一部分。

应该分析此Web服务的安全性。例如:

如果强制执行身份验证,则应进行测试。
访问控制(如果强制执行)应进行测试。
应该跟踪输入的潜在漏洞。
理想情况下,通信应通过SSL。
如果服务支持写操作(例如,通过POST),则应调查其对CSRF的漏洞。

参考
OWASP:REST评估速查表
OWASP:REST安全速查表
OWASP:Web服务安全速查表
OWASP:跨站点请求伪造
OWASP:CSRF预防速查表
CWE-20:输入验证不正确

找到挂毯页面

错误模式:TAPESTRY_ENDPOINT

在应用程序启动时发现了Tapestry端点。Tapestry应用程序由支持Java类和.tml 每个页面对应的Tapestry标记语言页面( 文件)构成。收到请求后,GET / POST参数将映射到后备Java类中的特定输入。映射是通过字段名称完成的:

   [...]
    protected String input;
   [...]

或显式注释的定义:

    [...]
    @org.apache.tapestry5.annotations.Parameter
    protected String parameter1;

    @org.apache.tapestry5.annotations.Component(id = "password")
    private PasswordField passwordField;
    [...]

页面映射到视图 /resources/package/PageName.tml。

应该研究此应用程序中的每个Tapestry页面,以确保以这种方式自动映射的所有输入在使用之前都经过正确验证。

参考
Apache Tapestry主页
CWE-20:输入验证不正确

找到Wicket网页

错误模式:WICKET_ENDPOINT

此类表示Wicket网页。输入是从传递给构造函数的PageParameters实例自动读取的。当前页面映射到视图 /package/WebPageName.html。

应该研究此应用程序中的每个Wicket页面,以确保以这种方式自动映射的所有输入在使用之前都经过正确验证。

参考
Apache Wicket主页
CWE-20:输入验证不正确

MD2,MD4和MD5是弱哈希函数

错误模式:WEAK_MESSAGE_DIGEST_MD5

不建议使用算法MD2,MD4和MD5。 例如,PBKDF2 应该用于哈希密码。

“ MD5哈希功能的安全性受到严重损害。存在冲突攻击,该攻击可在配备2.6 GHz Pentium 4处理器(复杂度为224.1)的计算机上在几秒钟内找到冲突。[1]此外,还存在一个选择的前缀。使用现成的计算硬件(复杂性239),可以在几个小时内对具有指定前缀的两个输入产生冲突。[2]“
-Wikipedia:MD5-安全
“ SHA-224,SHA-256,SHA-384,SHA-512,SHA-512 / 224和SHA-512 / 256:
使用这些哈希函数对于所有哈希函数应用程序都是可接受的。”
-NIST:过渡:过渡使用密码算法和密钥长度的建议p.15
“ PBKDF的主要思想是通过增加测试每个密码所需的时间来减缓对密码的字典或蛮力攻击。拥有可能密码列表的攻击者可以使用已知的迭代计数器和盐评估PBKDF。攻击者每次尝试都必须花费大量的计算时间,因此应用字典或蛮力攻击变得更加困难。”
-NIST:关于基于密码的密钥派生的建议p.12

脆弱代码:

MessageDigest md5Digest = MessageDigest.getInstance("MD5");
    md5Digest.update(password.getBytes());
    byte[] hashValue = md5Digest.digest();
 
byte[] hashValue = DigestUtils.getMd5Digest().digest(password.getBytes());
 

解决方案(使用充气城堡):

public static byte[] getEncryptedPassword(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
    PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
    gen.init(password.getBytes("UTF-8"), salt.getBytes(), 4096);
    return ((KeyParameter) gen.generateDerivedParameters(256)).getKey();
}

解决方案(Java 8和更高版本):

public static byte[] getEncryptedPassword(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 4096, 256 * 8);
    SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    return f.generateSecret(spec).getEncoded();
}

参考文献
[1]关于MD5的碰撞:MMJ Stevens
撰写的硕士论文[2] MD5及其应用的选择前缀冲突:Marc Stevens撰写的论文
Wikipedia:MD5
NIST:过渡:过渡使用密码算法和密钥长度的建议
NIST :基于密码的密钥派生
Stackoverflow的建议:Java
CWE-327的PBKDF2-HMAC-SHA256的可靠实现:使用损坏的或有风险的密码算法

SHA-1是弱哈希函数

错误模式:WEAK_MESSAGE_DIGEST_SHA1

对于散列密码,签名验证和其他用途,不推荐使用算法SHA-1。 例如,PBKDF2 应该用于哈希密码。

“ SHA-1用于数字签名生成:
SHA-1可仅被用于数字签名生成,其中特别是通过特定于协议的NIST指导允许对于所有其他应用程序,。  SHA-1不得用于数字签名生成。
SHA-1用于数字签名的验证:
对于数字签名的验证,  允许SHA-1用于旧版使用。
[...]
SHA-224,SHA-256,SHA-384,SHA-512,SHA-512 / 224和SHA- 512/256:
所有哈希函数应用程序都可以使用这些哈希函数。”
-NIST:过渡:过渡使用密码算法和密钥长度的建议第15页
“ PBKDF的主要思想是通过增加测试每个密码所需的时间来减缓对密码的字典或蛮力攻击。拥有可能密码列表的攻击者可以使用已知的迭代计数器和盐评估PBKDF。攻击者每次尝试都必须花费大量的计算时间,因此应用字典或蛮力攻击变得更加困难。”
-NIST:关于基于密码的密钥派生的建议p.12

脆弱代码:

MessageDigest sha1Digest = MessageDigest.getInstance("SHA1");
    sha1Digest.update(password.getBytes());
    byte[] hashValue = sha1Digest.digest();
 
byte[] hashValue = DigestUtils.getSha1Digest().digest(password.getBytes());

解决方案(使用充气城堡):

public static byte[] getEncryptedPassword(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
    PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
    gen.init(password.getBytes("UTF-8"), salt.getBytes(), 4096);
    return ((KeyParameter) gen.generateDerivedParameters(256)).getKey();
}

解决方案(Java 8和更高版本):

public static byte[] getEncryptedPassword(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 4096, 256 * 8);
    SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    return f.generateSecret(spec).getEncoded();
}

参考资料
Qualys博客:SHA1弃用:您需要了解的知识
Google Online Security博客:SHA-1
NIST逐渐淘汰:过渡:过渡使用密码算法和密钥长度的
建议NIST:关于基于密码的密钥派生的建议
Stackoverflow:可靠的实现
CWE-327的PBKDF2-HMAC-SHA256的说明:破损或有风险的加密算法的使用

具有默认构造函数的DefaultHttpClient与TLS 1.2不兼容

错误模式:DEFAULT_HTTP_CLIENT

脆弱代码:

HttpClient client = new DefaultHttpClient();

解决方案:
升级您的实现以使用推荐的结构之一,并将https.protocolsJVM选项配置为包括TLSv1.2:

使用SystemDefaultHttpClient代替
样例代码:

HttpClient client = new SystemDefaultHttpClient();

创建基于SSLSocketFactory的HttpClient-使用SSLScoketFactory实例,getSystemSocketFactory()并将其用于HttpClient创建;
基于SSLConnectionSocketFactory创建一个HttpClient-获取一个实例,getSystemSocketFactory()并使用该实例进行HttpClient创建;
使用HttpClientBuilder-useSystemProperties()在调用之前先调用build()
样例代码:

HttpClient client = HttpClientBuilder.create().useSystemProperties().build();

HttpClients-调用createSystem()以创建实例
样例代码:

HttpClient client = HttpClients.createSystem();

参考
诊断TLS,SSL和HTTPS

SSLContext弱

错误模式:SSL_CONTEXT

脆弱代码:

SSLContext.getInstance("SSL");

将您的实现升级到以下内容,并将https.protocols JVM选项配置 为包括TLSv1.2:
解决方案:

SSLContext.getInstance("TLS");

参考
诊断TLS,SSL和HTTPS

消息摘要是自定义的

错误模式:CUSTOM_MESSAGE_DIGEST

实现自定义MessageDigest很容易出错。

NIST建议使用SHA-224,SHA-256,SHA-384,SHA-512,SHA-512 / 224或SHA-512 / 256。

“ SHA-1用于数字签名生成:
SHA-1可仅被用于数字签名生成,其中特别是通过特定于协议的NIST指导允许对于所有其他应用程序,。  SHA-1不得用于数字签名生成。
SHA-1用于数字签名的验证:
对于数字签名的验证,  允许SHA-1用于旧版使用。
[...]
SHA-224,SHA-256,SHA-384,SHA-512,SHA-512 / 224和SHA- 512/256:
所有哈希函数应用程序都可以使用这些哈希函数。”
-NIST:过渡:过渡使用密码算法和密钥长度的建议

脆弱代码:

MyProprietaryMessageDigest extends MessageDigest {
    @Override
    protected byte[] engineDigest() {
        [...]
        //Creativity is a bad idea
        return [...];
    }
}

升级您的实施以使用批准的算法之一。使用足以满足您特定安全需求的算法。
解决方案:

MessageDigest sha256Digest = MessageDigest.getInstance("SHA256");
sha256Digest.update(password.getBytes());

参考
NIST批准的哈希算法
CWE-327:使用破损或有风险的密码算法

读取文件名受污染

错误模式:FILE_UPLOAD_FILENAME

客户端可以篡改FileUpload API提供的文件名,以引用未经授权的文件。
例如:

"../../../config/overide_file"
"shell.jsp\u0000expected.gif"

因此,此类值不应直接传递到文件系统API。如果可以接受,则应用程序应生成自己的文件名并使用它们。否则,应正确验证提供的文件名,以确保其结构正确,不包含未经授权的路径字符(例如/ \)并引用授权文件。

参考资料
Securiteam:文件上传安全建议
CWE-22:对受限目录的路径名不正确限制(“路径遍历”)
WASC-33:路径遍历
OWASP:路径遍历
CAPEC-126:路径遍历
CWE-22:对路径名的不正确限制受限目录的路径名(“路径遍历”)

正则表达式DOS(ReDOS)

错误模式:REDOS

正则表达式(Regex)经常遭受拒绝服务(DOS)攻击(称为ReDOS)。这是由于以下事实:根据特定的正则表达式定义,当分析某些字符串时,正则表达式引擎可能会花费大量时间。

例如,对于正则表达式: ^(a+)+$,输入“ aaaaaaaaaaaaaaaaX”将使正则表达式引擎分析65536个不同的路径。[1] 摘自OWASP参考的示例

因此,单个请求有可能在服务器端引起大量计算。与此正则表达式,以及其他类似的问题是,有两种不同的方式相同的输入字符可以由正则表达式所接受由于 + (或 *)在括号内,并且 + (或者 *)在括号外。编写方式 + 可能会占用字符“ a”。要解决此问题,应重写正则表达式以消除歧义。例如,可以将其简单地重写为: ^a+$,无论如何,这大概是作者的意思(任意多个a)。假设这就是原始正则表达式的含义,则可以快速评估此新正则表达式,并且不受ReDOS的约束。

参考
Sebastian Kubeck的Weblog:检测和防止ReDoS漏洞
[1] OWASP:正则表达式拒绝服务
CWE-400:资源消耗不受控制(“资源耗尽”)

XML分析容易受到XXE(XMLStreamReader)的攻击

错误模式:XXE_XMLSTREAMREADER

攻击
当XML解析器在处理从不受信任的来源接收到的XML时支持XML实体时,可能会发生XML外部实体(XXE)攻击。

风险1:公开本地文件内容(XXE:XML外部实体)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
   <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<foo>&xxe;</foo>

风险2:拒绝服务(XEE:XML实体扩展)

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
[...]
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

为了避免暴露XML解析器的危险功能,可以对代码进行以下更改。
脆弱代码:

public void parseXML(InputStream input) throws XMLStreamException {

    XMLInputFactory factory = XMLInputFactory.newFactory();
    XMLStreamReader reader = factory.createXMLStreamReader(input);
    [...]
}

以下代码片段显示了两种可用的解决方案。您可以设置一个或两个属性。
禁用外部实体的解决方案:

public void parseXML(InputStream input) throws XMLStreamException {

    XMLInputFactory factory = XMLInputFactory.newFactory();
    factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
    XMLStreamReader reader = factory.createXMLStreamReader(input);
    [...]
}

解决方案禁用DTD:

public void parseXML(InputStream input) throws XMLStreamException {

    XMLInputFactory factory = XMLInputFactory.newFactory();
    factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
    XMLStreamReader reader = factory.createXMLStreamReader(input);
    [...]
}

参考文献
CWE-611:对XML外部实体参考(‘XXE’)的不当限制
CERT:IDS10-J。防止XML外部实体攻击
OWASP.org:XML外部实体(XXE)处理
WS-Attacks.org:XML实体扩展
WS-Attacks.org:XML外部实体DOS
WS-Attacks.org:XML实体参考攻击
识别XML外部实体漏洞(XXE)
JEP 185:限制获取外部XML资源

XML解析容易受到XXE(XPathExpression)的攻击

错误模式:XXE_XPATH

攻击
当XML解析器在处理从不受信任的来源接收到的XML时支持XML实体时,可能会发生XML外部实体(XXE)攻击。

风险1:公开本地文件内容(XXE:XML外部实体)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
   <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<foo>&xxe;</foo>

风险2:拒绝服务(XEE:XML实体扩展)

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
[...]
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

为了避免暴露XML解析器的危险功能,可以对代码进行以下更改。
脆弱代码:

DocumentBuilder builder = df.newDocumentBuilder();

XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
XPathExpression xPathExpr = xpath.compile("/somepath/text()");

xPathExpr.evaluate(new InputSource(inputStream));

以下代码片段显示了两种可用的解决方案。您可以设置一个功能或同时设置两个功能。

使用“安全处理”模式的解决方案:
此设置将保护您免受拒绝服务攻击和远程文件访问。

DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder builder = df.newDocumentBuilder();

[...]

xPathExpr.evaluate( builder.parse(inputStream) );

解决方案禁用DTD:
通过禁用DTD,几乎所有XXE攻击都将得到阻止。

DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder builder = df.newDocumentBuilder();

[...]

xPathExpr.evaluate( builder.parse(inputStream) );

参考文献
CWE-611:对XML外部实体参考(‘XXE’)的不当限制
CERT:IDS10-J。防止XML外部实体攻击
OWASP.org:XML外部实体(XXE)处理
WS-Attacks.org:XML实体扩展
WS-Attacks.org:XML外部实体DOS
WS-Attacks.org:XML实体参考攻击
识别XML外部实体漏洞(XXE)
XML外部实体(XXE)预防备忘单

XML分析容易受到XXE(SAXParser)的攻击

错误模式:XXE_SAXPARSER

攻击
当XML解析器在处理从不受信任的来源接收到的XML时支持XML实体时,可能会发生XML外部实体(XXE)攻击。

风险1:公开本地文件内容(XXE:XML外部实体)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
   <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<foo>&xxe;</foo>

风险2:拒绝服务(XEE:XML实体扩展)

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
[...]
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

为了避免暴露XML解析器的危险功能,可以对代码进行以下更改。

脆弱代码:

SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

parser.parse(inputStream, customHandler);

以下代码片段显示了两种可用的解决方案。您可以设置一个功能或同时设置两个功能。

使用“安全处理”模式的解决方案:
此设置将保护您免受拒绝服务攻击和远程文件访问。

SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
SAXParser parser = spf.newSAXParser();

parser.parse(inputStream, customHandler);

解决方案禁用DTD:
通过禁用DTD,几乎所有XXE攻击都将得到阻止。

SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
SAXParser parser = spf.newSAXParser();

parser.parse(inputStream, customHandler);

参考文献
CWE-611:对XML外部实体参考(‘XXE’)的不当限制
CERT:IDS10-J。防止XML外部实体攻击
OWASP.org:XML外部实体(XXE)处理
WS-Attacks.org:XML实体扩展
WS-Attacks.org:XML外部实体DOS
WS-Attacks.org:XML实体参考攻击
识别XML外部实体漏洞(XXE)
Xerces完整功能列表

XML分析容易受到XXE(XMLReader)的攻击

错误模式:XXE_XMLREADER

攻击
当XML解析器在处理从不受信任的来源接收到的XML时支持XML实体时,可能会发生XML外部实体(XXE)攻击。

风险1:公开本地文件内容(XXE:XML外部实体)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
   <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<foo>&xxe;</foo>

风险2:拒绝服务(XEE:XML实体扩展)

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
[...]
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

为了避免暴露XML解析器的危险功能,可以对代码进行以下更改。

脆弱代码:

XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(customHandler);
reader.parse(new InputSource(inputStream));

以下代码片段显示了两种可用的解决方案。您可以设置一个或两个属性。

使用“安全处理”模式的解决方案:
此设置将保护您免受拒绝服务攻击和远程文件访问。

XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
reader.setContentHandler(customHandler);

reader.parse(new InputSource(inputStream));

解决方案禁用DTD:
通过禁用DTD,几乎所有XXE攻击都将得到阻止。

XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setContentHandler(customHandler);

reader.parse(new InputSource(inputStream));

参考文献
CWE-611:对XML外部实体参考(‘XXE’)的不当限制
CERT:IDS10-J。防止XML外部实体攻击
OWASP.org:XML外部实体(XXE)处理
WS-Attacks.org:XML实体扩展
WS-Attacks.org:XML外部实体DOS
WS-Attacks.org:XML实体参考攻击
识别XML外部实体漏洞(XXE)
Xerces完整功能列表

XML分析容易受到XXE(DocumentBuilder)的攻击

错误模式:XXE_DOCUMENT

攻击
当XML解析器在处理从不受信任的来源接收到的XML时支持XML实体时,可能会发生XML外部实体(XXE)攻击。

风险1:公开本地文件内容(XXE:XML外部实体)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
   <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<foo>&xxe;</foo>

风险2:拒绝服务(XEE:XML实体扩展)

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
[...]
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

为了避免暴露XML解析器的危险功能,可以对代码进行以下更改。

脆弱代码:

DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();

Document doc = db.parse(input);

以下代码片段显示了两种可用的解决方案。您可以设置一个功能或同时设置两个功能。

使用“安全处理”模式的解决方案:
此设置将保护您免受拒绝服务攻击和远程文件访问。

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder db = dbf.newDocumentBuilder();

Document doc = db.parse(input);

解决方案禁用DTD:
通过禁用DTD,几乎所有XXE攻击都将得到阻止。

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder db = dbf.newDocumentBuilder();

Document doc = db.parse(input);

参考文献
CWE-611:对XML外部实体参考(‘XXE’)的不当限制
CERT:IDS10-J。防止XML外部实体攻击
OWASP.org:XML外部实体(XXE)处理
WS-Attacks.org:XML实体扩展
WS-Attacks.org:XML外部实体DOS
WS-Attacks.org:XML实体参考攻击
识别XML外部实体漏洞(XXE)
Xerces2完整功能列表

XML分析容易受到XXE(TransformerFactory)的攻击

错误模式:XXE_DTD_TRANSFORM_FACTORY

攻击
当XML解析器在处理从不受信任的来源接收到的XML时支持XML实体时,可能会发生XML外部实体(XXE)攻击。

风险1:公开本地文件内容(XXE:XML外部实体)

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
   <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
<foo>&xxe;</foo>

风险2:拒绝服务(XEE:XML实体扩展)

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
[...]
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

为了避免暴露XML解析器的危险功能,可以对代码进行以下更改。

脆弱代码:

Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(input, result);

以下代码片段显示了两种可用的解决方案。您可以设置一个功能或同时设置两个功能。

使用“安全处理”模式的解决方案:
此设置将防止远程文件访问,但不会拒绝服务。

TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "all");

Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");

transformer.transform(input, result);

解决方案禁用DTD:
此设置将防止远程文件访问,但不会拒绝服务。

TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");

transformer.transform(input, result);

参考文献
CWE-611:对XML外部实体参考(‘XXE’)的不当限制
CERT:IDS10-J。防止XML外部实体攻击
OWASP.org:XML外部实体(XXE)处理
WS-Attacks.org:XML实体扩展
WS-Attacks.org:XML外部实体DOS
WS-Attacks.org:XML实体参考攻击
识别XML外部实体漏洞(XXE)

XSLT解析容易受到XXE(TransformerFactory)的攻击

错误模式:XXE_XSLT_TRANSFORM_FACTORY

攻击
当XSLT解析器在处理从不受信任源收到的XSLT时支持外部实体时,可能会发生XSLT外部实体(XXE)攻击。

为了避免暴露XML解析器的危险功能,可以对代码进行以下更改。

脆弱代码:

Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(input, result);

以下代码片段显示了两种可用的解决方案。您可以设置一个功能或同时设置两个功能。

使用“安全处理”模式的解决方案:
此设置将防止远程文件访问,但不会拒绝服务。

TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "all");

Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");

transformer.transform(input, result);

解决方案禁用DTD:
此设置将防止远程文件访问,但不会拒绝服务。

TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");

transformer.transform(input, result);

参考文献
CWE-611:对XML外部实体参考(‘XXE’)的不当限制
CERT:IDS10-J。防止XML外部实体攻击
OWASP.org:XML外部实体(XXE)处理
WS-Attacks.org:XML实体扩展
WS-Attacks.org:XML外部实体DOS
WS-Attacks.org:XML实体参考攻击
识别XML外部实体漏洞(XXE)

标签:审计,XML,Java,实体,错误模式,手册,SHA,XXE,org
来源: https://blog.csdn.net/kudos123/article/details/111571945

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有