美文网首页
Find_Sec_Bugs检测项

Find_Sec_Bugs检测项

作者: nightmare丿 | 来源:发表于2017-06-08 12:21 被阅读0次

    1、不安全的随机数生成,在CSRF TOKEN生成、password reset token生成等,会造成token被猜测到import java.util.Random;import java.security.SecureRandom;import org.apache.commons.codec.binary.Hex;public class Predictable_Random {public static void main(String[] args){System.out.println(generateSecretToken());System.out.println(generateSecretToken_Safe());}//Vulnerable Code:static String generateSecretToken() {    Random r = new Random();    return Long.toHexString(r.nextLong());}//Solution:static String generateSecretToken_Safe() {    SecureRandom secRandom = new SecureRandom();    byte[] result = new byte[32];    secRandom.nextBytes(result);    return Hex.encodeHexString(result);}}2、Scala代码,同Java代码形式相同Vulnerable Code:import scala.util.Randomdef generateSecretToken() {    val result = Seq.fill(16)(Random.nextInt)    return result.map("%02x" format _).mkString}Solution:import java.security.SecureRandomdef generateSecretToken() {    val rand = new SecureRandom()    val value = Array.ofDim[Byte](16)    rand.nextBytes(value)    return value.map("%02x" format _).mkString}3、Untrusted servlet parameter从GET、POST中读取的参数是不可信任的,如果直接将这些参数用于代码中,将会造成:SQL注入、文件打开、命令执行、XSS...4、Untrusted Content-Type headerHTTP header头的Content-Type能够被客户端控制,因此不能用于关键的判断5、Untrusted Hostname headerHostname能够被客户端控制,因此不宜作为关键的判断;ServletRequest.getServerName()、HttpServletRequest.getHeader("Host")两个方法均是从header中获取Host6、Untrusted session cookie value通过方法HttpServletRequest.getRequestedSessionId()能够获取JSESSIONID,这个值应该只能被session管理器管理,不能被作为普通代码;此外这个值能够被客户端修改,因此不宜作为关键的判断依据;不能使用日志文件记录下sessionid的值,因为这个值可能是有效的id,容易造成会话劫持7、Untrusted query string使用GET、POST提交的参数是不可信任的,不能用于关键参数的判断;使用方法HttpServletRequest.getParameter()获取提交的参数名称,HttpServletRequest.getQueryString()获取参数的值8、HTTP headers untrusted9、Untrusted Referer header任何访问控制、CSRF保护均不能以referer作为判断依据10、Untrusted User-Agent header11、Potentially sensitive data in a cookieCookie中不应该包含任何敏感或与session存在相关性的信息,敏感信息应该存储在session中且与session Cookie存在关联;Cookie存在的时间要比单独session的时间长12、Potential Path Traversal (file read)使用可控的变量打开文件,如不对变量进行过滤,将会存在文件遍历风险Vulnerable Code:@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();}Solution: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();}13、Potential Path Traversal (file write)14、Potential Path Traversal (file read)Vulnerable Code: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)  }}Solution: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 // Weak point    Ok(result)  }}15、Potential Command Injection用于执行一个系统命令Vulnerable Code:import java.lang.Runtime;Runtime r = Runtime.getRuntime();r.exec("/bin/sh -c some_tool" + input);16、Potential Command Injection (Scala)Scala执行系统命令def executeCommand(value:String) = Action {    val result = value.!    Ok("Result:\n"+result)}16、FilenameUtils not filtering null bytes(0x00)如果一个文件名中包含null字节,且这个文件名被传递到系统层面进行执行,那么将只会获取null字节之前的字符串,因为在系统层面,所有的字符串将会被null字符截断,即使Java自身并不关心null字符;这种系统的行为能够被用来绕过以文件名末尾作为判断依据的验证方式;修复方式:升级Java -> Java 7.40以后; 对文件名进行严格过滤,不包含null、path characters等17、TrustManager that accept any certificatesTrustManager的空实现能够更方便的实现连接未通过CA认证的证书网站,但是同样也会造成中间人攻击,因为它信任任何证书;TrustManager应该进行一个特殊的认证,比如基于一个truststoreVulnerable Code: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;    }}Solution (TrustMangager based on a keystore):protected org.apache.http.conn.ssl.SSLSocketFactory createAdditionalCertsSSLSocketFactory() {    try {        final KeyStore ks = KeyStore.getInstance("BKS");        // the bks file we generated above        final InputStream in = context.getResources().openRawResource( R.raw.mystore);          try {            // don't forget to put the password used above in strings.xml/mystore_password            ks.load(in, context.getString( R.string.mystore_password ).toCharArray());        } finally {            in.close();        }        return new AdditionalKeyStoresSSLSocketFactory(ks);    } catch( Exception e ) {        throw new RuntimeException(e);    }}/** * Allows you to trust certificates from additional KeyStores in addition to * the default KeyStore */public class AdditionalKeyStoresSSLSocketFactory extends SSLSocketFactory {    protected SSLContext sslContext = SSLContext.getInstance("TLS");    public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {        super(null, null, null, null, null, null);        sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)}, null);    }    @Override    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);    }    @Override    public Socket createSocket() throws IOException {        return sslContext.getSocketFactory().createSocket();    }    /**    * Based on http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager    */    public static class AdditionalKeyStoresTrustManager implements X509TrustManager {        protected ArrayListx509TrustManagers = new ArrayList();        protected AdditionalKeyStoresTrustManager(KeyStore... additionalkeyStores) {            final ArrayListfactories = new ArrayList();            try {                // The default Trustmanager with default keystore                final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());                original.init((KeyStore) null);                factories.add(original);                for( KeyStore keyStore : additionalkeyStores ) {                    final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());                    additionalCerts.init(keyStore);                    factories.add(additionalCerts);                }            } catch (Exception e) {                throw new RuntimeException(e);            }            /*            * Iterate over the returned trustmanagers, and hold on            * to any that are X509TrustManagers            */            for (TrustManagerFactory tmf : factories)                for( TrustManager tm : tmf.getTrustManagers() )                    if (tm instanceof X509TrustManager)                        x509TrustManagers.add( (X509TrustManager)tm );            if( x509TrustManagers.size()==0 )                throw new RuntimeException("Couldn't find any X509TrustManagers");        }        /*        * Delegate to the default trust manager.        */        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {            final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0);            defaultX509TrustManager.checkClientTrusted(chain, authType);        }        /*        * Loop over the trustmanagers until we find one that accepts our server        */        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {            for( X509TrustManager tm : x509TrustManagers ) {                try {                    tm.checkServerTrusted(chain,authType);                    return;                } catch( CertificateException e ) {                    // ignore                }            }            throw new CertificateException();        }        public X509Certificate[] getAcceptedIssuers() {            final ArrayListlist = new ArrayList();            for( X509TrustManager tm : x509TrustManagers )                list.addAll(Arrays.asList(tm.getAcceptedIssuers()));            return list.toArray(new X509Certificate[list.size()]);        }    }}18、HostnameVerifier that accept any signed certificatesHostnameVerifier将会接受任意的host,因为证书会被用户很多host上,但是这样会造成中间人攻击,因为客户端会信任任意证书;TrustManager应该进行一个特殊的认证,比如基于一个truststore;通用证书应该被创建用于多域名下Vulnerable Code:public class AllHosts implements HostnameVerifier {    public boolean verify(final String hostname, final SSLSession session) {        return true;    }}Solution (TrustMangager based on a keystore):KeyStore ks = //Load keystore containing the certificates trustedSSLContext sc = SSLContext.getInstance("TLS");TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");tmf.init(ks);sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);19、MD2, MD4 and MD5 are weak hash functionsVulnerable Code:MessageDigest md5Digest = MessageDigest.getInstance("MD5");    md5Digest.update(password.getBytes());    byte[] hashValue = md5Digest.digest();byte[] hashValue = DigestUtils.getMd5Digest().digest(password.getBytes());Solution (Using bouncy castle):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();}Solution (Java 8 and later):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();}20、SHA-1 is a weak hash functionVulnerable Code:MessageDigest sha1Digest = MessageDigest.getInstance("SHA1");    sha1Digest.update(password.getBytes());    byte[] hashValue = sha1Digest.digest();byte[] hashValue = DigestUtils.getSha1Digest().digest(password.getBytes());21、Tainted filename read上传的用户名能够被用户更改,如:"../../../config/overide_file"、"shell.jsp\u0000expected.gif"22、Regex DOS (ReDOS)正则表达式常常会造成拒绝服务(ReDOS),原因在于正则引擎将会花费大量的时间用于分析一个确定的字符串,基于其正则表达式;如:正则(^(a+)+$)将会对字符串 "aaaaaaaaaaaaaaaaX" 进行65536种不同的路径分析,因此一定要正确的书写正则表达式,减少歧义23、XML parsing vulnerable to XXE (XMLStreamReader)XML External Entiry(XXE)攻击发生在当一个XML反编译其支持外部实体的时候风险 1: Expose local file content (XXE: XML eXternal Entity)[文件读取]]>&xxe;风险 2: Denial of service (XEE: Xml Entity Expansion)[拒绝服务][...]]>&lol9;Vulnerable Code:public void parseXML(InputStream input) throws XMLStreamException {    XMLInputFactory factory = XMLInputFactory.newFactory();    XMLStreamReader reader = factory.createXMLStreamReader(input);    [...]}Solution disabling External Entities:(禁用外部实体)public void parseXML(InputStream input) throws XMLStreamException {    XMLInputFactory factory = XMLInputFactory.newFactory();    factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);    XMLStreamReader reader = factory.createXMLStreamReader(input);    [...]}Solution disabling DTD:(禁用文档类型定义)public void parseXML(InputStream input) throws XMLStreamException {    XMLInputFactory factory = XMLInputFactory.newFactory();    factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);    XMLStreamReader reader = factory.createXMLStreamReader(input);    [...]}24、XML parsing vulnerable to XXE (SAXParser)Vulnerable Code:SAXParser parser = SAXParserFactory.newInstance().newSAXParser();parser.parse(inputStream, customHandler);Solution using "Secure processing" mode:(防止拒绝服务攻击、远程文件读取攻击)SAXParserFactory spf = SAXParserFactory.newInstance();spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);SAXParser parser = spf.newSAXParser();parser.parse(inputStream, customHandler);Solution disabling DTD:SAXParserFactory spf = SAXParserFactory.newInstance();spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);SAXParser parser = spf.newSAXParser();parser.parse(inputStream, customHandler);25、XML parsing vulnerable to XXE (XMLReader)Vulnerable Code:XMLReader reader = XMLReaderFactory.createXMLReader();reader.setContentHandler(customHandler);reader.parse(new InputSource(inputStream));Solution using "Secure processing" mode:(防止拒绝服务攻击、远程文件读取攻击)XMLReader reader = XMLReaderFactory.createXMLReader();reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);reader.setContentHandler(customHandler);reader.parse(new InputSource(inputStream));Solution disabling DTD:XMLReader reader = XMLReaderFactory.createXMLReader();reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);reader.setContentHandler(customHandler);reader.parse(new InputSource(inputStream));26、XML parsing vulnerable to XXE (DocumentBuilder)Vulnerable Code:DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();Document doc = db.parse(input);Solution using "Secure processing" mode:(防止拒绝服务攻击、远程文件读取攻击)DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);DocumentBuilder db = dbf.newDocumentBuilder();Document doc = db.parse(input);Solution disabling DTD:DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);DocumentBuilder db = dbf.newDocumentBuilder();Document doc = db.parse(input);27、Potential XPath InjectionXPath注入跟SQL注入类似,输入来自不受信任输入,将获取XML文件中无权限读取的内容28、Found Struts 1 endpoint这个类是Structs 1的属性,请求消息经过这个控制类,表单对象就会自动的包含这个HTTP参数,这个参数使用时一定要保证其安全性29、Found Struts 2 endpointStructs 2 endpoint是一个简单的java Object对象(POJOs),它不需要继承或实现任何接口或类;一旦这个请求消息经过这个控制器,HTTP请求的参数将会被自动的映射到这个类的选择器中(setters),因此这个类的所有选择器都将会被认为是不可信任的输入,即使他们不包含这些值;30、Found Spring endpoint这个类是Spring的控制器,所有的方法添加了注释RequestMapping(或短注释GetMapping, PostMapping, PutMapping, DeleteMapping, PatchMapping)将会是远程可达的,这个类需要经过认真分析以确保暴露的方法是安全的31、Spring CSRF protection disabled禁用CSRF保护是不安全的Insecure configuration:@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Override    protected void configure(HttpSecurity http) throws Exception {        http.csrf().disable();    }}32、Spring CSRF unrestricted RequestMapping添加RequestMapping注释的方法默认将会映射到所有的HTTP请求方法,然而默认情况下,CSRF保护对于GET、HEAD、TRACE、OPTIONS方法无效;因此处于变动中的方法(state-changing)的方法被添加了RequestMapping的注释,且没有限制使用它的HTTP方法(如POST、PUT、DELETE、PATCH)将会存在CSRF风险Vulnerable Code:@Controllerpublic class UnsafeController {    @RequestMapping("/path")    public void writeData() {        // State-changing operations performed within this method.    }}Solution (Spring Framework 4.3 and later):@Controllerpublic class SafeController {    /**    * For methods without side-effects use @GetMapping.    */    @GetMapping("/path")    public String readData() {        // No state-changing operations performed within this method.        return "";    }    /**    * For state-changing methods use either @PostMapping, @PutMapping, @DeleteMapping, or @PatchMapping.    */    @PostMapping("/path")    public void writeData() {        // State-changing operations performed within this method.    }}Solution (Before Spring Framework 4.3):@Controllerpublic class SafeController {    /**    * For methods without side-effects use either    * RequestMethod.GET, RequestMethod.HEAD, RequestMethod.TRACE, or RequestMethod.OPTIONS.    */    @RequestMapping(value = "/path", method = RequestMethod.GET)    public String readData() {        // No state-changing operations performed within this method.        return "";    }    /**    * For state-changing methods use either    * RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, or RequestMethod.PATCH.    */    @RequestMapping(value = "/path", method = RequestMethod.POST)    public void writeData() {        // State-changing operations performed within this method.    }}33、Potential injection (custom)方法存在潜在注入的可能性,输入也没有进行严格的过滤Vulnerable code samples:SqlUtil.execQuery("select * from UserEntity t where id = " + parameterInput);34、Potential SQL InjectionVulnerable Code:createQuery("select * from User where id = '"+inputId+"'");Solution:import org.owasp.esapi.Encoder;createQuery("select * from User where id = '"+Encoder.encodeForSQL(inputId)+"'");34、Potential SQL Injection with TurbineTurbine API提供了一个DSL用于构建Java的查询Vulnerable Code:ListBasePeer.executeQuery( "select * from Customer where id=" + inputId );Solution (using Criteria DSL):Criteria c = new Criteria();c.add( CustomerPeer.ID, inputId );Listcustomers = CustomerPeer.doSelect( c );Solution (using specialized method):Customer customer = CustomerPeer.retrieveByPK( new NumberKey( inputId ) );Solution (using OWASP Encoder):import org.owasp.esapi.Encoder;BasePeer.executeQuery("select * from Customer where id = '"+Encoder.encodeForSQL(inputId)+"'");35、Potential SQL/HQL Injection (Hibernate)Hibernate标准用于预编译Vulnerable Code:Session session = sessionFactory.openSession();Query q = session.createQuery("select t from UserEntity t where id = " + input);q.execute();Solution:Session session = sessionFactory.openSession();Query q = session.createQuery("select t from UserEntity t where id = :userId");q.setString("userId",input);q.execute();Solution for dynamic queries (with Hibernate Criteria):Session session = sessionFactory.openSession();Query q = session.createCriteria(UserEntity.class)    .add( Restrictions.like("id", input) )    .list();q.execute();36、Potential SQL/JDOQL Injection (JDO)Vulnerable Code:PersistenceManager pm = getPM();Query q = pm.newQuery("select * from Users where name = " + input);q.execute();Solution:PersistenceManager pm = getPM();Query q = pm.newQuery("select * from Users where name = nameParam");q.declareParameters("String nameParam");q.execute(input);37、Potential SQL/JPQL Injection (JPA)Vulnerable Code:EntityManager pm = getEM();TypedQueryq = em.createQuery(    String.format("select * from Users where name = %s", username),    UserEntity.class);UserEntity res = q.getSingleResult();Solution:TypedQueryq = em.createQuery(    "select * from Users where name = usernameParam",UserEntity.class)    .setParameter("usernameParam", username);UserEntity res = q.getSingleResult();38、Potential JDBC Injection (Spring JDBC)Vulnerable Code:JdbcTemplate jdbc = new JdbcTemplate();int count = jdbc.queryForObject("select count(*) from Users where name = '"+paramName+"'", Integer.class);Solution:JdbcTemplate jdbc = new JdbcTemplate();int count = jdbc.queryForObject("select count(*) from Users where name = ?", Integer.class, paramName);39、Potential JDBC InjectionVulnerable Code:Connection conn = [...];Statement stmt = con.createStatement();ResultSet rs = stmt.executeQuery("update COFFEES set SALES = "+nbSales+" where COF_NAME = '"+coffeeName+"'");Solution:Connection conn = [...];conn.prepareStatement("update COFFEES set SALES = ? where COF_NAME = ?");updateSales.setInt(1, nbSales);updateSales.setString(2, coffeeName);40、Potential Scala Slick InjectionVulnerable Code:db.run {  sql"select * from people where name = '#$value'".as[Person]}Solution:db.run {  sql"select * from people where name = $value".as[Person]}41、Potential Scala Anorm InjectionVulnerable Code:val peopleParser = Macro.parser[Person]("id", "name", "age")DB.withConnection { implicit c =>  val people: List[Person] = SQL("select * from people where name = '" + value + "'").as(peopleParser.*)}Solution:val peopleParser = Macro.parser[Person]("id", "name", "age")DB.withConnection { implicit c =>  val people: List[Person] = SQL"select * from people where name = $value".as(peopleParser.*)}42、Potential LDAP Injection同SQL注入,但是LDAP注入并没有预编译的操作,因此要对输入进行严格的过滤Code at risk:NamingEnumerationanswers = context.search("dc=People,dc=example,dc=com", "(uid=" + username + ")", ctrls);43、Potential code injection when using Script Engine使用"Cloudbees Rhino Sandbox"沙箱安全的执行JS代码Code at risk:public void runCustomTrigger(String script) {    ScriptEngineManager factory = new ScriptEngineManager();    ScriptEngine engine = factory.getEngineByName("JavaScript");    engine.eval(script); //Bad things can happen here.}Solution:public void runCustomTrigger(String script) {    SandboxContextFactory contextFactory = new SandboxContextFactory();    Context context = contextFactory.makeContext();    contextFactory.enterContext(context);    try {        ScriptableObject prototype = context.initStandardObjects();        prototype.setParentScope(null);        Scriptable scope = context.newObject(prototype);        scope.setPrototype(prototype);        context.evaluateString(scope,script, null, -1, null);    } finally {        context.exit();    }}43、Potential code injection when using Spring ExpressionSpring Expression使用动态参数进行构建,用于构建的参数一定要严格过滤Code at risk:public void parseExpressionInterface(Person personObj,String property) {        ExpressionParser parser = new SpelExpressionParser();        //Unsafe if the input is control by the user..        Expression exp = parser.parseExpression(property+" == 'Albert'");        StandardEvaluationContext testContext = new StandardEvaluationContext(personObj);        boolean result = exp.getValue(testContext, Boolean.class);[...]44、Potential code injection when using Expression Language (EL)表达式语言Code at risk:public void evaluateExpression(String expression) {    FacesContext context = FacesContext.getCurrentInstance();    ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory();    ELContext elContext = context.getELContext();    ValueExpression vex = expressionFactory.createValueExpression(elContext, expression, String.class);    return (String) vex.getValue(elContext);}45、Potential code injection in Seam logging callSeam logging API支持将表达式语言导入日志消息中,表达式语言同样可以作为代码执行的源头Code at risk:public void logUser(User user) {    log.info("Current logged in user : " + user.getUsername());    //...}Solution:public void logUser(User user) {    log.info("Current logged in user : #0", user.getUsername());    //...}46、Potential HTTP Response Splitting(CRLF)response中包含CRLF字符时,将会被拆分为两个消息,攻击者通过控制第二个消息达到攻击的目的Code at risk:String author = request.getParameter(AUTHOR_PARAMETER);// ...Cookie cookie = new Cookie("author", author);response.addCookie(cookie);47、Potential CRLF Injection for logs通过回车换行(CRLF)在日志文件中导入恶意内容Code at risk:String val = request.getParameter("val");try {    number = Integer.parseInt(val);} catch (NumberFormatException) {    log.info("Failed to parse val = " + val);}48、Potential external control of configuration允许额外的系统设置将会导致服务中断,或造成应用出现不可预测行为,甚至是恶意行为,Code at risk:conn.setCatalog(request.getParameter("catalog"));49、Bad hexadecimal concatenation当将一个包含hash签名的字节数组转成可读的字符串时,如果逐字节进行转换将会带来安全问题;如:MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] resultBytes = md.digest(password.getBytes("UTF-8"));StringBuilder stringBuilder = new StringBuilder();for(byte b :resultBytes) {    stringBuilder.append( Integer.toHexString( b & 0xFF ) );}return stringBuilder.toString();上面的转换使用了Integer.toHexString()将会过滤掉每个字节的0字符,将会降低hash的碰撞强度,因为"0x0679" and "0x6709"将会产生相同的HASH值:“679”Solution:stringBuilder.append( String.format( "%02X", b ) );50、Hazelcast symmetric encryptionHazelcast集群服务的网络通信被设定为使用对称加密(DES、blowfish),这些单独的加密服务并不提供完整的或安全的认证,建议使用非对称加密51、NullCipher is insecureNullCipher在实际的产品中很少使用,通过返回明文信息来实现Cipher接口,在测试环境下,NullCipher可能会被使用Vulnerable Code:Cipher doNothingCihper = new NullCipher();[...]//The ciphertext produced will be identical to the plaintext.byte[] cipherText = c.doFinal(plainText);52、Unencrypted Socket用于通信的信道未加密,这些流量能够被攻击者读取Vulnerable Code(Plain socket (Cleartext communication)):Socket soc = new Socket("www.google.com",80);Solution(SSL Socket (Secure communication)):Socket soc = SSLSocketFactory.getDefault().createSocket("www.google.com", 443);除了使用SSL通信,还需要保证使用SSLSocketFactory对证书进行认证,已保证未受到中间人攻击53、Unencrypted Server SocketVulnerable Code(Plain server socket (Cleartext communication)):ServerSocket soc = new ServerSocket(1234);Solution(SSL Server Socket (Secure communication)):ServerSocket soc = SSLServerSocketFactory.getDefault().createServerSocket(1234);除了使用SSL通信,还需要保证使用SSLSocketFactory对证书进行认证,已保证未受到中间人攻击54、DES/DESede is insecureDES、DESede(3DES)对于现在的应用来说并不是强壮的加密算法,现在更推荐使用AESExample weak code:Cipher c = Cipher.getInstance("DESede/ECB/PKCS5Padding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);Example solution:Cipher c = Cipher.getInstance("AES/GCM/NoPadding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);55、RSA with no padding is insecure使用RSA算法,但是并没有正确的是指padding,这将削弱加密算法的安全性Vulnerable Code:Cipher.getInstance("RSA/NONE/NoPadding")Solution(The code should be replaced with):Cipher.getInstance("RSA/ECB/OAEPWithMD5AndMGF1Padding")56、Hard Coded Password密码(password)不应该被存储源码之中,因为源码会被分享到所有的环境中,甚至公开源码,为保证安全性,密钥应该被存储到单独的配置文件或KeyStore中Vulnerable Code:private String SECRET_PASSWORD = "letMeIn!";Properties props = new Properties();props.put(Context.SECURITY_CREDENTIALS, "p@ssw0rd");57、Hard Coded Key密钥(Cryptographic keys)不应该被存储源码之中,因为源码会被分享到所有的环境中,甚至公开源码,为保证安全性,密钥应该被存储到单独的配置文件或KeyStore中Vulnerable Code:byte[] key = {1, 2, 3, 4, 5, 6, 7, 8};SecretKeySpec spec = new SecretKeySpec(key, "AES");Cipher aes = Cipher.getInstance("AES");aes.init(Cipher.ENCRYPT_MODE, spec);return aesCipher.doFinal(secretData);58、Struts Form without input validation表单输入应该具有最小的输入验证,通过一个validate方法public class RegistrationForm extends ValidatorForm {    private String name;    private String email;    [...]    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {        //Validation code for name and email parameters passed in via the HttpRequest goes here    }}59、XSSRequestWrapper is a weak XSS protection一个HttpServletRequestWrapper的实现,其过滤较弱的原因如下:1)仅仅对参数进行了过滤,而对头、别的输入未进行过滤;2)替换规则能够被轻松绕过(见下示例);3)基于黑名单规则,而非白名单。Example of bypass:alert(1)上述输经过过滤后变成 "alert(1)" 移除VBScript,使得以前的语句转变成了 ".*"60、Blowfish usage with short keyBlowfish加密支持32位到448位的密钥长度,如果密钥太小,将会导致加密后的文档很容易被爆破,密钥长度应至少为128位Vulnerable Code:KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");keyGen.init(64);Solution:KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");keyGen.init(128);61、RSA usage with short keyNIST建议RSA的密钥长度应该保持在2048位以上Vulnerable Code:KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");keyGen.initialize(512);Solution(The KeyPairGenerator creation should be as follows with at least 2048 bit key size):KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");keyGen.initialize(2048);62、Unvalidated Redirect无效的重定向通常发生在应用通过用户指定的参数进行重定向,该方式可能导致钓鱼攻击攻击剧本如下所示:1)用户被重定向到一个恶意网站:http://website.com/login?redirect=http://evil.vvebsite.com/fake/login2)用户被重定向到一个跟元网页很像的登录网页(http://evil.vvebsite.com/fake/login)3)用户输入登录凭证4)恶意网站盗取用户的登录凭证并重定向到源网页Vulnerable Code:protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    [...]    resp.sendRedirect(req.getParameter("redirectUrl"));    [...]}def login(redirectUrl:String) = Action {    [...]    Redirect(url)}应对策略:不接受来自用户的重定向网址;使用一个destination key进行重定向;只接受相关联的路径;白名单;确认URL是否开始部分是否在白名单中;63、Dynamic JSP inclusion通过变量包含JSP文件,攻击者将一个别的文件进行包含,从而执行任意代码Vulnerable Code:Solution:64、Dynamic variable in Spring expressionSpring expression通过动态参数进行构建,动态参数应该被进行验证Vulnerable Code:<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %><%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>Solution:65、Escaping of special XML characters is disabled通过在客户端执行不需要的JS代码从而触发XSSVulnerable Code:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>Solution:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>66、Potential XSS in JSP预防XSS最有效的方法就是对输出进行编码,主要包含四种输出编码:HTML, JavaScript, CSS (styles), and URLsVulnerable Code:<%String taintedInput = (String) request.getAttribute("input");%>[...]<%= taintedInput %>Solution:<%String taintedInput = (String) request.getAttribute("input");%>[...]<%= Encode.forHtml(taintedInput) %>67、Potential XSS in ServletVulnerable Code:protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    String input1 = req.getParameter("input1");    [...]    resp.getWriter().write(input1);}Solution:protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    String input1 = req.getParameter("input1");    [...]    resp.getWriter().write(Encode.forHtml(input1));}68、XMLDecoder usageXMLDecoder不应该用于对不可信数据进行解析,反序列化用户输入将会导致代码执行,因为XMLDecoder支持方法调用,其本意是调用setter方法,但实际上,任意方法将会被调用Malicious XML example:该XML代码将会产生一个内容为"Hello World!"的文件/tmp/Hacked.txtHello World!Vulnerable Code:XMLDecoder d = new XMLDecoder(in);try {    Object result = d.readObject();}[...]69、Static IV每加密一个消息,初始化向量必须被重新生成Vulnerable Code:private static byte[] IV = new byte[16] {(byte)0,(byte)1,(byte)2,[...]};public void encrypt(String message) throws Exception {    IvParameterSpec ivSpec = new IvParameterSpec(IV);[...]Solution:public void encrypt(String message) throws Exception {    byte[] iv = new byte[16];    new SecureRandom().nextBytes(iv);    IvParameterSpec ivSpec = new IvParameterSpec(iv);[...]70、ECB mode is insecure一个认证的加密模式相较与ECB模式会提供对加密数据更好的机密性,ECB模式对于每一个相同的输入都会产生相同的输出,因此如果一个用户发送一个password,加密后的数值每次都是相同的,这将使得攻击者截获并重放这个数据;为修复这个问题,建议使用Galois/Counter Mode (GCM) 模式;Code at risk:Cipher c = Cipher.getInstance("AES/ECB/NoPadding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);Solution:Cipher c = Cipher.getInstance("AES/GCM/NoPadding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);71、Cipher is susceptible to Padding OracleCBC携带PKCS5Padding模式易受padding oracle的攻击,如果系统暴露了有效padding与无效padding下明文的不同,消息将会被破解掉;有效padding与无效padding之间的区别,主要通过不同条件下的返回的错误消息获得Code at risk:Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);Solution:Cipher c = Cipher.getInstance("AES/GCM/NoPadding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);72、Cipher with no integrity加密后的数据容易被攻击者篡改,那么就意味着哪些没有提供数据检测的加密模式将会受到影响;解决的方式是使用一个包含HMAC的加密模式,它们会先检查加密值的HMAC然后再进行解密等操作不提供HMAC的加密模式:CBC、OFB、CTR、ECBCode at risk:(AES in CBC mode)Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);(Triple DES with ECB mode  3DES)Cipher c = Cipher.getInstance("DESede/ECB/PKCS5Padding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);Solution:Cipher c = Cipher.getInstance("AES/GCM/NoPadding");c.init(Cipher.ENCRYPT_MODE, k, iv);byte[] cipherText = c.doFinal(plainText);73、Use of ESAPI EncryptorESAPI的漏洞相对而言是比较少的,这里将给出一个快速定位问题的列表:1)Library Version问题在版本 2.1.0 中被修复,版本 < 2.0.1的存在MAC绕过(CVE-2013-5679)2)Configuration版本2.1.0仍然存在这在密文定义中key大小被改变的问题(CVE-2013-5960),一些防护措施需要实施Insecure configuration:Encryptor.CipherText.useMAC=falseEncryptor.EncryptionAlgorithm=AESEncryptor.CipherTransformation=AES/CBC/PKCS5PaddingEncryptor.cipher_modes.additional_allowed=CBCSecure configuration:#NeededEncryptor.CipherText.useMAC=true#Needed to have a solid auth. encryptionEncryptor.EncryptionAlgorithm=AESEncryptor.CipherTransformation=AES/GCM/NoPadding#CBC mode should be removed to avoid padding oracleEncryptor.cipher_modes.additional_allowed=74、External file access (Android)存储在SD卡中数据,任何具有READ_EXTERNAL_STORAGE权限的应用均能够读取该数据,因此对于一些隐私数据,需要加密存储在SD卡中或存储在应用私有文件下Code at risk:file file = new File(getExternalFilesDir(TARGET_TYPE), filename);fos = new FileOutputStream(file);fos.write(confidentialData.getBytes());fos.flush();Better alternative:fos = openFileOutput(filename, Context.MODE_PRIVATE);fos.write(string.getBytes());75、Broadcast (Android)broadcast传递的消息能够被拥有合适权限的应用监听到,因此避免使用broadcast传递敏感数据Code at risk:Intent i = new Intent();i.setAction("com.insecure.action.UserConnected");i.putExtra("username", user);i.putExtra("email", email);i.putExtra("session", newSessionId);this.sendBroadcast(v1);Solution (if possible):Intent i = new Intent();i.setAction("com.secure.action.UserConnected");sendBroadcast(v1);Configuration (receiver)[1] Source: StackOverflow:...Configuration (sender)[1] Source: StackOverflow:76、World writable file (Android)使用MODE_WORLD_READABLE创建一个文件的时候,这个文件将会被任意应用读取Code at risk:fos = openFileOutput(filename, MODE_WORLD_READABLE);fos.write(userInfo.getBytes());Solution (using MODE_PRIVATE):fos = openFileOutput(filename, MODE_PRIVATE);Solution (using local SQLite Database):使用本地的SQLite数据库进行存储数据,但是需要保证数据库的权限完全正确77、WebView with geolocation activated (Android)建议询问用户是否获取其地理位置,而不要直接返回给用户地理位置相关信息Code at risk:webView.setWebChromeClient(new WebChromeClient() {    @Override    public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {        callback.invoke(origin, true, false);    }});Suggested code:webView.setWebChromeClient(new WebChromeClient() {    @Override    public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {        callback.invoke(origin, true, false);        //Ask the user for confirmation    }});78、WebView with JavaScript enabled (Android)webview具有支持JS的功能,意味着存在XSS漏洞的可能,因此这些页面需要对潜在的XSS漏洞进行防护WebView myWebView = (WebView) findViewById(R.id.webView);WebSettings webSettings = myWebView.getSettings();webSettings.setJavaScriptEnabled(true);                  //开启JS支持79、WebView with JavaScript interface (Android)使用JS接口将会将次API暴力给危险的接口,如果该Webview触发了XSS,这个类将会被恶意代码调用Code at risk:WebView myWebView = (WebView) findViewById(R.id.webView);myWebView.addJavascriptInterface(new FileWriteUtil(this), "fileWriteUtil");WebSettings webSettings = myWebView.getSettings();webSettings.setJavaScriptEnabled(true);[...]class FileWriteUtil {    Context mContext;    FileOpenUtil(Context c) {        mContext = c;    }    public void writeToFile(String data, String filename, String tag) {        [...]    }}80、Cookie without the secure flag一个包含Secure属性的Cookie,将会阻止浏览器使用不安全的通信(HTTP)传输次CookieCode at risk:Cookie cookie = new Cookie("userName",userName);response.addCookie(cookie);Solution (Specific configuration):Cookie cookie = new Cookie("userName",userName);cookie.setSecure(true); // Secure flagcookie.setHttpOnly(true);Solution (Servlet 3.0 configuration):[...]truetrue81、Cookie without the HttpOnly flag防止恶意脚本盗取CookieCode at risk:Cookie cookie = new Cookie("email",userName);response.addCookie(cookie);Solution (Specific configuration):Cookie cookie = new Cookie("email",userName);cookie.setSecure(true);cookie.setHttpOnly(true); //HttpOnly flagSolution (Servlet 3.0 configuration):[...]truetrue82、Object deserialization is used如果允许用户远程反序列话操作,将会导致代码执行等恶意行为,需要避免反序列话操作提供给远端用户Code at risk:public UserData deserializeObject(InputStream receivedFile) throws IOException, ClassNotFoundException {    try (ObjectInputStream in = new ObjectInputStream(receivedFile)) {        return (UserData) in.readObject();    }}83、This class could be used as deserialization gadget反序列化的gadget是那些被攻击者利用本地序列号API的类,这些类要么通过readObject添加用户的行为,要么被一个序列号对象调用;84、Trust Boundary Violation可信任边界可以理解为一条线,一边是可信任数据,另一边是不可信任数据,可信任边界的逻辑将数据从不可信任转化为可信任的;信任边界的污染主要通过将可信任数据与不可信任数据混杂在一起,使得应用信任恶意数据Code at risk:public void doSomething(HttpServletRequest req, String activateProperty) {    //..    req.getSession().setAttribute(activateProperty,"true");}public void loginEvent(HttpServletRequest req, String userSubmitted) {    //..    req.getSession().setAttribute("user",userSubmitted);}Solution: 解决的方式是预先设置一些安全session属性,如果可能使用预先设置的属性而不是用户输入的属性84、A malicious XSLT could be providedXSLT(Extensible Stylesheet Language Transformations)是一个将XML文档转换成另一个XML文档的语言;但是它可能通过样式脚本进行恶意行为,因此如果一个攻击者能够控制样式脚本的源,它将可能造成远程代码执行Code at risk:Code at risk:Source xslt = new StreamSource(new FileInputStream(inputUserFile)); //Dangerous source to validateTransformer transformer = TransformerFactory.newInstance().newTransformer(xslt);Source text = new StreamSource(new FileInputStream("/data_2_process.xml"));transformer.transform(text, new StreamResult(...));85、Potential information leakage in Scala Play页面对于不同的正确数据给出不同的回复,可能造成敏感信息(API keys, passwords, product versions or environment configurations)的泄露,Code at risk:def doGet(value:String) = Action {  val configElement = configuration.underlying.getString(value)  Ok("Hello "+ configElement +" !")}86、Scala Play Server-Side Request Forgery (SSRF)Vulnerable Code:def doGet(value:String) = Action {    WS.url(value).get().map { response =>        Ok(response.body)    }}Solution/Countermeasures:不接受来自用户请求目的地;接收一个目的key,并用其检查目标是否合法;URL白名单;确认URL的开始是否在白名单中;87、Potential XSS in Scala Twirl template engineVulnerable Code:@(value: Html)@valueSolution:@(value: String)@value88、Potential XSS in Scala MVC API engine Vulnerable Code:def doGet(value:String) = Action {    Ok("Hello " + value + " !").as("text/html")  }Solution:def doGet(value:String) = Action {    Ok("Hello " + Encode.forHtml(value) + " !")  }89、Potential template injection with VelocityVelocity脚本引擎是非常强力的,它能够在条件语句中增加逻辑判断、循环、或额外调用,它并没有设计在沙箱内部,一个恶意用户能够控制模板用于在服务端执行恶意代码Vulnerable Code:[...]Velocity.evaluate(context, swOut, "test", userInput);Solution: 避免以用户的输入作为模板的结尾,如果想要将模板暴露给用户进行编辑,最好使用少逻辑判断的引擎,如:Handlebars、Moustache等90、Potential template injection with Freemarker同89Vulnerable Code:Template template = cfg.getTemplate(inputTemplate);[...]template.process(data, swOut);91、Overly permissive CORS(跨域共享资源) policy跨域共享资源策略定义的过于宽泛,将会导致任意域的脚本都能在本页面执行,从而窃取本页面信息Vulnerable Code:response.addHeader("Access-Control-Allow-Origin", "*");92、Anonymous(匿名) LDAP bind没有进行有效的认证,执行一条能够被用户控制的LDAP语句,从而取得目录访问权限Vulnerable Code:...env.put(Context.SECURITY_AUTHENTICATION, "none");DirContext ctx = new InitialDirContext(env);...93、LDAP Entry PoisoningJNDI API支持在LDAP目录中绑定可序列号对象,如果该属性被设置,当应用进行查询操作时,对象将会被反序列化,对象反序列号应该被认为是一个远程代码执行的风险;如果拥有一个LDAP的入口,那么代码利用将成为可能Vulnerable Code:DirContext ctx = new InitialDirContext();//[...]ctx.search(query, filter,        new SearchControls(scope, countLimit, timeLimit, attributes,            true,                                                        //Enable object deserialization if bound in directory            deref));Solution:DirContext ctx = new InitialDirContext();//[...]ctx.search(query, filter,        new SearchControls(scope, countLimit, timeLimit, attributes,            false,                                                      //Disable            deref));94、Persistent(持久) Cookie UsageVulnerable Code: (下面的代码将Cookie的有效期设置为一年)[...]Cookie cookie = new Cookie("email", email);cookie.setMaxAge(60*60*24*365);[...]Solution:不使用持有Cookie用户传输敏感数据95、URL rewriting methodURL rewriting方法具有安全的风险,因为session ID出现在URL中,很容易被第三方获取,获取的方法有很多种,如:1)日志文件2)浏览器历史记录3)复制黏贴到邮件或post消息中4)HTTP refererVulnerable Code:out.println("Clickhere");96、Insecure SMTP SSL connection如果服务器端不支持SSL认证连接,一些邮件包虽然支持SSL连接,但是却不验证服务端证书;这就相当于信任所有的证书,当尝试连接服务器,应用就会接受一些恶意证书,这将导致敏感信息的泄露Vulnerable Code:...Email email = new SimpleEmail();email.setHostName("smtp.servermail.com");email.setSmtpPort(465);email.setAuthenticator(new DefaultAuthenticator(username, password));email.setSSLOnConnect(true);email.setFrom("user@gmail.com");email.setSubject("TestMail");email.setMsg("This is a test mail ... :-)");email.addTo("foo@bar.com");email.send();...Solution:(添加服务端证书校验)email.setSSLCheckServerIdentity(true);97、AWS Query Injection简单数据库查询包含用户的输入将会导致攻击者查看到未被认证的数据,跟SQL注入非常类似Vulnerable Code:...String customerID = getAuthenticatedCustomerID(customerName, customerCredentials);String productCategory = request.getParameter("productCategory");...AmazonSimpleDBClient sdbc = new AmazonSimpleDBClient(appAWSCredentials);String query = "select * from invoices where productCategory = '"            + productCategory + "' and customerID = '"            + customerID + "' order by '"            + sortColumn + "' asc";SelectResult sdbResult = sdbc.select(new SelectRequest(query));98、JavaBeans Property Injection一个攻击者通过设置bean的属性来改变系统的完整性,Bean一个属性就是允许设置其属性,一个攻击者能够利用这个功能来获取特殊的bean属性,如classloader,那么这将能够覆盖系统的一些属性导致代码执行Vulnerable Code:MyBean bean = ...;HashMap map = new HashMap();Enumeration names = request.getParameterNames();while (names.hasMoreElements()) {    String name = (String) names.nextElement();    map.put(name, request.getParameterValues(name));}BeanUtils.populate(bean, map);99、Struts File Disclosure使用用户输入构造服务端重定向地址将会使一个攻击者下载应用受保护的二进制文件、配置文件等;一个攻击者能够构造一个请求参数来匹配敏感文件地址,如请求:"http://example.com/?returnURL=WEB-INF/applicationContext.xml" 将会展示应用的配置文件applicationContext.xml,攻击者将能够查看并下载该文件,从而获取敏感信息进行进一步攻击Vulnerable Code:... String returnURL = request.getParameter("returnURL"); Return new ActionForward(returnURL); ...100、Spring File Disclosure同99Vulnerable Code:... String returnURL = request.getParameter("returnURL");return new ModelAndView(returnURL); ...101、Format String Manipulation用户控制字符串格式化操作将会导致抛出异常,甚至可能泄露一些敏感信息Vulnerable Code:Formatter formatter = new Formatter(Locale.US);String format = "The customer: %s %s has the balance %4$." + userInput + "f";formatter.format(format, firstName, lastName, accountNo, balance);102、HTTP Parameter Pollution(污染)将一个用户的输入连接到URL中,可能导致已有参数被覆盖,从而改变应用的行为,下例中如果用户输入lang=en&user_id=1,那么user_id参数将会被覆盖Vulnerable Code:String lang = request.getParameter("lang");GetMethod get = new GetMethod("http://www.host.com");get.setQueryString("lang=" + lang + "&user_id=" + user_id);get.execute();

    相关文章

      网友评论

          本文标题:Find_Sec_Bugs检测项

          本文链接:https://www.haomeiwen.com/subject/zcnzfxtx.html