Java 安全研究初探
发布日期:2023-04-03      作者:       来源:      分享:

Java 安全研究初探(上)

前言

Java 安全,通常指代的是 Java Web 安全。在刚开始学习的一段时间里,面对众多的框架和名词,比如 Spring、Weblogic、EJB、AKB,等等,总感觉狗啃泰山无从下嘴。于是就有了这篇文章,旨在记录学习过程中遇到的所有问题,希望也能对像我一样的初学者有所帮助。

Java EE

搞过 Java Web 开发的多少会知道 JavaEE。从字面上来看,它的意思是 Java Platform, Enterprise Edition,即 Java 平台企业版。这个名称有够抽象,实际上 Java EE 包含了一系列标准,这些标准主要用来为企业级应用提供分布式计算、Web 服务等的统一定义和接口。另外,这些标准在设计之初就考虑了安全性、拓展性、并发等能力。

Java EE 的前身为 J2EE、后来捐献给 Eclipse 基金会,改名为 Jakarta EE。在不引起混淆的情况下,后文统一称为 Java EE。

Java EE 是在 Java SE(Standard Edition) 的基础上建立的,并增加了许多额外的特性和技术来方便构建高可用的企业级应用。Java 中的标准一般定义在 JSR 中,即 Java Specification Requests。例如,Java EE 所涉及的标准就在 JSRs by Platform - Java EE,其中包含了一些已经撤回的。

从这些提案(标准)中,我们大概可以知道 Java EE 所涵盖的一些方向,比如:

  • jsr-9: XML Parsing Specification, 定义了 Java 中对 XML 进行处理的接口 (JAXP);

  • jsr-19: Enterprise JavaBeans 2.0,即 EJB,提供了易管理的企业级组件定义和接口;

  • jsr-52: A Standard Tag Library for JavaServer PagesTM,定义了 JSP 中标签库的标准;

  • jsr-53: JavaTM Servlet 2.3 and JavaServer PagesTM 1.2 Specifications,定义了 Servlet 和 JSP 的标准;

  • jsr-367: JavaTM API for JSON Binding (JSON-B),定义了 Java 对象到 JSON 之间转换(序列化/反序列化) 的标准;

除此之外还有许多其他的定义,如 EJB,JSF (JavaServer Faces),CDI (Contexts and Dependency Injection),JavaMail 等,这里其实可以不用太过关注,在需要深入理解的时候再阅读其文档即可。

参考资料:

Servlet

Servlet 可以说是 Java Web 开发中基础的基础。可能和我差不多的中年人都会知道早期 Web 开发大多是基于 CGI 的,即写一个二进制程序或者 Perl 脚本去处理 Web 服务器的 HTTP 请求,请求的内容会封装到环境变量里。Sun 公司在 1996 年发布 Servlet 技术就是为了和 CGI 进行竞争,Servlet 这个名字是 Server Applet 的缩写,即服务端小程序。

从上面的介绍也可以看到,Servlet 本身也是一个 Java EE 的标准,比如 Java Servlet 3.1 就是其中一个版本。一般开发者可能很少需要直接阅读标准文档,且文档中也说明其目标主要是 Web 服务器的开发者。不过作为安全研究人员,我们可以从中学习到很多 Servlet 的基础架构和设计思想。

从实现上来说,Servlet 是一个实现了特定接口的 Web 组件,由 Servlet 容器去加载并运行。容器 本身并不一定是 Web 服务器,但容器需要至少支持 HTTP 请求,并将请求的内容封装成 Servlet 接口的参数;因此容器通常与 Web 服务器集成或者作为其拓展而存在。目前常见的 Servlet 容器如 Tomcat、GlassFish、JBoss 等,同样也具备 Web 服务器的功能。

根据文档所述,一个基础的 Servlet 接口如下所示:

package javax.servlet;import java.io.IOException;


public interface Servlet {

void init(ServletConfig var1) throws ServletException;

ServletConfig getServletConfig();

void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

String getServletInfo();

void destroy();}

其中重要的接口是 service 方法,用于处理客户端的请求,并填充所需的返回结果。init 和 destory 是生命周期方法,在 Servlet 被加载和销毁时只执行一次。 getServletConfig 返回的对象是类似 name/value 格式的配置信息,由用户配置文件中进行传入。

HelloServlet

