软件安全-4.国际化安全

4.1 国际化的基本机制

4.1.1 国际化概述

随着经济全球化的发展,软件也应该具有支持各种语言和地区的能力。国际化的主要目的,是调整软件,使之能适用于不同的语言及地区。

与国际化类似的另一个概念是本地化(localization)。在业界内,两个概念一般一起讲,有时候甚至被等同起来。不过,从概念上说,本地化是实现国际化的一些手段的集合。

国际化的概念,比较偏向表达软件的设计思想,要求当软件被移植到不同的语言及地区时,软件的业务逻辑和程序源代码不用作改变或修正,但是软件又必须让该地区和语言的用户方便地使用;本地化的概念偏向对软件进行加工,使之满足特定地区和特定语言的用户对语言和功能的特殊要求,实际上是指一系列工作的过程。

软件本地化工作,可能涉及文字的翻译、用户界面布局调整、本地特性开发、联机文档和印刷手册的制作,以及保证本地化版本能正常工作等,实际上也算是软件质量保证活动的一部分。国际化简称为I18N,本地化由于其单词localization的L和N之间有10个字母,因此简称为L10N。

国际化和本地化这两个工作,一个是设计思想,一个是工作的手段,相辅相成,互为补充。在有些企业中,也使用全球化(globalization)来表示国际化和本地化的合称,用G11N作为简称。

从具体的工作内容上说,国际化与本地化工作,实际上包括的细节很多,也很繁杂。以下列举一些常见的工作:

  • 不同语言表达方式;
  • 电子文件的编码;
  • 数字命名系统的不同;
  • 文字书写方向(如英语是从左到右,阿拉伯语从右到左);
  • 语言细微差别(如英国英语中的Colour和美国英语中的 Color);
  • 货币;
  • 日期格式;
  • 数字格式,等等。

4.1.2 国际化过程

开发软件时,国际化和本地化对开发者是一个有挑战性的任务。很多软件,在软件刚开始设计时,并没有考虑到需要在不同的语言和地区使用,于是就没有按照国际化的思想设计,但是一段时间之后,软件突然出现要在其他地区使用的任务,国际化和本地化的工作将会十分艰难。

怎样让程序从一开始就为国际化和本地化提供开发基础呢?我们知道,软件的难度在于程序的业务逻辑,一般情况下不应该随便对业务逻辑进行改动。程序在不同地区运行的过程中,实际上,程序的逻辑只有一份,只是界面的表示有所不同,而应该避免的是程序逻辑的修改。因此,通常作法是:将文本和其他与环境相关的资源单独编写,与程序代码相分离。这样,在理想的情况下,应对变化的环境时,无须修改代码,只需要修改资源,从而简化了工作。

4.2 国际化中的安全问题

4.2.1 字符集

国际化过程中,遇到的最重要的问题是不同的语言文字在不同的系统中具有不同的表达方式,也就是通常所说的编码。编码是不同国家的语言在计算机中的一种存储和解释规范,在各个不同规范中,存储了相应能够表达一定内容的若干字符,称为字符集。

最原始的字符集是美国国家标准学会(American National Standards Institute,ANSI)的美国信息交换标准码(American Standard Code for Information Interchange,ASCII字符集),它使用7个比特来表示一个字符,总共表示128个字符;不过,由于一个字节一般占用8个比特,为了充分利用一个字节所能表达的最大信息,IBM公司对ASCII字符集进行了扩展,用一个字节来表示一个字符,这样,让ASCII码字符集总共可以表示256个字符。不过,人们常说的ASCII码字符集表达的还是128个字符,常见的ASCII码字符集表也是基于128字符的ASCII码字符集编写的。

由于英文和大部分的西方语言都是以字母拼写为基础的,需要的字母数量不多。因此,以上ASCII码字符集对这些语言的表达,基本能够胜任。但是,世界上的语言种类多种多样,如中文、日文、韩文等,语言中含有的文字就几千个,ASCII字符集就无法胜任其表达。因此,在ASCII字符集的基础之上,又派生出了一些新的字符集,如GB2312、UTF-8、UTF-16等,称为多字节字符系统(Multi-Byte Character System,MBCS)

4.2.2 字符集转换

在实际的项目中,源数据可能来自于不同的字符集(如支持不同字符集的数据库),而源数据需要以某种方式被目标程序使用,如显示在界面上,或者被目标程序进行加工等。这时候遇到的最大问题就是:目标程序所支持的字符集和源数据属于的字符集可能不一致。此种情况下,可能造成系统显示出错。下面根据情况的不同进行分类:

(1)当目标程序所支持的字符集和源数据属于的字符集完全不兼容时,数据无法显示(或者以乱码形式显示)。例如,对于中文来说,如果源数据库使用字符集GBK,从数据库中查出来的中文内容,想要在目标程序上使用,而目标程序默认支持ASCII,由于GBK是16位字符集,而ASCII是7位字符集,两者完全不兼容,或者说没有任何关系,每一个中文字符在ASCII中,都不能够找到对等的字符,所以所有中文字符都会丢失而变成乱码形式。

