boolean validFileName = ESAPI.validator().isValidFileName("文件名", fileName, false);
源码解析:
isValidFileName()同名方法共有三个
boolean isValidFileName(String context, String input, boolean allowNull) throws IntrusionException;
boolean isValidFileName(String context, String input, boolean allowNull, ValidationErrorList errorList) throws IntrusionException;
boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws IntrusionException;
区别在于是否有自定义的白名单后缀,是否有自定义的黑名单。
context:用户出错时日志中的标识
input:待检查的文件名
allowNull:是否允许文件名为空
errorList:自定义的黑名单
allowedExtensions:自定义的白名单
public boolean isValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
try {
getValidFileName( context, input, allowedExtensions, allowNull);
return true;
} catch( ValidationException e ) {
errors.addError(context, e);
return false;
}
}
若没有自定义的白名单,则使用系统默认配置ESAPI.securityConfiguration().getAllowedFileExtensions()
(这个内容可以在ESAPI.properties
文件中查找HttpUtilities.ApprovedUploadExtensions
)
public String getValidFileName(String context, String input, List<String> allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException {
if ((allowedExtensions == null) || (allowedExtensions.isEmpty())) {
throw new ValidationException( "Internal Error", "getValidFileName called with an empty or null list of allowed Extensions, therefore no files can be uploaded" );
}
String canonical = "";
// detect path manipulation
try {
if (isEmpty(input)) {
if (allowNull) return null;
throw new ValidationException( context + ": Input file name required", "Input required: context=" + context + ", input=" + input, context );
}
// do basic validation
canonical = new File(input).getCanonicalFile().getName();
getValidInput( context, input, "FileName", 255, true );
File f = new File(canonical);
String c = f.getCanonicalPath();
String cpath = c.substring(c.lastIndexOf(File.separator) + 1);
// the path is valid if the input matches the canonical path
if (!input.equals(cpath)) {
throw new ValidationException( context + ": Invalid file name", "Invalid directory name does not match the canonical path: context=" + context + ", input=" + input + ", canonical=" + canonical, context );
}
} catch (IOException e) {
throw new ValidationException( context + ": Invalid file name", "Invalid file name does not exist: context=" + context + ", canonical=" + canonical, e, context );
}
// verify extensions
Iterator<String> i = allowedExtensions.iterator();
while (i.hasNext()) {
String ext = i.next();
if (input.toLowerCase().endsWith(ext.toLowerCase())) {
return canonical;
}
}
throw new ValidationException( context + ": Invalid file name does not have valid extension ( "+allowedExtensions+")", "Invalid file name does not have valid extension ( "+allowedExtensions+"): context=" + context+", input=" + input, context );
}
从上面的代码中,可以看到中间还有一层验证getValidInput( context, input, "FileName", 255, true );
具体的规则可以在ESAPI.properties
中查找Validator.FileName
ServletFileUpload upload=newServletFileUpload(factory);
upload.setSizeMax(maxBytes);
实际上现在流行的框架都已经集成了文件大小设置,比如SpringMVC
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件大小上限,单位为字节(10MB) -->
<property name="maxUploadSize">
<value>10485760</value>
</property>
<property name="defaultEncoding">
<value>UTF-8</value>
</property>
</bean>
在实际业务代码中,个人建议文件名由系统生成(规则可以是md5(用户名)+System.currentTimeMillis()),文件后缀由系统白名单控制,文件大小由配置文件控制。