安全与规范(Java例)
对于我们写出来的程序,我们要做到“CIA”。
这个“CIA”当然不是美国中央情报局(Central Intelligence Agency)。
这里的C代表着机密性(Confidentiality),I代表着完整性(Integrity),A代表着可用性(Availiablity)。
- 机密性:确保数据不被非法访问
- 完整性:确保数据是完整的,没有经过别人恶意篡改的
- 可用性:确保被保护起来的资源是可以被随时访问的
说完我们的安全目标,接下来我们说说程序中常见的不可信参数有哪些。
我们常见的不可信参数:
- 文件
- 注册表
- 网络
- 环境变量
- 命令行
- 用户输入(命令行界面输入)
- 用户态数据
- 进程间的通讯
- 函数的参数
以上都是一些概念性的东西,大家一看一过就好
——————————————————————华丽的分割线——————————————————————
接下来我们来看一下常见的不安全案例。
说到安全的威胁,我们第一能想到的就是SQL注入攻击了。如果我们的代码不够规范,那么SQL注入攻击就会有机可乘。
我们看一下以下代码:
public void foo(String username, char[] password) throws SQLException {
String sqlString = "SELECT * FROM XXX WHERE username = '" + username + "' AND password = '"+ password +"' ";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sqlString);
}
这段代码其实就存在安全隐患,假设我们在用户名中输入:'abc' or '1' = '1' 那么我们是不是就可以绕过这个登陆认证了呐。
为了避免这种问题发生,我们可以:
1 最简单方法,对username参数进行检验:比如参数只能由大小写字母,数字和下划线组成。
2 使用PreparedStatement的方法:
public void foo(String username, char[] password) throws SQLException {
String sqlString = "SELECT * FROM XXX WHERE username = ? AND password = ?";
PreparedStatement stmt = connection.PrepareStatement(sqlString);
stmt.setString(1,username);
stmt.setString(2,password);
ResultSet rs = stmt.executeQuery();
}
接下来我们来看看OS命令行的注入。
话不多说,直接看案例:
Runtime.getRuntime().exec("cmd.exe /c dir" + dir)
假设 dir = "whocare",那么这段执行语句看起来没有什么问题;可是,事情不是我们想的那么美好。
如果dir = “whocare & del c:\\XXX”;这样,是不是有些悲惨的事情要发生了~~~
对于这样的事情,我们可以使用两种简单的办法进行解决:
- 使用jdk的API代替命令
- 进行输入校验(类似之前的SQL注入,对参数进行校验)
除了SQL注入和命令行注入,常见的还有XML注入。
依旧我们直接上例子:
“<user>
<role>operator</role>
<id>” + ? + "</id>
<description>" + ? + "</description>
</user>";
如果是按照套路来,输入someone doSomeThing, 这样就万事大吉了。
那么我们看看不按套路来的家伙会怎么办呐,假设输入为:someone</id><role>administrator</role><id>someone doSomeThing。
这样someone这个家伙是不是就有更大的权力去干一些不被允许的事情了。
对于XML注入的解决办法有:
- 使用白名单校验禁止输入特殊字符,例如:if (!Pattern.matches("[_a-bA-B0-9]+",user.getUserId()))
- 使用安全XML库
另外值得注意的是,在线程同步中,我们要防止将锁暴露给非信任的代码段,这可以预防Dos攻击(Dos可以理解为阻塞你的服务器/线程,让程序无法响应)。
直接暴力的上案例:
public class SomeObject{
public synchronized void doSomeThing(){}
}
这样这个锁是不是大家都可以访问啦,有人使坏把锁lock了,那么~~~后果大家一定猜到了,Dos!
下面来一段规范的写法来避免Dos:
public class SomeObject{
private final Object lock = new Object();
public void doSomeThing(){
synchronized(lock){}
}
}
这样非信任代码段就无法来访问和占用我们的锁啦。
最后啰嗦一下,我们代码中抛出的错误信息会引发我们的数据泄露。
例如:
- fileNotFoundException 会泄露底层文件结构
- SQLException 会泄露数据库结构,用户名列举
- BindException 会泄露非信任客户端可选择的服务器端口
- MissingResourceException 会泄露资源列举
- JarException 会泄露底层文件系统结构
- NotOwnerException 用来枚举会泄露所有者信息
- OutOfmemoryError 会引发Dos攻击
- StackOverflowError 会引发Dos攻击
对于以上问题,我们可以把异常封装后再抛出。
如果您读到了这里,鄙人谢谢您耐心看完了这篇文章,祝:抠腚愉快,happy coding!!!
浙公网安备 33010602011771号