为了方便开发者进行快速的原型开发,在 JDK 中已经实现了常用的 Servlet 功能,即 GenericServlet,而对于 HTTP 请求,又进一步实现了 HttpServlet 抽象类。这样,如果用户需要编写一个 Servlet 处理如 GET/POST 请求,只需要重写对应的方法即可。

使用 IDEA 自动创建的示例 Servlet 如下:

import java.io.*;import javax.servlet.http.*;import javax.servlet.annotation.*;


@WebServlet(name = "helloServlet", value = "/hello")public class HelloServlet extends HttpServlet {private String message;public void init() {message = "Hello World!";}

 

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {

response.setContentType("text/html");

 

// Hello PrintWriter out = response.getWriter();

out.println("<html><body>");

out.println("<h1>" + message + "</h1>");

out.println("</body></html>");

}

 

public void destroy() {

}}

值得注意的是,正常情况下定义好 HelloServlet 类之后,容器并不知道这个类的存在,也不知道应该将什么 HTTP 路径映射到这个 Servlet,传统上需要用户自己修改 web.xml 配置文件(这也是 Servlet 标准的一部分),添加 <servlet> 和 <servlet-mapping> 标签来指定这些信息。在 Servlet 3.0 之后,就可以使用注解的方式配置 Servlet 了,如上面的 WebServlet 注解。

Filter

Servlet 中另一个常用的特性是 Filter,用于为所有 Servlet 添加全局性的鉴权和过滤,一个简单的示例如下:

import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;import java.io.PrintWriter;


@WebFilter(filterName = "HelloFilter", urlPatterns = {"/*"})public class HelloFilter implements Filter {

 

@Override

public void init(FilterConfig filterConfig) throws ServletException {

 

}

 

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

String passwd = servletRequest.getParameter("passwd");

if (passwd != null && passwd.equals("123456")) {

filterChain.doFilter(servletRequest, servletResponse);

} else {

try (PrintWriter out = servletResponse.getWriter()) {

out.println("passwd error!");

}

}

}

 

@Override

public void destroy() {

 

}}

其中同样使用了注解来添加,如果使用配置文件的方式,可以在 web.xml 中加上对应的 <filter>、<filter-mapping> 标签。这里有个比较值得注意的点事 urlPatterns 的匹配方式,一开始我以为是正则表达式,但其实在 Servlet 的标准中有明确的定义,比如优先进行精确匹配,否则使用最长的路径匹配,然后是后缀匹配等,规则定义在标准的 12 章 (Mapping Requests to Servlets) 中,可以配合容器的源码去进行分析。

另外 Servlet 标准中还支持注册 Listener 作为监听器,实现上下文、会话、请求的监听能力,主要用于线上人数的统计和会话的监控和处理。实现方法与 Servlet/Filter 大同小异,这里就不再举例了。

Servlet 的标准经过了多个版本的迭代,一些关键的版本和特性如下:

  • Java Servlet 2.0 (1997): JDK 1.1

  • Java Servlet 2.1 (1998): 第一个官方版本的标准,增加 RequestDispatcher和 ServletContext;

  • Java Servlet 2.2 (1999): 正式成为 J2EE 的一部分,引入 .war独立 web 应用格式;

  • Java Servlet 2.3 (2001): 增加 FilterAPI,即上文介绍的过滤器;

  • Java Servlet 2.5 (2005): 要求 Java SE 5,开始支持注解;

  • Java Servlet 3.0 (2009): 改善注解处理,引入了 @WebServlet、@WebFilter等注解支持部分参数化;支持文件上传;

  • Java Servlet 3.1 (2013): 支持非阻塞 I/O,Websocket;

  • Java Servlet 4.0 (2017): 支持 HTTP/2;

  • Jakarta Servlet 4.0.3 (2019): 捐赠给 Eclipse 开源基金,重命名 Java 商标;

  • Jakarta Servlet 5.0 (2020): 包名从 servlet转为 jakarta.servlet;

  • Jakarta Servlet 6.0 (2022): 增加了一些没人关心的特性;

完整的列表可以参考 Wikipedia 中的 Servlet API history 一节。

JSP

