JAVA代码审计
文件下载
检查文件处理时,是否有对路径进行强限定,因为强限定路径可以避免用户恶意构建下载文件的路径
文件上传
了解文件路径
src—源代码目录(在部署的时候不会放入到容器里)
WebContent(WebApp\Web)—Web 应用,服务器部署目录,在服务器中生成当前文件同名的目录部署,表示工程根路径。如WEBDemo工程的部署,会将 WebContent改名为WEBDemo。如需要访问改目录则直接使用http://localhost:8080/WEBDemo
WEB-INF — WEB的配置目录或信息目录 web.xml (关键文件,可能有工程的整体配置),该目录会被服务器加载,但不能通过URL直接访问。该目录下的所 有文件如果需要访问则必须通过WEB-INF以外的页面访问它。如:WEB-INF里面有一个a.jsp,需要访问这个JSP不能通过URL:http://localho st:8080/projectName/WEB-INF/a.jsp的形式进行访问,必须在WEB-INF的外面,例如WebContent的根下面创建一个b.jsp,用URL访问 b.jsp,通过b.jsp可直接访问a.jsp或包含
classes — src源代码被编译以后的字节码文件
META-INF — 会被加载但不能被访问,一般用于存放配置文件
站外目录,非工程路径甚至非服务器路径,有可能是其它的某个盘符也有可能是其它的某个服务器。常见的单独的文件服务器
了解开发人员可能上传的路径
classes目录(根)上传—好定位,可以用代码通过当前类文件直接获取到路径甚至流
WebContent目录(根)上传—好定位
工程目录(根)上传
指定盘符上传—绝对路径
独立的文件服务器
检查文件上传路径是否在服务器可加载可编译可访问的位置上,上传在WebContent根或其它目录的路径需要修改。上传在WEB-INF目录的路径,需要更多检查是否在WebContent上可上传可包含以及检查上传路径是否可被修改。
检查文件类型,通常检查文件白名单、Content-Type和文件头
JPEG (jpg),文件头:FFD8FFE1
PNG (png),文件头:89504E47
GIF (gif),文件头:47494638
IFF (tif),文件头:49492A00
Windows Bitmap (bmp),文件头:424D
CAD (dwg),文件头:41433130
Adobe Photoshop (psd),文件头:38425053
Rich Text Format (rtf),文件头:7B5C727466
XML (xml),文件头:3C3F786D6C
HTML (html),文件头:68746D6C3E
Email [thorough only] (eml),文件头:44656C69766572792D646174653A
Outlook Express (dbx),文件头:CFAD12FEC5FD746F
Outlook (pst),文件头:2142444E
MS Word/Excel (xls.or.doc),文件头:D0CF11E0
MS Access (mdb),文件头:5374616E64617264204A
WordPerfect (wpd),文件头:FF575043
Postscript (eps.or.ps),文件头:252150532D41646F6265
Adobe Acrobat (pdf),文件头:255044462D312E
Quicken (qdf),文件头:AC9EBD8F
Windows Password (pwl),文件头:E3828596
ZIP Archive (zip),文件头:504B0304
RAR Archive (rar),文件头:52617221
Wave (wav),文件头:57415645
AVI (avi),文件头:41564920
Real Audio (ram),文件头:2E7261FD
Real Media (rm),文件头:2E524D46
MPEG (mpg),文件头:000001BA
MPEG (mpg),文件头:000001B3
Quicktime (mov),文件头:6D6F6F76
Windows Media (asf),文件头:3026B2758E66CF11
MIDI (mid),文件头:4D546864
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class FileTypeDetector {
//白名单,把能够通过的文件头都放到里面
private static Map<String,String> head2FileType = new HashMap<String,String>();
//构建白名单文件头
static{
head2FileType.put("FFD8FFE1", "jpg");
head2FileType.put("89504E47", "png");
head2FileType.put("47494638 ", "gif");
head2FileType.put("49492A00", "tif");
head2FileType.put("424D", "bmp");
head2FileType.put("41433130", "dwg");
head2FileType.put("38425053 ", "psd");
head2FileType.put("7B5C727466", "rtf");
head2FileType.put("3C3F786D6C", "xml");
head2FileType.put("68746D6C3E ", "html");
head2FileType.put("44656C69766572792D646174", "eml");
head2FileType.put("CFAD12FEC5FD746F ", "dbx");
head2FileType.put("2142444E", "pst");
head2FileType.put("D0CF11E0", "xls/doc");
head2FileType.put("5374616E64617264204A", "mdb");
head2FileType.put("FF575043", "wpd");
head2FileType.put("252150532D41646F6265", "eps/ps");
head2FileType.put("255044462D312E", "pdf");
head2FileType.put("E3828596", "pwl");
head2FileType.put("504B0304", "zip");
head2FileType.put("52617221", "rar");
head2FileType.put("57415645", "wav");
head2FileType.put("41564920", "avi");
head2FileType.put("2E7261FD", "ram");
head2FileType.put("2E524D46", "rm");
head2FileType.put("000001BA", "mpg");
head2FileType.put("000001B3", "mpg");
head2FileType.put("6D6F6F76", "mov");
head2FileType.put("3026B2758E66CF11", "asf");
head2FileType.put("4D546864", "mid");
}
//获取上传文件的文件头
private static String bytesToHexString(String fileName) throws IOException{
FileInputStream fis = null;
StringBuilder stringBuilder = new StringBuilder();
try{
//文件流
fis = new FileInputStream(new File(fileName));
//构建4个byte的数组,用来读取文件头
byte[] b = new byte[4];
//读取文件头
fis.read(b, 0, b.length);
//计算文件头
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
}finally{
if(fis != null)
fis.close();
}
//将最终计算得到的结果转大写并返回给验证方法
return stringBuilder.toString().toUpperCase();
}
//验证文件头是不是在白名单中
public static String fileType(String fileName) throws IOException{
//调用上面文件头获取的方法
String head = bytesToHexString(fileName);
//从白名单中通过文件头取值,如果没有取到说名文件并一在白名单中
return head2FileType.get(head);
}
public static void main(String[] args) throws IOException {
System.out.println(fileType("d://aaa.png"));
}
}
类型溢出
有符号的类型,首高位用0表示正数,用1表示负数。类型溢出是指通过设计让计算结果超过类型最大限制,造成首高位变成1
常见的是int形或者Integer的类型溢出,常常出现在有两个因子进行计算的地方,如:计算总价
检查参于计算的因子是否在计算后或超过限制
在接收到两个因子后直接将因子转成更大的类型,常用BigInteger处理
XSS
保证在页面上不使用innerHTML的方法,用innerText来代替,这样可以保证输出带有编码的内容不会被编译
遵循输入验证和数据净化原则
public static void main(String[] args) {
String s = "\uFE64" + "script" + "\uFE65";
//在验证前进行格式化
s = Normalizer.normalize(s, Normalizer.Form.NFKC);
System.out.println(s);
Pattern pattern = Pattern.compile("[<>]");
Matcher m = pattern.matcher(s);
if(m.find()){
System.out.println("输入不合法");
}else{
System.out.println("输入合法");
}
}
检查输出是否合法
SQL注入
检查SQL语句有没有拼接
检查是否使用预编译(参数模块化)
遵循下列输入验证和数据净化原则(净化穿越受信边界的非受信数据)
文件包含
检查在使用include指令或动作时,参数是否可变
检查可变参数是否受控于服务器
例如:服务器有验证
例如:以列表的形式限制可以引入的文件或页面,客户端根据选择(下列菜单)传入的并非资源名而是代表这个资源的key,通过这个key在列表中去取资源名和路径,能取到则引入取不到则取消(设置失败,白名单)
命令执行
检查Runntime.exec()方法参数是否可变
检查参数是否在白名单中,参考文件包含或文件上传的白名单,将可执行的命令控制在有效的范围内
SSRF
不要在前端暴露服务器请求的信息
<a href="WebDemo/read-file?file=http://192.168.17.64/fileName>读取文件</a>
如果服务器有向其它服务器发送请求,那么这个请求在服务器内部处理,由服务器定位资源的路径、访问的地址等
前端只需要传入资源的名称而不能去定位资源
CSRF
在关键提交出检查是否有Token,Token是否合法
在关键提交出检查提交数据的来源Referer,这并不是最好的途径,因为Referer头可以伪造
在关键提交出严格做法将关键信息封装到session里面和Cookie里面。不要放在URL上面,用Cookie取到的和Session中的比较
XXE
检查是否有实体禁用语句
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
检查DTD文件,实体定义有没有违法语句
如果服务器接收用户提交的XML并过行解析,那么会由服务器定义DTD文件。
过滤用户输入的XML
检查XML中有没有实体引用的关键词
反序列化漏洞
检查序列化方法的正确签名
不要覆写readObject()方法
输入验证和数据净化
净化穿越受信边界的非受信数据
验证前规范化字符串
在验证之前标准化路径名
不要记录未经净化的用户输入
限制传递给ZipInputStream的文件大小
使用ASCII字符集的子集作为文件名和路径名
从格式字符串中排除用户输入
不要向Runntime.exec()方法传递非受信、未净化的数据
净化传递给正则表达式的非受信数据
如果没有指定适当的locale,不要使用locale相关方法处理与locale相关的数据
不要拆分两种数据结构中的字符串
在验证前去掉字符码点
在不同的字符编码中无损转换字符串数据
在文件或者网络I/O两端使用兼容的编码方式