(2)当目标程序所支持的字符集是源数据属于的字符集的子集时,信息会部分丢失。例如,如果源数据库使用GBK,而目标程序字符集使用GB2312,这个过程中绝大部分字符都能够正确转换,但是由于GB2312字符集小于GBK,因此一些超出GB2312字符集的字符变为乱码。

在这些情况下,就必须进行字符集转换,俗称转码。各种语言中都有不同的转码支持。本节以Visual C++为例来讲解转码问题。

在Visual C++中,有如下两个函数对转码进行支持:

  • MultiByte ToWideChar;
  • WideCharToMultiByte;

在这两个函数中,MultiByte称为短字符,一般为8位或8位以内来表示的字符,如ASCII码。WideChar称为宽字符,一般指用16位或以上(如果有的话)表示的字符,如UNICODE。

MultiByteToWideChar函数的功能是:将一个由短字符组成的字符串转换为一个宽字符组成的字符串。函数原型是:

int MultiByteToWideChar(UINT CodePage, 
                        DWORD dwFlags, 
                        LPCSTR 1pMultiByteStr, 
                        int cchMultiByte, 
                        LPWSTR 1pWideCharStr, 
                        int cchMideChar)

4.2.3 I18N缓冲区溢出问题

在转码时,如果使用不当,就会出现缓冲区溢出问题。这种情况在使用MultiByteToWideChar 和WideCharToMultiByte函数时更容易出现。如下函数:

int MultiByteToWideChar(UINT CodePage, 
                        DWORD dwFlags, 
                        LPCSTR 1pMultiByteStr, 
                        int cchMultiByte, 
                        LPWSTR 1pWideCharStr, 
                        int cchMideChar)

函数的参数5中,定义了目标字符串的指针。怎样定义目标字符串呢?一种方法是,事先定义一个足够大的宽字符数组,但是可能有如下的缺陷:

  • 当lpMultiByteStr占用空间较小时,可能会造成空间浪费;
  • 如果为了避免空间浪费,分配的数组空间较小,当lpMultiByteStr占用的空间超过了预分配空间时,又可能造成缓冲区溢出。

因此,该方法不是一个最好的办法。

此种情况下,需要通过一些手段来获知转码的目标数组lpMultiByteStr所需要的数组空间。方法是:将MultiByteToWideChar函数的第4个形参设为一1,即可返回所需的短字符数组空间的个数。下面就是一个例子

int nten=MultiByteToMideChar(CP_UTE8,0,
                             1pMultiByteStr,-1, 
                             NULL,0);

nLen中得到的就是所需的短字符数组空间的个数。因此,完整的安全代码结构如下:

//获得需要分配的内存大小,存入nlen 
int nfen= MultiByteoNideChar(CP_UIE8,MB_ERR_INVALTD_CHARS,
    						  1pMultiByteStr,-1,
    						  NULL,0);
if(nLen==0)
{
//函数调用异常,作异常处理
//跳出
}
//为新的字符串分配内存
LPWSTR 1pWideCharStr =(LPWSTR)Globala1loc(0,sizeof(WCHAR)*nLen));if(1pWideCharStr ==NULL)
{
	//内存分配失败,作相应处理
    //跳出
}
//正式转换
nLen=MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,
    					  1pMiltiByteStr,-1,
          				  1pWideCharStr,nLen);
if(nLen==0){
//函数调用异常,作异常处理
//跳出
}
//其他收尾操作

同样的道理,WideCharToMultiByte函数的使用中,也应该特别注意缓冲区溢出的问题

4.3 推荐使用Unicode

实际上,理想的情况是,在软件开发的过程中,全程使用某一种编码。并且不在这种编码和别的字符集之间进行转换,自然不会出现字符集转换中信息丢失的问题。

在要选择的编码中,Unicode(Universal Multiple-Octet Coded Character Set)是一种值得推荐的字符集。

Unicode又称统一码、万国码或单一码,是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode于1990年开始研发,在1994年正式公布。随着网络的发展和信息交流的增强,Unicode也在面世以来的十多年里得到普及。作为一种在计算机上使用的字符编码,针对每种语言中的每个字符,Unicode都设定了统一并且唯一的二进制编码,这样,不管在什么语言、什么平台下,Unicode不需要转码,满足了跨语言、跨平台进行文本转换、处理的要求。

Unicode是采用16位编码体系,一律使用两个字节表示一个字符,特别值得强调的是,对于ASCII字符它也使用两字节表示。

Unicode制定了3套编码方式。分别是UTF-8、UTF-16和UTF-32(使用较少)。
因此,如果在开发软件的过程中使用Unicode,并且不将Unicode和其他字符集进行转换,就就基本上不会遇到转码过程中的安全问题

posted @ 2021-08-01 18:43  1ink  阅读(227)  评论(0编辑  收藏  举报