IBM Support

使调试过程变得安全

Technical Blog Post


Abstract

使调试过程变得安全

Body

作者介绍:

Mark Hessler IBM 明尼苏达州罗切斯特实验室的一名资深软件工程师,他主要从事 IBM i 系统 ILE C C++ 编译器的开发工作。

Scott Elliott IBM 明尼苏达州罗切斯特实验室的一名资深软件工程师,他主要从事 IBM i 系统 XML Toolkit 以及调试器的开发工作

 
IBM i系统7.1版本为应用程序开发人员提供了一个新特性:对ILEIntegrated Language Environment,集成语言环境)应用程序相关联的调试视图进行加密。本文将介绍这个新特性,以及如何使用它,同时也解释了调试视图数据加密的原因。另外,本文还将会着重说明在使用这个新特性时,开发人员可能会遇到一些潜在的问题。
 

什么是调试视图?

ILE的调试环境提供了应用程序源码级的调试支持。源码级调试器是一个调试工具,它在应用程序运行时提供高级语言的源代码视图,使用户可以设置断点,查看或者修改变量的值。

在创建应用程序时,用户可以指定不同的调试视图,包括*SOURCE调试视图(DBGVIEW(*SOURCE))以及*LIST调试视图((DBGVIEW(*LIST))

当使用*SOURCE调试视图时,应用程序中会保存指向源文件的指针。这样可以减小应用程序的大小,但是需要源文件存在于调试该程序的系统上。因此,源码调试视图主要用于内部开发环境。

当使用*LIST调试视图时,应用程序中会保存一份源码清单的拷贝。这样虽然增加了应用程序的大小,但是由于不再需要源文件了,应用程序就可以在任何系统上进行源代码级的调试。

IBM i 7.1版本中支持的调试加密,提供了一种对*LIST调试视图的文本信息进行加密的机制。为了启用这个新的支持,ILE模块创建命令和调试命令中都加入了一个新参数: DBGENCKEY
 

为什么要使用加密的调试视图?

在开发过程中,调试程序最方便的方法之一是:编译生成支持源代码级调试的程序版本,然后对其进行调试。使用程序的源码级调试版本进行调试。而在客户系统中,用源码级的调试通常是不可行的,对外发布的程序一般都不会包含调试数据。即便是使用*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时,使用DBGENCKEY参数直接指明密钥。如果在STRDBG命令中没有指定密钥,或者指定了错误的密钥,调试器会打开一个密钥输入窗口,提示输入密钥(见下图1)。不论是在STRDBG命令中,还是在请求密钥窗口中输入了正确的密钥后,调试器都会解密调试视图数据,并显示该视图。
图像
 图 1
如果指定了无效的密钥,则调试视图将不可见(见下图2)。在这种情况下,调试器虽然允许通过行号设置断点、显示或更新变量值等操作,但源码视图仍然是不可见的。
图像
图 2

如果应用程序有多个模块使用了相同的密钥,在调试过程中,仅需输入一次密钥。在STRDBG命令和密钥输入窗口中输入的密钥会被缓存下来。当调试器遇到一个加密的调试视图时,它将首先尝试用缓存下来的密钥去解密。只有当所有缓存的密钥都不能解密这个调试视图,调试器才会请求输入新的密钥。这样做是为了减少密钥输入的次数。调试结束时,所有缓存下来的密钥都将被清除。

 

有什么样的潜在问题?

加密调试数据的一个显著问题就是如果忘了密钥怎么办。调试加密使用的是高级加密标准(Advanced Encryption Standard),一个私有的基于密钥的块密码加密技术,它是通过IBM i 系统密码服务API被调用的。因此,一旦调试数据被加密,除非有正确的密钥,否则它是不可被访问的。

如果使用加密调试这个特性,就应该想一个方法来生成和保存密钥值。密钥值应该被记录在一个安全地方。如果没有正确的密钥,在调试中想要看到源码的唯一途径就是重新创建应用程序。

当应用程序在不同的系统上被创建和调试的时候,会出现另一个不太明显的问题。用于加密和解密调试视图数据的实际密钥是通过DBGENCKEY参数或密钥输入窗口中输入的值生成的。若长度不足16,密钥值先会被在右侧补全空格,然后根据当前作业(Job) code page 转换为一个十六进制的字符串。这个十六进制的字符串就是用于加密和解密调试视图数据的实际密钥。

在不同系统的code page中,相同code point不一定都对应相同的字符。因此,如果当生成应用程序的作业的code page和调试程序的作业的code page不一致时,即使在创建和调试应用程序时都输入相同的密钥,实际使用的密钥值也会是不同的。

例如,假设创建模块时系统使用的codepage37,输入的密钥值是’MyKey’,则用于加密的实际密钥值将会是如下的十六进制字符串: ’D4A8D285A84040404040404040404040’

现在假设程序在被调试时系统使用的codepage290,输入同样的密钥值’MyKey’,此时用于解密的实际密钥将会是如下的十六进制字串: ’D4B8D266B84040404040404040404040’

可以看到,实际用于加密调试数据的密钥值,和实际用于解密调试数据的密钥值并不相同。这是因为在code page 37code page 290中,小写字母的code point是不同的。因此即便在创建模块和用STRDBG命令进行调试时输入了同样的密钥值,调试器还是由于密钥不匹配而不能解密调试数据。

为了避免这个问题,需要保证用于密钥的字符来自于一个不变字符集(invariant character set)。当EBCDIC code page转换为其他code page的时候, 不变字符集中的code point不会随之改变。这个字符集常常包括大写字母A-Z,数字0-9以及空格。如果想要了解更多有关不变字符集的内容,可以参考信息中心不变字符集的网页。
 

调试加密已经可用了!

本文简要介绍了在ILE调试环境中支持的调试数据加密特性。它使用简单,并且能使调试更加安全。调试数据加密特性在IBM i 7.1系统上已经可用了。尽情(并安全)地调试吧! 

[{"Business Unit":{"code":"BU009","label":"Systems - Cognitive"}, "Product":{"code":"SWG60","label":"IBM i"},"Component":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"","Edition":""}]

UID

ibm11145956