安全性和类载入程序体系结构

1/5/2008来源:Java教程人气:8105


  ─ 类载入程序在JVM整体安全模式中的作用

--------------------------------------------------------------------------------

摘 要: java 技 术 之 所 以 适 用 于 网 络 是 因 为 它 有 完 备 的、 设 计 到 其 结 构 中 的 安 全 模 式。 本 文 先 讨 论Java 沙 箱 中 的 创 新 程 序, 然 后 讨 论 安 全 模 式 中Java 虚 拟 机(JVM) 的 类 载 入 程 序 结 构。 类 载 入 程 序 结 构 能 将 从 不 同 来 源 下 载 的 代 码 隔 离 开 来, 并 防 止 载 入 那 些 看 似 是 某 个 已 被 确 认 的 库 的 一 部 分 而 实 际 上 并 未 获 得 确 认 的 类。

沙 箱 刷 新 程 序
Java 安 全 模 式 的 重 点 在 于 保 护 最 终 用 户 不 受 从 网 上 下 载 的 破 坏 性 程 序 的 干 扰。 为 达 到 这 个 目 的,Java 提 供 了 一 个 专 用 的 运 行Java 程 序 的 沙 箱。Java 程 序 在 它 的 沙 箱 内 可 做 任 何 事 情, 但 出 此 边 界 就 不 能 有 任 何 操 作。 例 如, 未 经 确 认 的Java Applet 的 沙 箱 禁 止 许 多 操 作, 其 中 包 括:

禁 止 对 本 地 磁 盘 的 读 写;

除 了 下 载 此Applet 的 主 机 外 不 能 与 任 何 别 的 主 机 连 接;

禁 止 建 立 一 个 新 的 进 程;

禁 止 载 入 一 个 直 接 调 用 本 地 方 法 的 新 的 动 态 库。
通 过 限 定 下 载 代 码 的 可 执 行 操 作 的 范 围,Java 安 全 模 式 可 使 用 户 免 受 破 坏 性 程 序 的 威 胁。

类 载 入 程 序 体 系 结 构
在 安 全 沙 箱 中JVM 的 一 个 重 要 方 面 是 其 类 载 入 程 序 结 构。 在JVM 中, 类 载 入 程 序 负 责 输 入 那 些 定 义 运 行 程 序 的 类 和 接 口 的 二 进 制 数 据。 在 图1 中 只 有 一 块 被 标 记 为“ 类 载 入 程 序”, 但 事 实 上, 在JVM 内 部 可 能 有 多 个 类 载 入 程 序。 可 以 说, 图 中 的 类 载 入 程 序 实 际 代 表 了 一 个 可 能 涉 及 许 多 类 载 入 程 序 的 系 统。JVM 有 非 常 灵 活 的 类 载 入 结 构, 它 允 许Java 应 用 程 序 自 己 定 义 装 载 类 的 方 式。



图1 Java类载入程序体系结构
Java 应 用 能 用 二 种 类 载 入 程 序:“ 原 始 的” 类 载 入 程 序 和 类 载 入 程 序 对 象。 原 始 的 类 载 入 程 序( 只 有 一 个) 是JVM 的 一 部 分。 例 如, 如 果JVM 在 某 个 操 作 系 统 上 作 为C 程 序 被 启 用, 那 么 原 始 的 类 载 入 程 序 就 是 那 个C 程 序 的 一 部 分。 原 始 的 类 载 入 程 序 装 载 获 得 确 认 的 类, 其 中 包 括Java API 类, 它 们 通 常 取 自 本 地 的 硬 盘。

在 程 序 运 行 时Java 应 用 装 入 类 载 入 程 序 对 象, 它 们 能 以 自 定 义 的 方 式 载 入 类, 例 如 通 过 网 络 来 下 载 类 文 件。JVM 认 为 它 用 原 始 的 类 载 入 程 序 装 入 的 任 何 一 个 类 都 是 已 经 确 认 的, 不 管 它 是 否 是Java API 的 一 部 分。 然 而 它 对 那 些 通 过 类 载 入 对 象 装 入 的 类 则 持 另 一 种 态 度。 在 默 认 的 情 况 下 它 认 为 它 们 是 未 获 确 认 的。 虽 然 原 始 的 类 载 入 程 序 是 虚 拟 机 运 行 的 本 质 部 分, 但 类 载 入 对 象 不 是。 恰 恰 相 反, 类 载 入 程 序 是 用Java 写 的, 编 译 成 类 文 件, 载 入 虚 拟 机 中, 然 后 象 别 的 对 象 一 样 被 实 例 化。 实 际 上 它 们 只 是 一 个 运 行 中 的 程 序 的 部 分 执 行 代 码。 图2 描 述 了 这 种 结 构。



