您的位置:首页 > 其它

如何更好的判断系统上传文件是指定文件类型--文件魔术数字

2016-03-03 15:06 411 查看

理论介绍

这里所说的表示不同文件类型的魔术数字,指定是文件的最开头的几个用于唯一区别其它文件类型的字节,有了这些魔术数字,我们就可以很方便的区别不同的文件,这也使得编程变得更加容易,因为我减少了我们用于区别一个文件的文件类型所要花费的时间。

比如,一个JPEG文件,它开头的一些字节可能是类似这样的”ffd8 ffe0 0010 4a46 4946 0001 0101 0047 ……JFIF…..G“,这里”ffd8“就表示了这个文件是一个JPEG类型的文件,”ffe0“表示这是JFIF类型结构。

以下例出的是一些我们常见的文件类型,以及它用于判断这种文件的类型的几个开始字节及所对尖的ASCII数字:

图片文件

文件类型扩展名16进制数字,xx这里表示变量Ascii数字 . = 不是Ascii字符
Bitmap format.bmp42 4dBM
FITS format.fits53 49 4d 50 4c 45SIMPLE
GIF format.gif47 49 46 38GIF8
Graphics Kernel System.gks47 4b 53 4dGKSM
IRIS rgb format.rgb01 da..
ITC (CMU WM) format.itcf1 00 40 bb….
JPEG File Interchange Format.jpgff d8 ff e0….
NIFF (Navy TIFF).nif49 49 4e 31IIN1
PM format.pm56 49 45 57VIEW
PNG format.png89 50 4e 47.PNG
Postscript format.[e]ps25 21%!
Sun Rasterfile.ras59 a6 6a 95Y.j.
Targa format.tgaxx xx xx
TIFF format (Motorola – big endian).tif4d 4d 00 2aMM.*
TIFF format (Intel – little endian).tif49 49 2a 00II*.
X11 Bitmap format.xbmxx xx
XCF Gimp file structure.xcf67 69 6d 70 20 78 63 66 20 76gimp xcf
Xfig format.fig23 46 49 47#FIG
XPM format.xpm2f 2a 20 58 50 4d 20 2a 2f/* XPM */

压缩文件

文件类型扩展名16进制数字 xx这里表示变量Ascii数字. = 不是Ascii字符
Bzip.bz42 5aBZ
Compress.Z1f 9d..
gzip format.gz1f 8b..
pkzip format.zip50 4b 03 04PK..

存档文件

文件类型扩展名16进制数字xx这里表示变量Ascii数字. = 不是Ascii字符
TAR (pre-POSIX).tarxx xx(a filename)
TAR (POSIX).tar75 73 74 61 72ustar (offset by 257 bytes)

可执行文件

文件类型扩展名16进制数字xx这里表示变量Ascii数字. = 不是Ascii字符
MS-DOS, OS/2 or MS Windows4d 5aMZ
Unix elf7f 45 4c 46.ELF

其它文件

文件类型扩展名16进制数字xx这里表示变量Ascii数字. = 不是Ascii字符
pgp public ring99 00..
pgp security ring95 01..
pgp security ring95 00..
pgp encrypted dataa6 00¦.

java实现(图片类型判断)

使用魔术数字判断

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ImageTypeCheck {

public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
// byte转int
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
public static void main(String[] args) throws IOException {
String imagePath = "c:/favicon.png";
File image = new File(imagePath);
InputStream is = new FileInputStream(image);
byte[] bt = new byte[2];
is.read(bt);
System.out.println(bytesToHexString(bt));
}
}


使用图片长宽判断

如果能够正常的获取到一张图片的宽高属性,那肯定这是一张图片,因为非图片文件我们是获取不到它的宽高属性的,以下是用于获取根据是否可以获取到图片宽高属性来判断这是否一张图片的JAVA代码:

/**
* 通过读取文件并获取其width及height的方式,来判断判断当前文件是否图片,这是一种非常简单的方式。
*
* @param imageFile
* @return
*/
public static boolean isImage(File imageFile) {
if (!imageFile.exists()) {
return false;
}
Image img = null;
try {
img = ImageIO.read(imageFile);
if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) {
return false;
}
return true;
} catch (Exception e) {
return false;
} finally {
img = null;
}
}


好了,我们终于判断出一个文件是否图片了,可是如果是在一个可以正常浏览的图片文件中加入一些非法的代码呢:



这就是在一张正常的图片末尾增加的一些iframe代码,我曾经尝试过单独打开这张图片,也将这张图片放于网页上打开,虽然这样都不会被执行,但并不代表插入其它的代码也并不会执行,杀毒软件(如AVAST)对这种修改是会报为病毒的。

那我们要如何预防这种东西,即可以正常打开,又具有正确的图片文件扩展名,还可以获取到它的宽高属性?呵,我们这个时候可以对这个图片进地重写,给它增加水印或者对它进行resize操作,这样新生成的图片就不会再包含这样的恶意代码了,以下是一个增加水印的JAVA实现:

增加水印的JAVA实现

/**
* 添加图片水印
*
* @param srcImg 目标图片路径,如:C:\\kutuku.jpg
* @param waterImg 水印图片路径,如:C:\\kutuku.png
* @param x 水印图片距离目标图片左侧的偏移量,如果x<0, 则在正中间
* @param y 水印图片距离目标图片上侧的偏移量,如果y<0, 则在正中间
* @param alpha 透明度(0.0 -- 1.0, 0.0为完全透明,1.0为完全不透明)
* @throws IOException
*/
public final static void addWaterMark(String srcImg, String waterImg, int x, int y, float alpha) throws IOException {
// 加载目标图片
File file = new File(srcImg);
String ext = srcImg.substring(srcImg.lastIndexOf(".") + 1);
Image image = ImageIO.read(file);
int width = image.getWidth(null);
int height = image.getHeight(null);

// 将目标图片加载到内存。
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(image, 0, 0, width, height, null);

// 加载水印图片。
Image waterImage = ImageIO.read(new File(waterImg));
int width_1 = waterImage.getWidth(null);
int height_1 = waterImage.getHeight(null);
// 设置水印图片的透明度。
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));

// 设置水印图片的位置。
int widthDiff = width - width_1;
int heightDiff = height - height_1;
if (x < 0) {
x = widthDiff / 2;
} else if (x > widthDiff) {
x = widthDiff;
}
if (y < 0) {
y = heightDiff / 2;
} else if (y > heightDiff) {
y = heightDiff;
}

// 将水印图片“画”在原有的图片的制定位置。
g.drawImage(waterImage, x, y, width_1, height_1, null);
// 关闭画笔。
g.dispose();

// 保存目标图片。
ImageIO.write(bufferedImage, ext, file);
}


整理参考:

/article/2658322.html

/content/1894754.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: