安全与规范(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”;这样,是不是有些悲惨的事情要发生了~~~

对于这样的事情,我们可以使用两种简单的办法进行解决:

  1. 使用jdk的API代替命令
  2. 进行输入校验(类似之前的SQL注入,对参数进行校验)

 

除了SQL注入和命令行注入,常见的还有XML注入。

依旧我们直接上例子:

“<user>

  <role>operator</role>

  <id>” + ? + "</id>

  <description>" + ? + "</description>

</user>";

如果是按照套路来,输入someone doSomeThing, 这样就万事大吉了。

那么我们看看不按套路来的家伙会怎么办呐,假设输入为:someone</id><role>administrator</role><id>someone doSomeThing。

这样someone这个家伙是不是就有更大的权力去干一些不被允许的事情了。

对于XML注入的解决办法有:

  1. 使用白名单校验禁止输入特殊字符,例如:if (!Pattern.matches("[_a-bA-B0-9]+",user.getUserId()))
  2. 使用安全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!!!

 

posted @ 2020-12-29 21:35  Vincent_Qi  阅读(13)  评论(0)    收藏  举报