图2 Java类载入程序体系结构
因 为 有 了 类 载 入 程 序 对 象, 在 编 译 时 你 不 必 知 道 都 有 哪 些 类 最 终 加 入 了Java 应 用 程 序。 这 样, 你 能 在 运 行 时 动 态 地 扩 展Java 应 用 程 序。 当 你 运 行 应 用 程 序 时, 它 能 判 断 需 要 别 的 什 么 类, 然 后 通 过 一 个 或 多 个 类 载 入 程 序 对 象 来 装 入 它 们。 因 为 你 是 用Java 编 写 类 载 入 程 序 的, 所 以 你 能 用 任 何 方 式 安 装 类: 可 通 过 网 络 下 载, 从 某 些 数 据 库 中 取 得, 甚 至 在 乘 飞 机 时 把 它 算 出 来。

类 载 入 程 序 和 命 名 空 间
JVM 对 每 个 它 所 载 入 的 类 都 记 下 了 是 用 哪 种 类 载 入 程 序 装 入 的, 当 一 个 被 载 入 的 程 序 引 用 另 一 个 类 时, 虚 拟 机 要 求 用 同 一 个 类 载 入 程 序 装 入 被 引 入 的 类。 如 果 虚 拟 机 用 某 一 个 类 载 入 程 序 装 入 了Volcano 类, 它 将 用 同 样 的 类 载 入 程 序 来 装 入Volcano 类 引 用 的 所 有 类。 如 果Volcano 引 用 了 一 个 叫Java 的 类, 也 许 调 用 了Java 类 的 方 法, 虚 拟 机 就 会 向 装 入Volcano 类 的 载 入 程 序 要 求 获 得Java 类。 载 入 程 序 返 回 的Java 类 与Volcano 类 是 动 态 链 接 的。

因 为JVM 用 这 种 方 法 装 载 类, 在 默 认 条 件 下 某 个 类 只 能 看 见 用 同 一 个 类 载 入 程 序 装 入 的 其 它 类。Java 体 系 结 构 用 这 种 方 法 在 单 个Java 应 用 中 建 立 多 个 命 名 空 间。 命 名 空 间 是 一 些 由 特 定 的 类 载 入 程 序 装 入 的 类 的 独 一 无 二 的 名 字 集 合。JVM 为 每 个 类 载 入 程 序 维 护 一 个 命 名 空 间, 所 有 由 该 类 载 入 程 序 装 入 的 类 的 名 字 组 成 了 这 个 命 名 空 间。

例 如: 一 旦 某 个JVM 把 一 个 叫Volcano 的 类 装 入 到 某 一 特 定 的 命 名 空 间 后, 就 不 能 再 把 别 一Volcano 类 装 入 那 个 命 名 空 间。 然 而 你 可 以 把 多 个Volcano 类 装 入JVM, 因 为 你 只 要 建 立 多 个 类 载 入 程 序 就 能 在 某 个Java 应 用 中 建 立 多 个 命 名 空 间。 如 果 你 在 某 个 运 行 着 的Java 应 用 中 建 立 了 三 个 单 独 的 命 名 空 间( 三 个 类 载 入 程 序 每 个 载 入 程 序 一 个), 那 么 给 每 个 命 名 空 间 装 入 一 个Volcano 类, 你 的 应 用 中 就 有 三 个 不 同 的Volcano。

Java 应 用 能 使 多 个 类 载 入 程 序 对 象 实 例 化, 不 管 它 是 否 来 自 同 一 个 类。 因 此 它 能 根 据 需 要 建 立 多 个 类 载 入 程 序 对 象。 用 不 同 的 类 载 入 程 序 装 入 的 类 在 不 同 的 命 名 空 间 中, 并 且 除 非 明 确 许 可 外 都 不 能 互 相 访 问。 当 你 开 发Java 应 用 时, 你 可 以 把 从 不 同 来 源 载 入 的 类 隔 离 到 不 同 的 命 名 空 间 中。 这 样 用Java 的 类 载 入 程 序 体 系 结 构 就 可 控 制 不 同 来 源 的 代 码 间 的 访 问, 你 可 以 防 止 破 坏 性 代 码 的 访 问 正 常 的 代 码。

