VB安全编码规范

VB安全编码规范

时间:2016-08-09 作者:zhankehua 评论:0 点击:2502 次

1. 概述

1.1.  目的

本文档为项目组所使用的VB,VB.NET编码规范。该规范源自于产品开发方法中的经验,并在不断完善。本规范的目的在于帮助公司开发人员提高开发效率,减少代码中出现的bug,并增强代码的安全性和可维护性。

另:采纳一个不熟悉的规范可能在初期会有一些不适应,但是这些不适应很快便会消失,它所带来的好处和优势很快便会显现,特别是在当您接手他人代码时。建议开发人员能够自觉遵守该规范。

1.2.  原则

在编写本规范时,遵循以下原则:

1)  易读性 – 语言描述和代码示例必须易读,且简单明确。它们必须能展示出重点所在。示例代码不可包含多余代码。它们必须带有相应文档说明。

2)  正确性 – 示例代码必须通过测试,确保其正确性,能够正确展现重点。示例代码要能够进行编译和运行。

3)  一致性 – 语言描述前后一致,示例代码应该按照一致的编程风格和设计来保证代码易读。

4)  时效性 – 代码示例应使用现行的编程实践,例如使用 Unicode,错误处理,以及可移植性。示例代码应当使用当前推荐的运行时库和API函数。

5)  合规性 – 代码示例必须符合法律,隐私和政策标准和规范。不允许展示入侵性或低质的编程实践,不允许永久改变机器状态。所有的安装和执行方法必须可以被撤销。

6)  安全性 - 示例代码应该展示如何使用安全的编程实践:例如最低权限原则,使用安全版本的运行时库函数,指出过时函数和替代函数等。

2. 术语

n  一定要:该规范或实践在任何情况下都应该遵守。除非您认为您的应用是例外,则可能不适用。

n  一定不要:不允许将该规范或实践应用到实际编程中。

n  应该:该规范和实践适用于大多数情况,如果符合您的实际情况,请遵循该规范。

n  不应该:不应该将该规范或实践,应用到您的实际编码当中,除非有合理的理由。

n  可以:该标准和规范您可以按需应用。

3. 通用编码规范

3.1.  简明性和一致性

一定要确保代码的简明性,易读性和透明性。编程规范致力于确保代码是易懂和易维护的。没有什么胜于清晰、简洁、自描述的代码。

一定要确保一旦应用了本编程规范,要在所有代码中应用,以保持一致性。

3.2.  格式和风格

 ×一定不要使用制表符。不同的文字编辑器使用不同的空格来生成制表符,这就带来了格式混乱。所有代码都应该使用4个空格来表示缩进。可以配置Visual Studio 文字编辑器,以空格代替制表符。

 

 

应该限制一行代码的最大长度。过长的代码降低了代码易读性。为了提高易读性,将代码长度可为78列。若78列太窄,可以为86或者90。

一定要在您的代码编辑器中使用定宽字体,例如 Courier New。

3.3.  库的使用

×一定不要引用不必要的库,包括不必要的头文件,或引用不必要的程序集。注重细节能够减少项目生成时间,最小化出错几率,并给读者一个良好的印象。

3.4.  全局变量

一定要尽量少用全局变量。为了正确的使用全局变量,一般是将它们作为参数传入函数。永远不要在函数或类内部直接引用全局变量,因为这会引起一个副作用:在调用者不知情的情况下改变了全局变量的状态。这对于静态变量同样适用。如果您需要修改全局变量,您应该将其作为一个输出参数,或返回其一份全局变量的拷贝。

3.5.  变量的声明和初始化

一定要在最小的,包含该局部变量的作用域块内声明它。一般,如果语言允许,就仅在使用前声明它们,否则就在作用域块的顶端声明。

一定要在声明变量时初始化它们。

一定要在语言允许的情况下,将局部变量的声明和初始化或赋值置于同一行代码内。这减少了代码的垂直空间,确保了变量不会处在未初始化的状态。

 