虽然我们可以用 Servlet 来生成网页中的动态内容,但这个过程相对繁琐。从上面的示例代码中可以看出,所有文本和标签都是硬编码,即使只做出微小的修改,也需要对源码进行重新编译和部署。JSP 解决了 Servlet 的这些问题,它是 Servlet 很好的补充,可以专门用作为用户呈现视图(View),而 Servlet 作为控制器(Controller)专门负责处理用户请求并转发或重定向到某个页面。

JSP 的全称是 Java Server Pages,包含一系列技术的组合,既表示动态网页的框架,也表示一种文件类型。JSP 的标准为 JSR 245,其中包含两个主要文档,分别是 JSP 的标准文档和 JSP EL 表达式的标准。

基本语法

JSP 的基本语法可以总结如下:

  • <% code %>scriptlet 可以用来包裹和执行 Java 代码,也可以用 <jsp:scriptlet> 标签来进行包含;

  • <%! declaration; [ declaration; ]+ ... %>用于变量声明,同 <jsp:declaration>;

  • <%= expr %>用来包括和执行表达式,表达式的结果作为 HTML 的内容,同 <jsp:expression>;

  • <%-- comment --%>为 JSP 注释,注释中的内容会被 JSP 引擎忽略;

  • <%@ directive attribute="value" %>指令,影响对应 Servlet 的类结构,后面细说;

  • <jsp:action_name attribute="value" />使用 XML 控制 Servlet 引擎的的行为,称为 action;

完整的语法可以参考本节末尾的 JSP Tutorial 或者相关教程。

JspServlet

JSP 作为一个脚本引擎,其本质上也是通过 Servlet 实现的,即 JSP 会被容器编译为对应的 Servlet 并加载。例如,下述简单的 index.jsp 文件:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


<h1> config = <%=config%> </h1>

在被 Tomcat 容器加载后会生成如下的 Java 文件:

package org.apache.jsp;


import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {

private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();

private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

private static final java.util.Set<java.lang.String> _jspx_imports_packages;

private static final java.util.Set<java.lang.String> _jspx_imports_classes;

static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}

private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}

public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}

public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}

public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}

public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}

public void _jspInit() {
}

public void _jspDestroy() {
}

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {

if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.String _jspx_method = request.getMethod();
if ("OPTIONS".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
return;
}
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET, POST or HEAD. Jasper also permits OPTIONS");
return;
}
}

final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;

