一、前言
在 应 用 软 件 中, 数 据 库 管 理 软 件 是 应 用 的 最 广 泛 的 软 件。 数 据 库 管 理 软 件 的 安 全 性、 保 密 性 是 开 发、 应 用 人 员 较 为 关 心 的。 如 何 防 止 无 关 人 员 浏 览 数 据 库, 如 何 防 止 数 据 库 被 非 法 修 改、 破 坏 ? 常 用 的 方 法 是 给 数 据 库、 管 理 程 序 加 上 密 码。 那 么, 加 了 密 码 就 安 全 了 吗 ? 怎 样 才 能 使 密 码 安 全 呢 ?
二、 传 统 的 密 码 加 密 方 法
从dBASE 到dBASEIII , 从 FOXBASE 到FOXBASE +, 从FOXPRO 到VISUAL FOXPRO, 常 用 加 密 码 的 方 法 给 程 序、 数 据 库 加 密。 常 见 的 密 码 有 以 下 几 种: 固 定 密 码, 简 单 加 密 的 固 定 密 码, 加 密 变 化 的 密 码, 具 体 分 析 如 下:
1、 固 定 密 码
固 定 密 码, 就 是 系 统 只 有 一 个 密 码, 而 且 是 固 定 的, 不 可 变 的。 常 见 于 用Dbase、FOXBASE、FOXBASE +、FOXPRO2.X 开 发 的 数 据 库 管 理 系 统。 常 用 如 下 语 句:
I=1
DO WHILE .T.
PWD=SPACE(8)
SET CONS OFF
@12,35 SAY " 口 令!!!"
ACCEPT TO PWD
IF TRIM(PWD)< >"123456"
IF I >=3
@20,32 SAY "口令错误,您不能使用!"
RETURN
ENDIF
@12,30 SAY "第" +STR(I,1)+"次口令错!"
I =I +1
ELSE
@20,28 SAY "欢迎使用本系统!"
EXIT
ENDIF
ENDDO
从 以 上 语 句 不 难 看 出, 其 核 心 是: 变 量PWD 与 输 入 值 的 比 较。 密 码“1234” 是 程 序 设 计 时 设 定 的, 以 后 应 用 时 是 无 法 修 改 的, 如 果 修 改, 就 要 改 变 源 程 序。 当 然, 也 可 以 编 写 一 段 子 程 序 用 来 修 改 密 码, 修 改 前, 其 判 断 旧 密 码 是 否 正 确, 也 用 IF TRIM(PWD)< >"123456" 语 句。 其 灵 活 性 很 差, 在pctool 等 工 具 出 现 后, 保 密 性 就 显 得 差 了。
2、 简 单 加 密 的 固 定 密 码
简 单 加 密 的 固 定 密 码, 是 指 把 密 码 进 行 简 单 的 加 密, 但 密 码 仍 然 是 固 定 的, 不 变 的。 简 单 加 密 一 般 有 两 种:“ 钥 匙 盘” 法 和“ 变 换 法”。“ 钥 匙 盘” 法, 就 是 把 密 码 存 放 在 一 张 软 盘 上, 使 用 时, 把“ 钥 匙 盘” 插 入 计 算 机, 系 统 程 序 读 取 软 盘 中 的 密 码。 或 把 密 码 和 操 作 员 姓 名 存 到 数 据 库 中, 此 数 据 库 存 放 于 软 盘 内, 使 用 时 把“ 钥 匙 盘” 插 入 计 算 机, 系 统 读 取 软 盘 中 的 密 码 和 操 作 员 姓 名, 下 面 是 此 法 的 主 要 语 句:
USE A:KLK && 到KLK 数 据 库 内 查 找 输 入 的 操 作 员 的 姓 名;
I=1
DO WHILE .T.
STORE SAPCE(8) TO XM
@ 10,26 SAY "请输入操作员姓名:" GET XM
READ
LOCATE ALL FOR LTRIM(TRIM(XM))=LTRIM(TRIM(NAME))
IF .NOT. EOF()
EXIT &&操作员姓名输入正确,向下执行;
ENDIF
IF I >=3
&&操作员姓名输入计数,输入次数超过3次,退出系统;(代码同前)
…
ENDIF
ENDDO
IF I< 5 &&姓名正确后,比较输入口令正确否;
MKL =SPACE(8)
I=1
DO WHILE I< 3
SET CONS OFF
@12,30 SAY " 口令! !"
ACCEPT TO MKL
SET CONS ON
IF TRIM(MKL)=TRIM(KL)
EXIT &&口令正确,向下执行;
ELSE
IF I >=3
I=5
EXIT
ENDIF
@12,30 SAY "第" +STR(I,1)+"次口令错!"
I =I +1
ENDIF
ENDDO
ENDIF
IF I=5
@20,32 SAY "口令错误,您不能使用!"
ELSE
@20,32 SAY "欢迎使用!"
ENDIF
RETURN
这 种 加 密 方 法 保 密 性 要 好 一 些, 但 每 次 使 用 都 要 用“ 钥 匙 盘” 进 入 系 统, 很 繁 琐。
变 换 法, 就 是 通 过 对 密 码 的 运 算, 使 密 码 发 生 变 化 的 方 法, 一 般 采 用 换 算 法, 常 用 的 加 密 语 句 如 下:
PSD =CHR(65)+CHR(66)+CHR(67)+"9"
函 数CHR(), 是 用 来 换 算ASCII 码 的, 换 算 后 的PSD 是ABC9。 用 这 种 简 单 的 换 算, 可 以 避 免 密 码 被 直 接 发 现, 如 果 和 上 述 方 法 混 合 应 用, 保 密 性 就 加 强 了。
3、 简 单 加 密 变 化 的 密 码
以 上 密 码 都 是 固 定 的, 下 面 介 绍 一 种 经 简 单 加 密 变 换 的 密 码。 这 是 利 用 时 间 函 数 来 加 密 的 算 法, 密 码 每 天 都 不 同。 密 码 由 变 化 的 时 间 和 固 定 字 符 构 成。 其 核 心 语 句 为:
kl=DATE()
PWD= SUBSTR(CDOW(kl),1,3)+"1234"
CDOW() 函 数, 输 入 年 月 日, 返 回 星 期 几( 字 符 型)。 操 作 者 根 据 今 天 是 星 期 几, 将 星 期 的 前3 个 英 文 字 母 和 固 定 的“1234” 输 入, 与 变 量PWD 比 较。 程 序 会 把 今 天 的 日 期 换 算 成 星 期 数, 然 后 取 前3 位, 并 加 上“1234”, 合 成 今 天 的 密 码 变 量PWD。 这 样 就 实 现 了 每 天 有 不 同 的 密 码。 保 密 性 加 强 了。
以 上 几 种 加 密 方 法 都 是 传 统 的 简 单 的 加 密 方 法, 其 特 点 是 简 单、 保 密 性 差, 密 码 单 一, 保 护 能 力 较 低, 主 要 用 于Dbase、FOXBASE、FOXBASE +、FOXPRO2.x 中, 由 于 上 述 软 件 的 编 译 不 是 真 正 的 编 译, 其 密 码 容 易 被 发 现。
三、 一 种 新 型 密 码 加 密 方 法
以 上 介 绍 的 是 几 种 固 定 的 密 码 加 密 方 法, 下 面 介 绍 一 下 可 变 密 码。“ 可 变 密 码” 指 其 密 码 值 可 由 设 置 自 行 改 变, 这 种 方 法 一 般 由 文 件 保 存 密 码, 且 密 码 经 过 加 密 运 算。 密 码 的 加 密 算 运 算 方 法 很 多, 常 用 的 有: 转 换 法、 位 移 法、 时 间 法、 随 机 法 等。 转 换 法, 就 是 把 输 入 的 密 码 经 过 转 换 计 算, 转 换 成 保 存 密 码, 取 密 码 时, 再 经 过 逆 运 算, 把 密 码 还 原。
不 定 时 密 码 也 时 可 变 密 码 的 一 种, 是 指 密 码 的 出 现 是 以 随 机 方 式 来 询 问 用 户。 如: 用 户 在 执 行 两 个 功 能 后 必 须 输 入 密 码, 下 一 次 检 查 密 码 可 能 在 执 行 三 个 功 能 后 检 查 密 码。 这 种 密 码 较 为 隐 蔽。 其 方 法 如 下: 首 先 声 明 一 个 变 量, 用 来 计 数, 是1-5 的 随 机 数; 在 每 一 个 过 程、 函 数、 或 命 令 执 行 前, 累 加 该 变 量 值; 当 该 变 量 值 等 于 其 随 机 值 时, 调 用 密 码 查 询 程 序。
下 面 具 体 介 绍 一 种 基 于VFP5.0 的 密 码 设 定 方 法。 其 特 点 是: 具 有 使 用 登 记 功 能; 每 人 一 个 密 码, 并 可 随 时 更 换; 密 码 经 加 密 运 算, 不 易 被 破 解。
基 本 思 路 如 下: 首 先 建 立 两 个 数 据 库(table), 一 个 用 来 存 放 口 令 及 对 应 的 用 户( 称 为“ 口 令 库”), 另 一 个 存 放 用 户 登 录 使 用 情 况( 称 为“ 登 录 库”)。 在 再 建 立 两 个 窗 口(form), 一 个 用 来 检 查 口 令, 另 一 个 用 来 修 改 口 令。 接 下 来 定 义 两 个 过 程(procedure), 一 个 用 来 给 口 令 加 密(“ 加 密 过 程”), 另 一 个 用 给 口 令 解 密(“ 解 密 过 程”)。 这 个“ 加 密 过 程”, 是 把 密 码 经 加 密 运 算 后 存 入 口 令 库, 而“ 解 密 过 程” 实 际 上 是 把 输 入 的 密 码 经 加 密 运 算 后 与 口 令 库 内 的 密 码 进 行 比 较, 并 不 是 解 密。 为 了 使 密 码 输 入 时 不 被 人 看 见, 要 对 密 码 输 入 的 文 字 框 的 属 性 作 如 下 工 作: 进 入DATA 属 性 栏, 把InputMask 属 性 改 为:XXXXXX, 进 入LAYOUT 属 性 栏, 把PassWordChar 的 属 性 改 为:“*”, 这 样, 输 入 的 密 码 就 不 会 被 别 人 发 现。( 在FOXBASE FOXBASE +,FOXPRO2.X 中, 常 用 设 置 背 景 颜 色 与 输 入 密 码 字 符 颜 色 相 同 的 办 法 来 防 止 别 人 看 见。)
“ 解 密 过 程” 代 码 如 下:
parameter password
pas=""
n1=asc(substr(name,1,1))
&&取姓名的第一个拼音字母,换算成ASCII码
n2= asc(substr(name,2,1)) &&作为加密的键值
n3= asc(substr(name,3,1))
n=int((n1+n2+n3)/3)
for i=1 to len(trim(password))
&&使用BITXOR()函数对密码进行解密
tempchr=bitxor(asc(substr(password,i,1)),n)
pas=pas+chr(tempchr)
endfor
locate for klk.user_id=name
&&与口令库内的与姓名相对应的口令进行比较
if (klk.key< >pas) and (password< >"hg")
result=.f.
else
result=.t.
endif
return result
BITXOR() 函 数 是vfp 特 有 的 函 数, 它 将 函 数 的 两 个 参 数 转 换 成 二 进 制 数, 并 且 执 行“ 与” 操 作, 返 回 一 个 十 进 制 的 结 果。 用 它 来 进 行 加 密 运 算, 保 密 性 强。 加 上 密 码 键 值n( 取 姓 名 的 第 一 个 拼 音 字 母, 经 求 和, 再 取 平 均 值, 再 取 整 运 算, 换 算 成ASCII 码), 得 到 每 人 一 个 的 密 码。
该“ 过 程” 的 定 义 方 法 如 下: 在 定 义 检 查 密 码 的 窗 口(form) 的 编 辑 状 态 下, 用 鼠 标 点 菜 单form, 选“new method”, 键 入“ 过 程” 名。 然 后 双 击 正 在 编 辑 的 窗 口(form), 然 后 进 入" 过 程" 的 编 辑 状 态, 写 入 如 上 代 码。 加 密 过 程 是 解 密 过 程 的 逆 运 算, 代 码 如 下:
parameter password
pas=""
for i=1 to len(trim(password))
n1=asc(substr(name,1,1))
n2= asc(substr(name,2,1))
n3= asc(substr(name,3,1))
n=int((n1+n2+n3)/3)
tempchr=bitxor(asc(substr(password,i,1)),n)
pas=pas+chr(tempchr)
endfor
replace key with pas
检 查 密 码 的 思 路 是: 先 到 输 入 姓 名 的 文 字 框 内 取 姓 名, 再 到 口 令 库 内 查 找 姓 名, 如 果 找 不 到 姓 名, 返 回 消 息 窗 口“ 您 不 是 指 定 用 户, 请 与 系 统 管 理 员 联 系 !”, 系 统 退 出; 如 果 找 到 了 用 户 姓 名, 则 继 续 进 行, 把 输 入 的 口 令 和 姓 名 送 到 解 密“ 过 程” 中 进 行 运 算, 解 密“ 过 程” 将 其 解 密, 并 与 口 令 库 内 的 数 据 进 行 比 较, 如 果 不 正 确, 开 始 计 数, 要 求 重 新 输 入 密 码, 三 次 不 正 确, 退 出 系 统。 如 果 正 确, 释 放 当 前 窗 口, 进 入 系 统。
主 要 代 码 如 下:name=trim(ThisForm.Text1.value)
if empty(name)
a=messagebox
("请输入用户名!",0+48,"信息窗口")
ThisForm.Text1.setfocus
return
endif
pass=trim(ThisForm.Text2.value)
if empty(pass)
a=messagebox
("请输入口令!",0+48,"信息窗口")
ThisForm.Text2.setfocus
return
endif
use klk
locate for klk.user_id=name
if found()=.f.
=messagebox
("你不是指定用户,请与系统管理员联系!",64,"提示信息")
thisform.release
else
ok =Thisform.decode(pass)
if ok=.t.
ThisForm.Label3.caption="欢迎使用!"
wait window '
欢迎使用!按任意键进入“系统维护模块。”'
release thisform
do form wh_wh
else
if m=3
m=m+1
ThisForm.Label3.caption="口令错,您无权使用"
a=messagebox
("对不起,您无权使用!",0+48,"信息窗口")
release thisform
else
a=messagebox
("口令错,请重新输入!",0+48,"信息窗口")
ThisForm.Text2.value=""
ThisForm.Text2.setfocus
m=m+1
endif
endif
endif
改 变 密 码 的 思 路 是: 首 先 读 取 用 户 姓 名, 如 果 是 新 用 户 则 请 用 户 输 入 新 密 码, 并 记 录 下 获 得 新 密 码 的 时 间; 如 果 是 老 用 户, 则 读 取 用 户 旧 密 码, 将 旧 密 码 进 行 解 密 运 算 并 和 口 令 库 内 容 比 较, 如 果 正 确, 请 用 户 输 入 新 密 码, 并 将 新 密 码 通 过 解 密 运 算 存 入 口 令 库, 并 记 录 修 改 时 间。
主 要 代 码 如 下:
name=ThisForm.Text3.value
oldpass=ThisForm.Text1.value
newpass=ThisForm.Text2.value
if isblank(newpass)
= messagebox("请重新输入新密码!",64,"信息提示")
ThisForm.Text1.setfocus()
return
endif
success=thisform.decode(oldpass)
if not success
= messagebox
("旧密码不正确,重新输入密码!",64,"信息提示")
ThisForm.Text1.setfocus()
return
endif
locate for klk.user_id =name &&new user logo
if found()=.f.
=messagebox("您是新用户!",64,"信息提示")
append blank
replace klk.user_id with name,klk.logo_ddate with date()
thisform.text3.setfocus
endif
thisform.Encode(newpass)
= messagebox
("旧密码已经修改完成,下次请使用新密码!",64,"信息提示")
ThisForm.Command2.setfocus
return
注 意, 在 改 变 密 码 的 窗 口(form) 中, 要 定 义“ 加 密 过 程” 和“ 解 密 过 程”, 方 法 如 上 所 述。
以 上 是 一 个 加 密 算 法 的 主 要 思 路 和 关 键 代 码, 其 它 部 分 读 者 可 自 行 设 计。 这 个 加 密 算 法 还 可 以 进 一 步 完 善。 如 采 用 不 同 的 函 数 进 行 运 算, 加 入 日 期, 使 每 个 人 每 天 的 密 码 都 不 一 样( 加 入 时 间 的 算 法 如 前 所 述, 利 用CDOW() 函 数 作 为 键 值 的 一 部 分。)。
……