Dim name As String = myObject.Name

Dim val  As Integer = time.Hours

 

×一定不要在同一行中声明多个变量。推荐每行只包含一句声明,这样有利于添加注释,也减少歧义。例如:

Good:

Dim username As String

Dim password As String

 

Bad:

Dim username, password As String

 

3.6.  函数的声明和调用

函数或方法的名称,返回值,参数列表可以有多种形式。原则上应该都将这些置于同一行代码内。如果带有过多参数不能置于一行代码,可以进行换行:多个参数一行或者一个参数一行。将返回值置于函数或方法名称的同一行。例如:

单行格式:

 

hr = DoSomeFunctionCall(param1, param2, param3)

 

多行格式:

 

hr = DoSomeFunctionCall(param1, param2,param3, _

param4, param5

 

将参数列表置于多行代码时,每一个参数应该整齐排列于前一个参数的下方。第一个类型/参数对置于新行行首,并缩进一个制表符宽度。函数或方法调用时的参数列表同样需按照这一格式。

 

hr = DoSomeFunctionCall

hwnd,

   param1,

   param2,

   param3,

   param4,

param5

 

一定要将参数排序,并首先将输入参数分组,再将输出参数放置最后。在参数组内,按照能够指导编码人员输入正确值的原则来将参数排序。例如,如果一个函数带有2个参数, “left” 和 “right”,将 “left” 置于 “right” 之前,则它们的放置顺序符合其参数名。当设计一系列具有相同参数的函数时,在各函数内使用一致的顺序。比如,如果一个函数带有一个输入类型为句柄的参数作为第一参数,那么所有相关函数都应该将该输入句柄作为第一参数。

一定要为每个参数指定数据类型。

应该根据情况传递ByVal或ByRef。给每个参数冠以ByVal或ByRef所需要的规则是非常重要的

一定要对数进行检验,决不要假设你得数据没有问题。程序员常犯的一个错误是在编写过程时假设数据没有问题。在初始编程阶段,当编写调用过程时,这样的假设并无大碍。这时你完全能够知道什么是参数的许可值,并按要求提供这些值。但如果你不对参数的数据进行检验,那么下列情况就会给你带来很大麻烦:另外某个人创建了一个调用过程,但此人不知道允许的值;你在晚些时候添加了新的调用过程,并错误的传递了坏数据。

可以在参数只接受较小的一组值时,用枚举值。使用枚举值,可降低编码时出现数据输入错误的可能性。只要有可能,就可考虑使用枚举值。

3.7.  代码语句

×一定不要在同一行内放置多个代码语句。这会使得单步调试变得更为困难。

Good:

 

If (IsAdministrator()) Then

Console.WriteLine"YES"

End If

Bad:

 

If(IsAdministrator())Then Console.WriteLine"YES"

3.8.  枚举

一定要将代表某些值集合的强类型参数,属性和返回值声明为枚举类型。

一定要在合适的情况下尽量使用枚举类型,而不是静态常量。枚举类型是一个具有一个静态常量集合的结构体。

 

Good:

 

Public Enum Color

        Red

        Green

        Blue

End Enum

Bad:

 

Public Class Color

        Public Const Red As Integer = 0

        Public Const Green As Integer = 1

        Public Const Blue As Integer = 2

End Class

×一定不要使用公开集合作为枚举(例如操作系统版本,您亲朋的姓名)。

一定要为简单枚举提供一个0值枚举量,可以考虑将之命名为“None”。如果这个名称对于特定的枚举并不合适,可以自行定义为更准确的名称。

 

 

Public Enum Compression

        None = 0

        GZip

        Deflate

    End Enum

×一定不要在.NET中使用 Enum.IsDefined 来检查枚举范围。Enum.IsDefined有2个问题。首先,它加载反射和大量类型元数据,代价极其昂贵。第二,它存在版本的问题。

 

Good:

 

If (c > Color.Black Or c < Color.White) Then

    Throw New ArgumentOutOfRangeException...);