try {
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;

out.write("\n");
out.write("<h1> config = ");
out.print(config);
out.write(" </h1>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}

代码有点长,其中有几个重点:

  • 生成的类名与 jsp 文件名相同,不合法的字符会转换为 _,比如jsp 会生成 index_jsp, 1.jsp 会生成 _1_jsp;

  • 生成的 Java 类继承自抽象类 HttpJspBase,这个类继承自HttpServlet;

  • _jspInit、_jspDestory对应 Servlet 中的生命周期函数;_jspService 中处理客户端的请求,JSP 中的 Java 代码也会转移到这里;

该类在第一次访问的时候才会生成、编译、加载,因此对于没访问过的页面是没有对应文件的。另外,在 _jspService 方法中为了方便 JSP 的代码编写,定义了几个常用的对象,比如:

  1. request,对应客户端请求的 HttpServletRequest对象;

  2. response,对应返回的 HttpServletResponse对象;

  3. session,对应 HttpSession,存储当前请求会话信息;

  4. application,对应 ServletContext,用于全局共享数据;

  5. config,对应 ServletConfig即 Servlet 的配置对象;

  6. page,为 Object类型,指向当前 Servlet 对象;

  7. out,即控制页面输出的 JspWriter;

  8. pageContext,当前页面共享数据对象;

这几个对象通常称为 JSP 的 “八大对象”,或者 “九大对象” (算上 exception 对象)。

标签库与 JSTL

前面介绍 JSP 语法的时候提到过 directive 的语法,即 <%@ directive attribute="value" %>。在 JSP 中有三种指令类型,分别是:

  1. <%@ page ... %>用于定义页面相关的属性,比如脚本语言类型、错误页面和缓存等;

  2. <%@ include ... %>用语言在脚本翻译阶段包含其他文件;

  3. <%@ taglib ... %>引入标签库的定义;

其中标签(tag)是指的是在 JSP 中一个可重用的动态组件,类似于 HTML 或者 XML 的标签,只不过多用于生成动态内容。taglib 就是一系列自定义标签的集合。JSP 支持让用户自定义标签,同时也提供了标准的标签库来方便开发,这个标准库就是 JSTL。JSTL 中常见的标签库有下面这些:

  • 核心标签库 (uri="http://java.sun.com/jsp/jstl/core”)

    • <c:out>- 输出字符串

    • <c:if>- 条件处理

    • <c:forEach>- 集合遍历

  • 格式化标签库 (uri="http://java.sun.com/jsp/jstl/fmt”)

    • <fmt:message>- 格式化并显示消息

    • <fmt:formatDate>- 格式化日期

  • SQL 标签库 (uri="http://java.sun.com/jsp/jstl/sql”)

    • <sql:setDataSource>- 指定数据源并使其可用于 SQL 操作

    • <sql:query>- 执行 SQL 查询并迭代结果集

    • <sql:update>- 通过执行 SQL 语句更新数据源

  • XML 标签库(uri="http://java.sun.com/jsp/jstl/xml”)

    • <x:parse>- 解析 XML 文档

    • <x:forEach>- 遍历 XML 文档

    • <x:out>- 输出结果

  • 函数标签库 (uri="http://java.sun.com/jsp/jstl/functions”)

关于这些标签库提供的完整功能可以参考官方文档 JavaServer Pages Standard Tag Library 1.1 Tag Reference。一个使用核心标签库的示例 JSP 如下所示:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>

<head><title>JSTL c:out Example</title></head>

<body>

<h2> <c:out value="1+1=${1+1}"/> </h2>

</body>

</html>

c:out 的输出类似于 <%= 的输出,只不过前者是针对表达式,关于表达式在后文中会另外进行介绍。

注意在 Tomcat 或者 Jetty 这种轻量级的容器内使用 JSTL 需要安装对应的依赖,可以参考 JSTL - Stackoverflow 中的相关介绍。

虽然在前后端分离的开发浪潮中 JSP 似乎已经日渐式微,但在网站快速原型开发中 JSP 还占有一席之地,而且许多历史项目也是基于 JSP 进行开发,因此对其了解还是有必要的。

参考资料:

Java 表达式语言

上节中我们说到 JSP 的标准中包含了两份文档,一份是 JSP 语言本身的标准,另一份则是表达式语言的标准。Java 表达式语言即 Expression Language,简称为 EL,最初就是为了在 JSTL 中使用便捷的表达式解析而出现的。虽然 JSP 中可以写 Java 代码,但这一方面对不懂 Java 语言的人不太友好,另一方面很多时候我们仅需要在 JSP 中执行比较、循环、加减乘除等简单操作,而引入完整的 Java 引擎显得有点 overkill 了。

在最初的版本中,Java EL 被称为 SPEL(Simplest Possible Expression Language) 或者 JUEL(Java Unified Expression Language),注意不要和 Spring 的 SpEL(Spring Expression Language) 搞混。EL 的语法被设计得和 javascript 类似,比如:

  • 无法强制类型转换,转换过程是隐式的;

  • 单引号和双引号等价;

  • property和 object['property'] 实现的效果基本相同;

  • 抽象属性的类型,property根据属性的类型不同,实际上可能会调用 object.get("property")、object.getProperty("property") 或者 object.getProperty() 等;

EL 的标准也经过了多个版本的迭代,在 EL 2.1 版本时,为了综合考虑 JSF 等技术的需要,对 EL 的功能进行了拓展,比如增加了延迟表达式(Deferred expressions),使得表达式不用在 JSP 翻译的过程就被执行;另外提供了 get/set 去获取/修改对象属性的能力,以及方法表达式支持调用 Java 对象的方法。

EL 3.0 中正式将 EL 的标准从 JSP/JSF 的标准中独立出来,编号为 JSR-341。这个版本中在 EL 中增加了类似 Java8 Stream 和 lambda 表达式的功能,几个简单的示例如下:

public class ELTestServlet extends HttpServlet {


@Override

public void doGet(HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException {

 

List list = new ArrayList<>();

list.add(Double.valueOf(1.0));

list.add(Double.valueOf(2.0));

list.add(Double.valueOf(3.0));

 

ELProcessor elp = new ELProcessor();

elp.defineBean("data", list);

 

Object message = (Double)elp.eval(

"n = data.stream().count(); s = data.stream().sum(); sq = data.stream().map(i -> i*i).sum(); Math.sqrt(sq/n - Math.pow(s/n, 2))");

res.getWriter().println(message);

 

message = (Double)elp.eval(

"n = 0; s = 0; sq = 0; data.stream().forEach(d -> (n = n + 1; s = s + d; sq = sq + d*d)); Math.sqrt(sq/n - Math.pow(s/n, 2))");

res.getWriter().println(message);

 

message = (Double)elp.eval(

"n = 0; s = 0; sq = data.stream().reduce(0, (a, i) -> (n = n + 1; s = s + i; a + i*i)); Math.sqrt(sq/n - Math.pow(s/n, 2))");

res.getWriter().println(message);

}}

目前最新的 EL 标准是 2020 年 4.0 版本,这个版本中将包名从 javax.el 移到了 jakarta.el。除了 Java EE 中定义的 EL 表达式标准和实现,在许多三方库中也实现了类似的表达式语言功能,包括但不限于:

  • OGNL: Object-Graph Navigation Language,一个开源的表达式语言实现,在许多其他项目中被使用,比如 Struts2、Spring Web Flow、MyBatis、FreeMarker 等;

  • MVEL: MVFLEX Expression Language,另一个开源的表达式语言实现,一些知名的 Java 项目使用其作为表达式引擎,比如 JBoss Drools、Hibernate ORM、JIRA 等;

  • SpEL: Spring 框架中使用的表达式语言,除了 Spring 宇宙中的组件,还有其他 Java 项目也是用了该引擎,比如 Thymeleaf、Apache Camel、Apache Sling 等;

  • JEXL: 即 Apache Commons JEXL(Java EXpression Language),参考 Java EL 和 Apache Velocity 实现的一个表达式语言,语法接近于 JavaScript 和 Shell 脚本;

这类表达式语言通常作为模版语言在 Web 页面中使用,如果其中的表达式能够被恶意输入所控制,就可能出现任意代码执行的危害。历史上出现过许多由于 EL 表达式注入导致的远程代码执行,因此这算是 Java 中高危的攻击面之一。

JDBC

在传统 MVC 架构中,将内容呈现的过程分成模型(Model),视图(View),控制器(Controller)三个部分,现在我们已经介绍了控制器部分的 Servlet,视图层的 JSP,为了使得一个网页能够真正有价值,还缺少其中关键的模型部分,即数据来源。

JDBC (Java Database Connectivity) 就是 Java 访问数据库的一个标准。历史上在 JDK 1.1 中 JDBC 就已经是其中的一部分,也就是说 JDBC 本身也是 Java SE 的标准,但也包含在 Java EE 中。其包名可通过 java.sql 或者 javax.sql 进行访问。

JDBC 标准现在由 JCP 维护,主要经过以下几个版本的迭代:

不同版本的 JDBC 主要引进了一些新的特性,比如 JDBC 4.0 支持自动加载数据库驱动,4.1 支持 try-with-resource,大对象(LOB)更新等;主要的框架并没有太大变化。

JDBC API 的常见用法如下:

import java.sql.*;


 

public class TestJdbc {

public static void main(String[] args) throws Exception {

// Class.forName("com.mysql.jdbc.Driver");

// 1. Establishing the connection with the database Connection conn = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/mydb?useSSL=false", "root", "rxhtemp");

 

// 2. Creating a statement object to execute a query Statement stmt = conn.createStatement();

 

// 3. Executing the query and getting the ResultSet ResultSet rs = stmt.executeQuery("SELECT * FROM user");

 

// 4. Processing the ResultSet while (rs.next()) {

int id = rs.getInt("id");

String name = rs.getString("name");

System.out.println("ID: " + id + ", Name: " + name);

}

 

// 5. Closing the database resources rs.close();

stmt.close();

conn.close();

}

}


友情链接:

返回软协官网首页

通讯地址:北京市海淀区海淀南路甲21号中关村知识产权大厦A座2层206、207室     邮政编码:100080

电话:010-62565314 刘莉    京ICP证16064523号-2    版权所有:北京软件和信息服务业协会

技术支持:中科服    内容支持:鑫网安

你知道你的Internet Explorer是过时了吗?

为了得到我们网站最好的体验效果,我们建议您升级到最新版本的Internet Explorer或选择另一个web浏览器.一个列表最流行的web浏览器在下面可以找到.