Java判断文件类型 (转)
2014-02-10 11:41
253 查看
通常,在WEB系统中,上传文件时都需要做文件的类型校验,大致有如下几种方法:
1. 通过后缀名,如exe,jpg,bmp,rar,zip等等。
2. 通过读取文件,获取文件的Content-type来判断。
3. 通过读取文件流,根据文件流中特定的一些字节标识来区分不同类型的文件。
4. 若是图片,则通过缩放来判断,可以缩放的为图片,不可以的则不是。
然而,在安全性较高的业务场景中,1,2两种方法的校验会被轻易绕过。
1. 伪造后缀名,如图片的,非常容易修改。
2. 伪造文件的Content-type,这个稍微复杂点,为了直观,截图如下:
3.较安全,但是要读取文件,并有16进制转换等操作,性能稍差,但能满足一定条件下对安全的要求,所以建议使用。
但是文件头的信息也可以伪造,截图如下,对于图片可以采用图片缩放或者获取图片宽高的方法避免伪造头信息漏洞。
被伪装成gif的恶意图片文件
对应的Java代码如下:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
public class FileTypeTest
{
public
final static Map FILE_TYPE_MAP = new HashMap();
private
FileTypeTest(){}
static{
getAllFileType();
//初始化文件类型信息
}
private
static void getAllFileType()
{
FILE_TYPE_MAP.put("jpg",
"FFD8FF"); //JPEG (jpg)
FILE_TYPE_MAP.put("png",
"89504E47"); //PNG (png)
FILE_TYPE_MAP.put("gif",
"47494638"); //GIF (gif)
FILE_TYPE_MAP.put("tif",
"49492A00"); //TIFF (tif)
FILE_TYPE_MAP.put("bmp",
"424D"); //Windows Bitmap (bmp)
FILE_TYPE_MAP.put("dwg",
"41433130"); //CAD (dwg)
FILE_TYPE_MAP.put("html",
"68746D6C3E"); //HTML (html)
FILE_TYPE_MAP.put("rtf",
"7B5C727466"); //Rich Text Format (rtf)
FILE_TYPE_MAP.put("xml",
"3C3F786D6C");
FILE_TYPE_MAP.put("zip",
"504B0304");
FILE_TYPE_MAP.put("rar",
"52617221");
FILE_TYPE_MAP.put("psd",
"38425053"); //Photoshop (psd)
FILE_TYPE_MAP.put("eml",
"44656C69766572792D646174653A");
//Email [thorough only] (eml)
FILE_TYPE_MAP.put("dbx",
"CFAD12FEC5FD746F"); //Outlook Express
(dbx)
FILE_TYPE_MAP.put("pst",
"2142444E"); //Outlook (pst)
FILE_TYPE_MAP.put("xls",
"D0CF11E0"); //MS Word
FILE_TYPE_MAP.put("doc",
"D0CF11E0"); //MS Excel 注意:word 和
excel的文件头一样
FILE_TYPE_MAP.put("mdb",
"5374616E64617264204A"); //MS Access (mdb)
FILE_TYPE_MAP.put("wpd",
"FF575043"); //WordPerfect (wpd)
FILE_TYPE_MAP.put("eps",
"252150532D41646F6265");
FILE_TYPE_MAP.put("ps",
"252150532D41646F6265");
FILE_TYPE_MAP.put("pdf",
"255044462D312E"); //Adobe Acrobat (pdf)
FILE_TYPE_MAP.put("qdf",
"AC9EBD8F"); //Quicken (qdf)
FILE_TYPE_MAP.put("pwl",
"E3828596"); //Windows Password (pwl)
FILE_TYPE_MAP.put("wav",
"57415645"); //Wave (wav)
FILE_TYPE_MAP.put("avi",
"41564920");
FILE_TYPE_MAP.put("ram",
"2E7261FD"); //Real Audio (ram)
FILE_TYPE_MAP.put("rm",
"2E524D46"); //Real Media (rm)
FILE_TYPE_MAP.put("mpg",
"000001BA"); //
FILE_TYPE_MAP.put("mov",
"6D6F6F76"); //Quicktime (mov)
FILE_TYPE_MAP.put("asf",
"3026B2758E66CF11"); //Windows Media (asf)
FILE_TYPE_MAP.put("mid",
"4D546864"); //MIDI (mid)
}
public
static void main(String[] args) throws Exception
{
File
f = new File("c://aaa.gif");
if
(f.exists())
{
String
filetype1 = getImageFileType(f);
System.out.println(filetype1);
String
filetype2 = getFileByFile(f);
System.out.println(filetype2);
}
}
public
final static String getImageFileType(File f)
{
if
(isImage(f))
{
try
{
ImageInputStream
iis = ImageIO.createImageInputStream(f);
Iterator
iter = ImageIO.getImageReaders(iis);
if
(!iter.hasNext())
{
return
null;
}
ImageReader
reader = iter.next();
iis.close();
return
reader.getFormatName();
}
catch
(IOException e)
{
return
null;
}
catch
(Exception e)
{
return
null;
}
}
return
null;
}
public
final static String getFileByFile(File file)
{
String
filetype = null;
byte[]
b = new byte[50];
try
{
InputStream
is = new FileInputStream(file);
is.read(b);
filetype
= getFileTypeByStream(b);
is.close();
}
catch
(FileNotFoundException e)
{
e.printStackTrace();
}
catch
(IOException e)
{
e.printStackTrace();
}
return
filetype;
}
public
final static String getFileTypeByStream(byte[] b)
{
String
filetypeHex = String.valueOf(getFileHexString(b));
Iterator>
entryiterator = FILE_TYPE_MAP.entrySet().iterator();
while
(entryiterator.hasNext()) {
Entry
entry = entryiterator.next();
String
fileTypeHexValue = entry.getValue();
if
(filetypeHex.toUpperCase().startsWith(fileTypeHexValue)) {
return
entry.getKey();
}
}
return
null;
}
public
static final boolean isImage(File file){
boolean
flag = false;
try
{
BufferedImage
bufreader = ImageIO.read(file);
int
width = bufreader.getWidth();
int
height = bufreader.getHeight();
if(width==0
|| height==0){
flag
= false;
}else
{
flag
= true;
}
}
catch
(IOException e)
{
flag
= false;
}catch
(Exception e) {
flag
= false;
}
return
flag;
}
public
final static String getFileHexString(byte[] b)
{
StringBuilder
stringBuilder = new StringBuilder();
if
(b == null || b.length <= 0)
{
return
null;
}
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);
}
return
stringBuilder.toString();
}
}
这样,不管是传入的文件有后缀名,还是无后缀名,或者修改了后缀名,真正获取到的才是该文件的实际类型,这样避免了一些想通过修改后缀名或者Content-type信息来攻击的因素。但是性能与安全永远是无法同时完美的,安全的同时付出了读取文件的代价。本人建议可采用后缀名与读取文件的方式结合校验,毕竟攻击是少数,后缀名的校验能排除大多数用户,在后缀名获取不到时再通过获取文件真实类型校验,这样来适当提高性能。
1. 通过后缀名,如exe,jpg,bmp,rar,zip等等。
2. 通过读取文件,获取文件的Content-type来判断。
3. 通过读取文件流,根据文件流中特定的一些字节标识来区分不同类型的文件。
4. 若是图片,则通过缩放来判断,可以缩放的为图片,不可以的则不是。
然而,在安全性较高的业务场景中,1,2两种方法的校验会被轻易绕过。
1. 伪造后缀名,如图片的,非常容易修改。
2. 伪造文件的Content-type,这个稍微复杂点,为了直观,截图如下:
3.较安全,但是要读取文件,并有16进制转换等操作,性能稍差,但能满足一定条件下对安全的要求,所以建议使用。
但是文件头的信息也可以伪造,截图如下,对于图片可以采用图片缩放或者获取图片宽高的方法避免伪造头信息漏洞。
被伪装成gif的恶意图片文件
对应的Java代码如下:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
public class FileTypeTest
{
public
final static Map FILE_TYPE_MAP = new HashMap();
private
FileTypeTest(){}
static{
getAllFileType();
//初始化文件类型信息
}
private
static void getAllFileType()
{
FILE_TYPE_MAP.put("jpg",
"FFD8FF"); //JPEG (jpg)
FILE_TYPE_MAP.put("png",
"89504E47"); //PNG (png)
FILE_TYPE_MAP.put("gif",
"47494638"); //GIF (gif)
FILE_TYPE_MAP.put("tif",
"49492A00"); //TIFF (tif)
FILE_TYPE_MAP.put("bmp",
"424D"); //Windows Bitmap (bmp)
FILE_TYPE_MAP.put("dwg",
"41433130"); //CAD (dwg)
FILE_TYPE_MAP.put("html",
"68746D6C3E"); //HTML (html)
FILE_TYPE_MAP.put("rtf",
"7B5C727466"); //Rich Text Format (rtf)
FILE_TYPE_MAP.put("xml",
"3C3F786D6C");
FILE_TYPE_MAP.put("zip",
"504B0304");
FILE_TYPE_MAP.put("rar",
"52617221");
FILE_TYPE_MAP.put("psd",
"38425053"); //Photoshop (psd)
FILE_TYPE_MAP.put("eml",
"44656C69766572792D646174653A");
//Email [thorough only] (eml)
FILE_TYPE_MAP.put("dbx",
"CFAD12FEC5FD746F"); //Outlook Express
(dbx)
FILE_TYPE_MAP.put("pst",
"2142444E"); //Outlook (pst)
FILE_TYPE_MAP.put("xls",
"D0CF11E0"); //MS Word
FILE_TYPE_MAP.put("doc",
"D0CF11E0"); //MS Excel 注意:word 和
excel的文件头一样
FILE_TYPE_MAP.put("mdb",
"5374616E64617264204A"); //MS Access (mdb)
FILE_TYPE_MAP.put("wpd",
"FF575043"); //WordPerfect (wpd)
FILE_TYPE_MAP.put("eps",
"252150532D41646F6265");
FILE_TYPE_MAP.put("ps",
"252150532D41646F6265");
FILE_TYPE_MAP.put("pdf",
"255044462D312E"); //Adobe Acrobat (pdf)
FILE_TYPE_MAP.put("qdf",
"AC9EBD8F"); //Quicken (qdf)
FILE_TYPE_MAP.put("pwl",
"E3828596"); //Windows Password (pwl)
FILE_TYPE_MAP.put("wav",
"57415645"); //Wave (wav)
FILE_TYPE_MAP.put("avi",
"41564920");
FILE_TYPE_MAP.put("ram",
"2E7261FD"); //Real Audio (ram)
FILE_TYPE_MAP.put("rm",
"2E524D46"); //Real Media (rm)
FILE_TYPE_MAP.put("mpg",
"000001BA"); //
FILE_TYPE_MAP.put("mov",
"6D6F6F76"); //Quicktime (mov)
FILE_TYPE_MAP.put("asf",
"3026B2758E66CF11"); //Windows Media (asf)
FILE_TYPE_MAP.put("mid",
"4D546864"); //MIDI (mid)
}
public
static void main(String[] args) throws Exception
{
File
f = new File("c://aaa.gif");
if
(f.exists())
{
String
filetype1 = getImageFileType(f);
System.out.println(filetype1);
String
filetype2 = getFileByFile(f);
System.out.println(filetype2);
}
}
public
final static String getImageFileType(File f)
{
if
(isImage(f))
{
try
{
ImageInputStream
iis = ImageIO.createImageInputStream(f);
Iterator
iter = ImageIO.getImageReaders(iis);
if
(!iter.hasNext())
{
return
null;
}
ImageReader
reader = iter.next();
iis.close();
return
reader.getFormatName();
}
catch
(IOException e)
{
return
null;
}
catch
(Exception e)
{
return
null;
}
}
return
null;
}
public
final static String getFileByFile(File file)
{
String
filetype = null;
byte[]
b = new byte[50];
try
{
InputStream
is = new FileInputStream(file);
is.read(b);
filetype
= getFileTypeByStream(b);
is.close();
}
catch
(FileNotFoundException e)
{
e.printStackTrace();
}
catch
(IOException e)
{
e.printStackTrace();
}
return
filetype;
}
public
final static String getFileTypeByStream(byte[] b)
{
String
filetypeHex = String.valueOf(getFileHexString(b));
Iterator>
entryiterator = FILE_TYPE_MAP.entrySet().iterator();
while
(entryiterator.hasNext()) {
Entry
entry = entryiterator.next();
String
fileTypeHexValue = entry.getValue();
if
(filetypeHex.toUpperCase().startsWith(fileTypeHexValue)) {
return
entry.getKey();
}
}
return
null;
}
public
static final boolean isImage(File file){
boolean
flag = false;
try
{
BufferedImage
bufreader = ImageIO.read(file);
int
width = bufreader.getWidth();
int
height = bufreader.getHeight();
if(width==0
|| height==0){
flag
= false;
}else
{
flag
= true;
}
}
catch
(IOException e)
{
flag
= false;
}catch
(Exception e) {
flag
= false;
}
return
flag;
}
public
final static String getFileHexString(byte[] b)
{
StringBuilder
stringBuilder = new StringBuilder();
if
(b == null || b.length <= 0)
{
return
null;
}
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);
}
return
stringBuilder.toString();
}
}
这样,不管是传入的文件有后缀名,还是无后缀名,或者修改了后缀名,真正获取到的才是该文件的实际类型,这样避免了一些想通过修改后缀名或者Content-type信息来攻击的因素。但是性能与安全永远是无法同时完美的,安全的同时付出了读取文件的代价。本人建议可采用后缀名与读取文件的方式结合校验,毕竟攻击是少数,后缀名的校验能排除大多数用户,在后缀名获取不到时再通过获取文件真实类型校验,这样来适当提高性能。
相关文章推荐
- java&nbsp;判断&nbsp;android、iph…
- java判断标点符号问题
- java判断操作系统是windows还是linux
- Java中用正则表达式判断日期格式是否正确
- Java判断对象或者原始数据类型是否是数组
- java 判断一个字符串是否包含某个字符串中的字符
- java&nbsp;温习之字符串(获取两个…
- java File 类 判断文件路径是否存在并创建文件
- JAVA判断文件编码类型
- Java 判断文件夹、文件是否存在、否则创建文件夹
- [Java] 练习题014: 输入某年某月某日,判断这一天是这一年的第几天?
- JAVA判断中文字符
- java判断字符串中是否包含中文并过滤中文
- Java判断文件编码格式
- java 获取elasticsearch Client以及 判断是否存在此索引
- 【Java】在Java中如何判断一个对象属于一个类
- 判断是否是回文数字(java版)
- tomcat启动java web项目时报…
- java 反射机制
- Java怎么判断一个静态方法是否要加synchronized?