End If

Bad:

 

If Not [Enum].IsDefinedGetTypeColor), c) Then

        Throw New ArgumentOutOfRangeException...

End If

 

3.8.1.   标志枚举

标志枚举用于对枚举值进行位运算的支持。标志枚举通常用于表示选项。

一定要将 System.FlagsAttribute 应用于标志枚举。一定不要将此属性用于简单枚举。

一定要利用2进制强大的能力,因为它可以自由的进行位异或运算。例如:

 

<Flags()>

    Public Enum AttributeTargets

        Assembly = &H1

        Struct = &H4

        ...

    End Enum

应该提供一些特殊的枚举值,以便进行常见的标志枚举的组合运算。位运算属于高级任务,所以在简单任务中无需使用它们。FileAccess.ReadWrite 便是标志枚举特殊值的一个示例。然而,如果一个标志枚举中的某些值组合起来是非法的,您就不应该创建这样的标志枚举。

 

<Flags()> _

    Public Enum FileAccess

        Read = &H1

        Write = &H4

        ReadWrite = Read Or Write

    End Enum

×不应该在标志枚举中使用0值,除非它代表“所有标志被清除了”,并被恰当的命名为类似“None”的名字。如下示例展示了一常见的检查某一标志是否被设置了的实现(查看如下 if-语句)。该检查运行结果正确,但是有一处例外,便是对于0值的检查,它的布尔表达式结果恒为true。

 

Bad:

<Flags()>

    Public Enum FomeFlag

        ValueA = &H0 '这样可能使用户迷惑

        ValueB = &H1

        ValueC = &H2

        ValueBandC = ValueB And ValueC

    End Enum

Good:

<Flags()>

    Public Enum BorderStyle

        Fixed3D = &H1

        FixedSingle = &H2

        None = &H0

    End Enum

    Function MyTest()

        If foo.BorderStyoe = BorderStyle.None Then

            ...

        End If

    End Function

3.9.  空格

3.9.1.   空行

应该使用空行来分隔相关语句块。省略额外的空行会加大代码阅读难度。比如,您可以在变量声明和代码之间有一行空行。

 

Good:

Function SpaceSample()

        Dim counter As Integer

 

        If counter = 0 Then

            ...

        End If

End Function

Bad:

Function SpaceSample()

        Dim counter As Integer

        If counter = 0 Then

            ...

        End If

        

        

         If counter = 0 Then

            ...

        End If

End Function

 

在本例中,过多的空行造成了空行滥用,并不能使代码更易于阅读。

您应该使用2行空行来分隔方法实现或类型声明。

3.9.2.   空格

空格能够降低代码密度以增加可读性。以下是使用空格符的一些指导规范:

您应该像如下般在一行代码中使用空格。

 

Good:

 

CreateFoo() ' 在函数名和括号之间不使用空格

Method(myChar, 0, 1) ' 在逗号之后使用一个空格

x = Array(Index) ' 在括号内部不使用空格

While (x = y) ' 在控制语句后使用一个空格

     If (x = y) Then  ' 使用一个空格来分隔操作

Bad:

 

CreateFoo () ' 在函数名和括号之间有空格

Method(myChar,0,1) ' 在逗号之后没有空格

x = Array( Index ) ' 在括号内部使用空格

While(x = y) ' 在控制语句后没有空格

     If (x=y) Then  ' 没有空格来分隔操作

 

3.10.     大括号

一定要使用奥尔曼风格的大括号。

奥尔曼风格是以艾瑞·奥尔曼命名的,有时也被称为"ANSI风格"。该风格将大括号与相关代码置于下一行内,与控制语句的缩进相同。大括号内的语句缩进一个等级。(注:VB中不使用大括号,而是使用结束标志来组织代码块)

 