Applet 的 类 载 入 程 序
Web 浏 览 器 是 用 类 载 入 程 序 进 行 动 态 扩 展 的 一 个 例 子, 它 用 类 载 入 程 序 对 象 从 网 上 为 某 个applet 下 载 类 文 件。Web 浏 览 器 启 动 一 个 装 入 类 载 入 程 序 对 象( 通 常 称 作applet 类 载 入 程 序) 的Java 应 用, 这 种 类 载 入 程 序 对 象 知 道 怎 样 从HTTP 服 务 器 获 得 类 文 件。Applet 是 动 态 扩 展 的 一 个 例 子, 因 为 当Java 应 用 启 动 时, 它 并 不 知 道 浏 览 器 会 要 它 从 网 上 下 载 哪 些 类 文 件。 要 下 载 的 类 文 件 是 在 运 行 中 浏 览 器 遇 上 含 有Java applet 的 网 页 时 决 定 的。

Web 浏 览 器 启 动 的Java 应 用 通 常 为 它 索 取 类 文 件 的 网 上 站 点 建 立 不 同 的applet 类 载 入 程 序 对 象。 因 而 不 同 来 源 的 类 文 件 由 不 同 的 类 载 入 程 序 对 象 装 入, 并 被 放 入 主Java 应 用 内 不 同 的 命 名 空 间 中。 因 为 不 同 来 源 的applet 类 文 件 放 在 隔 离 开 的 命 名 空 间 中, 所 以 破 坏 性 的 代 码 就 不 能 与 从 其 他 来 源 下 载 的 代 码 直 接 接 触。

类 载 入 程 序 间 的 合 作
通 常 情 况 下, 类 载 入 程 序 对 象 都 是 互 相 依 赖 以 完 成 各 自 遇 到 的 类 载 入 需 求, 至 少 它 们 都 依 赖 于 原 始 的 类 载 入 程 序。 例 如, 假 设 你 写 了 个Java 应 用, 它 安 装 了 一 个 通 过 从 网 上 下 载 类 文 件 来 获 取 类 的 载 入 程 序。 假 定 在 运 行Java 应 用 期 间 要 求 你 的 类 载 入 程 序 装 入 一 个 叫Volcano 的 类。 一 种 实 现 方 法 是, 首 先 让 原 始 的 类 载 入 程 序 在 已 经 确 认 的 类 库 中 寻 找 并 装 入 该 类。 由 于Volcano 不 是Java API 的 一 部 分, 那 么 原 始 的 类 载 入 程 序 就 找 不 到 它, 这 样 你 的 类 载 入 程 序 就 会 用 它 自 定 义 的 方 式 从 网 上 下 载Volcano 类, 假 定 你 的 类 载 入 程 序 能 下 载Volcano 类, 那 么 它 就 可 以 在 将 来 的 应 用 程 序 的 执 行 中 发 挥 作 用。

下 面 继 续 同 一 个 例 子, 假 定 一 段 时 间 后Volcano 类 的 某 个 方 法 第 一 次 被 调 用, 它 引 用 了Java API 中 的String 类, 由 于 这 是 第 一 次 调 用, 虚 拟 机 要 求 你 的 类 载 入 程 序( 载 入Volcano 的 那 个) 装 入String 类。 和 前 面 一 样, 你 的 类 载 入 程 序 先 把 要 求 传 递 给 原 始 的 类 载 入 程 序, 不 过 这 一 次 原 始 的 类 载 入 程 序 能 直 接 返 回String 类, 因 为String 类 是 很 基 本 的 类, 它 肯 定 已 被 用 过, 因 此 也 已 被 装 入 了。 大 多 数 情 况 下 原 始 的 类 载 入 程 序 会 返 回 从 正 获 确 认 中 预 先 装 入 的String 类。 这 样, 类 载 入 程 序 就 不 会 再 从 网 上 下 载 它, 而 只 是 把 原 始 的 类 载 入 程 序 返 回 的 类 传 递 给 虚 拟 机。 以 后 不 管 什 么 时 候Volcano 类 要 引 用String 类, 虚 拟 机 都 会 调 用 已 载 入 的 这 个 类。

沙 箱 中 的 类 载 入 程 序
在Java 沙 箱 中, 类 载 入 程 序 体 系 结 构 是 阻 挡 破 坏 性 代 码 的 第 一 道 防 线。 不 过 也 正 是 类 载 入 程 序 把 可 能 造 成 破 坏 的 代 码 带 进JVM 中。

类 载 入 程 序 体 系 结 构 对Java 水 箱 的 作 用 有:

它 阻 止 了 破 坏 性 代 码 干 扰 良 好 的 代 码。

