Technical Blog Post
Abstract
使调试过程变得安全
Body
作者介绍:
Mark Hessler 是 IBM 明尼苏达州罗切斯特实验室的一名资深软件工程师,他主要从事 IBM i 系统 ILE C 和 C++ 编译器的开发工作。
Scott Elliott 是 IBM 明尼苏达州罗切斯特实验室的一名资深软件工程师,他主要从事 IBM i 系统 XML Toolkit 以及调试器的开发工作。
什么是调试视图?
ILE的调试环境提供了应用程序源码级的调试支持。源码级调试器是一个调试工具,它在应用程序运行时提供高级语言的源代码视图,使用户可以设置断点,查看或者修改变量的值。
在创建应用程序时,用户可以指定不同的调试视图,包括*SOURCE调试视图(DBGVIEW(*SOURCE))以及*LIST调试视图((DBGVIEW(*LIST))。
当使用*SOURCE调试视图时,应用程序中会保存指向源文件的指针。这样可以减小应用程序的大小,但是需要源文件存在于调试该程序的系统上。因此,源码调试视图主要用于内部开发环境。
当使用*LIST调试视图时,应用程序中会保存一份源码清单的拷贝。这样虽然增加了应用程序的大小,但是由于不再需要源文件了,应用程序就可以在任何系统上进行源代码级的调试。
为什么要使用加密的调试视图?
在开发过程中,调试程序最方便的方法之一是:编译生成支持源代码级调试的程序版本,然后对其进行调试。使用程序的源码级调试版本进行调试。而在客户系统中,用源码级的调试通常是不可行的,对外发布的程序一般都不会包含调试数据。即便是使用*LIST调试视图创建的程序,也只会暂时部署于客户系统中,用于问题定位。这种方法的缺点是在应用程序中包含了一份源码的拷贝,这就潜在地允许了客户系统中的任何用户都可以访问源码。在大多数情况下,这都是一个严重的问题。因此需要使用不同的机制在客户系统上进行调试。
在创建应用程序的可调试版本时,这个新的支持允许用户在模块创建命令中指定一个密钥。当使用*LIST调试视图时,保存在应用程序中的源码将会被这个密钥加密。这样创建出来的可调试的应用程序就能放在用户系统上用于问题定位了。只有知道密钥,才能看到源码。源码依旧保持安全,没有密钥它就不能被访问。
这个新特性的另一个应用是用于控制受限代码的访问权限。有些应用程序的部分源代码可能对某些开发人员是受限制的。在这种情况下,加密方式有利于允许所有开发人员调试应用程序,同时控制了源码关键部分的访问权限。
如何启用调试加密?
调试数据加密是在ILE模块创建命令中指定的。IBM i
7.1版本中的所有ILE编译器(RPG, COBOL, CL, C, C++以及SQL)都支持调试加密。要在创建模块时启用加密调试,需要用参数DBGVIEW(*LIST)来指明使用*LIST调试视图,并且使用DBGENCKEY参数指定密钥。这个密钥会用于加密调试数据,同时在调试时用于解密。例如:
CRTCMOD MODULE(QTEMP/TEST) SRCFILE(QGPL/QSCRC)
DBGVIEW(*LIST) DBGENCKEY(‘MYKEY’) |
ILE模块创建命令中,DBGENCKEY参数的默认值是*NONE,表明不会加密调试数据。如果指定密钥,它的长度应该在1-16个字符之间,长度不足16字符时,右边将被自动补全空格。因此,’ABC’ 和 ‘ABC_’(_表示一个空格)会被看作是相同的密钥。指定长度为零的密钥和指定*NONE的作用是相同的。
由于调试数据加密是作为模块创建的一部分被执行的,因此一个应用程序可以使用相同的密钥为所有模块加密,也可以使用不同的密钥为每一个模块加密。应用程序同时可以包含被加密的模块和未经加密的模块。
如果应用程序有多个模块使用了相同的密钥,在调试过程中,仅需输入一次密钥。在STRDBG命令和密钥输入窗口中输入的密钥会被缓存下来。当调试器遇到一个加密的调试视图时,它将首先尝试用缓存下来的密钥去解密。只有当所有缓存的密钥都不能解密这个调试视图,调试器才会请求输入新的密钥。这样做是为了减少密钥输入的次数。调试结束时,所有缓存下来的密钥都将被清除。
有什么样的潜在问题?
加密调试数据的一个显著问题就是如果忘了密钥怎么办。调试加密使用的是高级加密标准(Advanced Encryption Standard),一个私有的基于密钥的块密码加密技术,它是通过IBM i 系统密码服务API被调用的。因此,一旦调试数据被加密,除非有正确的密钥,否则它是不可被访问的。
如果使用加密调试这个特性,就应该想一个方法来生成和保存密钥值。密钥值应该被记录在一个安全地方。如果没有正确的密钥,在调试中想要看到源码的唯一途径就是重新创建应用程序。
当应用程序在不同的系统上被创建和调试的时候,会出现另一个不太明显的问题。用于加密和解密调试视图数据的实际密钥是通过DBGENCKEY参数或密钥输入窗口中输入的值生成的。若长度不足16,密钥值先会被在右侧补全空格,然后根据当前作业(Job)的 code
page 转换为一个十六进制的字符串。这个十六进制的字符串就是用于加密和解密调试视图数据的实际密钥。
在不同系统的code page中,相同code
point不一定都对应相同的字符。因此,如果当生成应用程序的作业的code
page和调试程序的作业的code page不一致时,即使在创建和调试应用程序时都输入相同的密钥,实际使用的密钥值也会是不同的。
例如,假设创建模块时系统使用的codepage是37,输入的密钥值是’MyKey’,则用于加密的实际密钥值将会是如下的十六进制字符串: ’D4A8D285A84040404040404040404040’。
现在假设程序在被调试时系统使用的codepage是290,输入同样的密钥值’MyKey’,此时用于解密的实际密钥将会是如下的十六进制字串: ’D4B8D266B84040404040404040404040’。
可以看到,实际用于加密调试数据的密钥值,和实际用于解密调试数据的密钥值并不相同。这是因为在code page 37和code
page 290中,小写字母的code
point是不同的。因此即便在创建模块和用STRDBG命令进行调试时输入了同样的密钥值,调试器还是由于密钥不匹配而不能解密调试数据。
调试加密已经可用了!
UID
ibm11145956