Good:

 

Function MyTest()

        If foo.BorderStyoe = BorderStyle.None Then

            ...

        End If

    End Function

 

Bad:

Function MyTest()

    If foo.BorderStyoe = BorderStyle.None Then

            ...

    End If

    End Function

应该在即使是单行条件式的情况下也使用大括号。这样做使得将来增加条件式更简便,并减少制表符引起的歧义。

 

Good:

 

If x>5 Then

  y = 0

End If

Bad:

 

If x>5 Then y = 0

3.11.     注释

应该使用注释来解释一段代码的设计目的。一定不要让注释仅仅是重复代码。

 

Good:

    ' 确定系统是否运行在windows vista 或更新的操作系统(major version >=6),因为它们支

    '持令牌环,但以前的版本(major version < 6)不支持。

 

Bad:

'下面的代码将变量i设置为数组的起始值。然后它们循环数组中每一项。

3.11.1. 内联代码注释

内联注释应该在单独的行,并与所描述的代码具有相同的缩进。在其之前需要放置一个空行。其之后不需要空行。描述代码块的注释应该置于单独的行,与所描述的代码具有相同的缩进。其之前和之后都有一个空行。举例:

当内联注释只为结构体,类成员变量,参数和较短语句做描述时,则内联注释允许出现在和实际代码的同一行。在本例中,最好将所有变量的注释对齐。

CreateFoo() ' 在函数名和括号之间不使用空格

Method(myChar, 0, 1) ' 在逗号之后使用一个空格

x = Array(Index) ' 在括号内部不使用空格

应该为那些仅通过阅读很难理解其意图的代码添加注释。

3.11.2. 文件头注释

一定要为每一份手写代码(不是编码工具自动生成的代码)文件加入文件头注释。

 

文件头注释模板:

 

'***************************** 模块头*******************************\

' Module Name:  用户管理模块

' Project:      人事管理系统

' Copyright (c) My Company name

'

' 文件描述

'

' 这个源码是受到XXX公司许可的

' 参见http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.

' XXX公司保持其所有权

' ……

'***************************************************************************/

3.11.3. 类注释

应该为每一个重要的类或结构体作注释。注释的详细程度应该视代码的用户群而定。

 

' <summary>

' <Class description>

'</summary>

3.11.4. 函数注释

应该为所有重要的Public或非Public函数作注释。注释的详细程度应该视代码的用户群而定。

VB.NET 使用 .NET 描述性 XML 文档化注释。在一般情况下,至少需要一个<summary>元素,一个<parameters>元素和<returns>元素。会抛出异常的方法应使用<exception>元素来告知使用者哪些异常会被抛出。

' <summary>

        ' <Function description>

        ' </summary>

        ' <param name="Parameter name">

        ' <Parameter description>

        ' </param>

        ' <returns>

        ' <Description of function return value>

        ' </returns>

        ' <exception cref="<Exception type>">

        ' <Exception that may be thrown by the function>

        ' </exception>

任何调用失败会带来副作用的方法或函数,都需要清楚地在注释中描述清楚副作用的后果。一般而言,代码在发生错误或失败时不应该有副作用。当出现副作用时,在编写代码时应该有清楚的理由。

3.11.5. 将代码注释掉

当您用多种方法实现某一项功能时,将代码注释掉便是必须的。除了第一个方法,其余实现方法都会被注释掉。使用 [-or-] 来分隔多个方法。举例,

' 第一个解决方案

DemoSolution1();

' [-or-]

 

' 第二个解决方案

'DemoSolution2();

3.11.6. TODO 待办注释

×一定不要在已发布的代码示例中使用TODO待处理的注释。每一个代码示例都必须完整,在代码中不能有未完成的任务。

3.12.     代码块

一定要使用代码块声明。通过作用域或者功能性分类,将大量代码分组,会改善代码易读性和结构。


未完,待续......