它 保 护 已 获 得 确 认 的 类 库。
类 载 入 程 序 结 构 通 过 识 别 类 是 否 获 得 确 认 来 保 护 类 库。 如 果 某 个 破 坏 性 的 类 能 成 功 地 欺 骗JVM, 使JVM 相 信 它 是 来 自 于Java API 的 已 获 确 认 的 类, 那 么 它 就 会 突 破 沙 箱 的 防 线。 而 类 载 入 程 序 结 构 能 防 止 未 获 确 认 的 类 模 仿 已 获 得 确 认 的 类, 这 种 预 防 办 法 保 障 了Java 运 行 时 的 安 全。

命 名 空 间 和 保 护 屏
类 载 入 程 序 结 构 给 不 同 的 类 载 入 程 序 装 入 的 类 提 供 了 受 保 护 的 命 名 空 间, 这 阻 止 了 破 坏 性 的 代 码 干 扰 正 常 的 代 码。 前 面 提 到 过, 命 名 空 间 是JVM 维 护 的 被 载 入 类 的 名 字 集 合。

命 名 空 间 有 助 于 安 全 性 是 因 为 你 能 在 不 同 命 名 空 间 的 类 间 放 置 保 护 屏。 在JVM 内 部, 同 一 个 命 名 空 间 中 的 类 能 直 接 地 互 相 交 互, 但 不 同 命 名 间 中 的 类 甚 至 不 知 道 对 方 的 存 在, 除 非 明 确 地 提 供 允 许 访 问 的 机 制。 如 果 某 个 破 坏 性 的 类 被 装 入 后 获 得 了 对 目 前 其 它 装 入 类 的 访 问 权, 那 这 个 类 就 有 可 能 知 道 它 不 该 知 道 的 东 西, 或 有 可 能 干 扰 你 的 程 序 运 行。

建 立 一 个 安 全 的 环 境
当 你 写 一 个 使 用 类 载 入 程 序 的 应 用 时, 你 就 建 立 了 一 个 运 行 被 动 态 装 入 的 代 码 的 环 境。 如 果 你 想 让 它 没 有 漏 洞, 在 编 写 应 用 和 类 载 入 程 序 时 必 须 遵 守 一 些 规 则。 总 的 来 说 要 隔 离 破 坏 性 的 代 码 和 正 常 的 代 码, 并 象 保 护Java API 一 样 保 护 获 得 确 认 的 类 库。

命 名 空 间 和 代 码 来 源
为 了 发 挥 命 名 空 间 对 安 全 性 的 作 用, 你 要 确 保 用 不 同 的 类 载 入 程 序 从 不 同 的 来 源 装 入 类。 这 就 是 支 持Java 的Web 浏 览 器 所 采 用 的 体 制。Web 浏 览 器 所 启 动 的Java 应 用 通 常 为 它 从 网 上 下 载 类 的 每 个 来 源 建 立 不 同 的applet 类 载 入 程 序 对 象。 例 如, 某 个 浏 览 器 用 一 个 类 载 入 程 序 对 象 从http://www.riscapplets.com 下 载 类, 而 用 另 一 个 从http://www.meanapplets.com 下 载 类。

保 护 受 限 制 的 包
Java 允 许 同 一 个 包 中 的 类 互 相 授 予 某 些 访 问 权 利, 但 不 能 对 包 外 的 类 授 予 访 问 权。 因 此, 如 果 你 的 类 载 入 程 序 接 到 请 求, 要 装 入 一 个 从 名 字 上 看 似 是Java API 一 部 分 的 类( 如 名 为Java.lang.virus 的 类), 它 就 要 小 心 处 理 这 一 请 示。 如 果 这 样 的 类 被 装 入 的 话, 它 就 有 权 访 问Java.lang 这 个 已 获 确 认 的 类 库, 从 而 有 可 能 搞 破 坏。

因 此, 你 应 该 正 式 地 写 这 样 一 个 类 载 入 程 序, 它 能 很 简 单 地 阻 绝 装 入 那 些 看 似 是Java API 一 部 分( 或 任 何 其 它 已 获 信 任 的 库), 却 不 在 本 地 已 获 确 认 的 库 中 的 类。 换 名 说, 当 你 的 类 载 入 程 序 把 请 示 传 递 给 原 始 的 类 载 入 程 序 后, 后 者 表 示 它 不 能 装 入 这 个 类, 你 的 类 载 入 程 序 就 应 查 看 一 下 这 个 类 有 没 有 声 明 为 某 个 已 获 确 认 的 包 中 的 一 员。 如 果 它 声 明 了, 那 你 的 类 载 入 程 序 就 应 该 发 出 安 全 性 异 常 消 息, 而 不 是 从 网 上 下 载 它。

