使用JAVA如何對圖片進行格式檢查以及安全檢查處理
一、通常情況下,驗證一個檔案是否圖片,可以通過以下三種方式:
1)、判斷檔案的副檔名是否是要求的圖片副檔名
這種判斷是用得比較多的一種方式,不過這種方式非常的不妥,別人稍微的把一個不是圖片的檔案的副檔名修改為圖片的副檔名,就繞開了你的這種校驗,如果這上傳的檔案是shell、php或者jsp,那你的網站基本上可以說就在別人的手裡面了。
不過這種判斷方式也不是完全沒有用,我們可以把它放在判斷圖片的最外層,如果一個檔案連副檔名都不是我們所要求的圖片副檔名,那就根本不用後面的內 容格式檢查了,從一定程度上說,對減少伺服器的壓力還是有一定的幫助,否則所有的檔案都等上傳完後成後再通過伺服器去判斷,那會在一定程度上浪費器資源 的。
2)、根據檔案的前面幾個位元組,即常說的魔術數字進行判斷,不同檔案型別的開頭幾個位元組,可以檢視我的另外一篇專站介紹:表示不同檔案型別的魔術數字 。
但是這種判斷方式也是非常不靠譜的,因為他只能夠驗證檔案的前面幾個位元組,如此時有人把一個可執行的PHP檔案的副檔名修改為PNG,然後再在前面補上”89 50″兩個位元組,就又繞開了這種驗證方式。
以下是一段通過JAVA程式碼獲取檔案前面兩個位元組的示例程式:
[java] view plain copy print ?- 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
- }
- for ( int i = 0 ; i < src.length; i++) {
- 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));
- }
- }
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++) { 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)); } }
不過這種判斷方式和判斷副檔名一樣,也不是完全沒有用,至少可以在前期在簡單的檢查,為進入下一步檢查做鋪墊。
3)、獲取圖片的寬高屬性
如果能夠正常的獲取到一張圖片的寬高屬性,那肯定這是一張圖片,因為非圖片檔案我們是獲取不到它的寬高屬性的,以下是用於獲取根據是否可以獲取到圖片寬高屬性來判斷這是否一張圖片的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 ;
- }
- }
/**
* 通過讀取檔案並獲取其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實現:
- /**
- * 新增圖片水印
- *
- * @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);
- }
/**
* 新增圖片水印
*
* @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);
}
通過以上幾種方式,應該可以避免絕大部份圖片中帶惡意程式碼的安全問題,不過由於我個人的才疏學淺,可能有沒有考慮周全的地方,還請各位不吝指教了。