保 护 被 禁 止 的 包
此 外, 你 也 许 在 获 得 信 任 的 库 中 安 装 了 一 些 包, 你 只 希 望 你 的 应 用 通 过 原 始 的 类 载 入 程 序 来 装 入 其 中 的 类, 而 不 想 让 那 些 你 的 类 载 入 程 序 装 入 的 类 有 访 问 权 利。 例 如, 假 定 你 已 经 建 立 了 一 个 叫absolutepower 的 包, 并 把 它 装 到 原 始 的 类 载 入 程 序 有 权 访 问 的 本 地 库 中, 再 假 定 你 不 希 望 自 己 的 类 载 入 程 序 装 入 的 类 能 装 入absolutepower 包 中 的 任 何 一 个 类。 在 这 种 情 况 下, 你 所 写 的 类 载 入 程 序 要 做 的 第 一 件 事 是 确 保 所 需 要 的 类 没 有 声 明 是absolutepower 包 中 的 一 员。 如 果 这 样 的 类 有 需 求 的 话, 你 的 类 载 入 程 序 就 应 发 出 安 全 性 异 常 消 息 , 而 不 是 把 类 名 传 给 原 始 的 类 载 入 程 序。

类 载 入 程 序 区 分 某 个 类 是 来 自 受 限 制 的 包 还 是 来 自 被 禁 止 的 包 的 唯 一 办 法 是 看 它 的 名 字。 因 此 必 须 给 类 载 入 程 序 一 张 受 限 制 的 和 被 禁 止 的 包 的 名 单。 因 为 类 名java.lang.virus 表 示 它 是 来 自java.lang 的 包, 而java.lang 是 受 限 制 的 包, 所 以 如 果 原 始 的 类 载 入 程 序 不 能 载 入 它 的 话, 你 的 类 载 入 程 序 就 应 该 发 出 一 条 安 全 异 常 消 息。 因 为 类 名absolutepwer.FaneyClassloader 说 明 它 属 于 被 禁 止 的 包absolutepwer, 你 的 类 载 入 程 序 也 应 该 发 出 安 全 异 常 消 息。

注 重 安 全 性 的 类 载 入 程 序
写 注 重 安 全 性 的 类 载 入 程 序 的 方 法 一 般 是 用 以 下 四 步:

如 果 有 类 载 入 程 序 无 权 访 问 的 类, 那 么 类 载 入 程 序 就 检 查 需 要 的 类 是 否 属 于 上 面 提 到 的 被 禁 止 的 包。 如 果 是, 就 发 一 条 安 全 性 异 常 消 息, 如 果 不 是 则 继 续 第 二 步;

类 载 入 程 序 把 要 求 转 给 原 始 的 类 载 入 程 序, 如 果 它 成 功 地 返 回 了 类, 类 载 入 程 序 就 返 回 它, 否 则 的 话 继 续 第 三 步;

如 果 有 获 得 信 任 的 包 禁 止 类 载 入 程 序 加 入 这 个 类, 那 么 类 载 入 程 序 就 检 查 所 要 求 的 类 是 否 属 于 受 限 制 的 包, 如 果 是, 它 就 发 安 全 异 常 消 息, 否 则 继 续 第 四 步;

类 载 入 程 序 最 后 试 着 用 自 定 义 的 方 法 装 入 类, 例 如 从 网 上 下 载。 如 果 成 功 的 话 就 返 回 这 个 类, 否 则 报 告 有“ 没 有 发 现 类 定 义” 的 错 误。
类 载 入 程 序 执 行 上 述 第 一 步 和 第 三 步 保 护 了 已 获 确 认 的 包。 第 一 步 彻 底 防 止 了 装 入 被 禁 止 的 包 中 的 类, 第 三 步 禁 止 未 获 得 确 认 的 类 把 它 自 己 添 加 到 已 获 信 任 的 包 中。

结 论
类 载 入 程 序 的 体 系 结 构 用 二 种 方 法 帮 助 建 立Java 的 安 全 模 式:

把 代 码 分 离 到 不 同 的 命 名 空 间 并 在 不 同 命 名 空 间 的 代 码 间 设 置 保 护 屏;

保 护 象Java API 这 样 已 获 确 认 的 库。
为 了 发 挥Java 类 载 入 程 序 体 系 结 构 在 安 全 性 方 面 的 作 用, 程 序 员 们 必 须 正 确 使 用 它 的 上 述 二 种 功 能。 为 利 用 命 名 空 间 所 形 成 的 保 护, 不 同 来 源 的 代 码 应 用 不 同 的 类 载 入 程 序 对 象 来 装 入; 为 利 用 受 信 任 的 包 得 到 的 保 护, 编 写 的 类 载 入 程 序 必 须 对 照 受 限 制 的 和 被 禁 止 的 包 的 名 单 来 检 查 所 需 求 的 类 的 命 名。