实际上,只有字符串文本保留在Permgen的字符串池区域中。创建的字符串是一次性的。
所以……内存转储可能是基本身份验证的一个小问题。其他包括:
-
密码以明文形式通过网络发送。
-
每次请求都会重复发送密码。(较大的攻击窗口)
-
密码由Web浏览器缓存,至少为窗口/进程的长度。(可以通过对服务器的任何其他请求(例如CSRF)进行静默重用)。
-
如果用户请求,密码可以永久存储在浏览器中。(与上一点相同,此外,共享计算机上的其他用户可能会窃取)。
-
即使使用SSL,内部服务器(SSL协议后面)也可以访问纯文本可缓存密码。
同时,Java容器已经解析了HTTP请求并填充了对象。所以,这就是为什么从请求头获取String。您可能应该重写Web容器以解析安全HTTP请求。
使现代化
我错了。至少适用于Apache Tomcat。
http://alvinalexander.com/java/jwarehouse/apache-tomcat-6.0.16/java/org/apache/catalina/authenticator/BasicAuthenticator.java.shtml
您可以看到,Tomcat项目中的BasicAuthenticator使用MessageBytes(即避免使用String)来执行身份验证。
/**
* Authenticate the user making this request, based on the specified
* login configuration. Return <code>true if any specified
* constraint has been satisfied, or <code>false if we have
* created a response challenge already.
*
* @param request Request we are processing
* @param response Response we are creating
* @param config Login configuration describing how authentication
* should be performed
*
* @exception IOException if an input/output error occurs
*/
public boolean authenticate(Request request,
Response response,
LoginConfig config)
throws IOException {
// Have we already authenticated someone?
Principal principal = request.getUserPrincipal();
String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
if (principal != null) {
if (log.isDebugEnabled())
log.debug("Already authenticated '" + principal.getName() + "'");
// Associate the session with any existing SSO session
if (ssoId != null)
associate(ssoId, request.getSessionInternal(true));
return (true);
}
// Is there an SSO session against which we can try to reauthenticate?
if (ssoId != null) {
if (log.isDebugEnabled())
log.debug("SSO Id " + ssoId + " set; attempting " +
"reauthentication");
/* Try to reauthenticate using data cached by SSO. If this fails,
either the original SSO logon was of DIGEST or SSL (which
we can't reauthenticate ourselves because there is no
cached username and password), or the realm denied
the user's reauthentication for some reason.
In either case we have to prompt the user for a logon */
if (reauthenticateFromSSO(ssoId, request))
return true;
}
// Validate any credentials already included with this request
String username = null;
String password = null;
MessageBytes authorization =
request.getCoyoteRequest().getMimeHeaders()
.getValue("authorization");
if (authorization != null) {
authorization.toBytes();
ByteChunk authorizationBC = authorization.getByteChunk();
if (authorizationBC.startsWithIgnoreCase("basic ", 0)) {
authorizationBC.setOffset(authorizationBC.getOffset() + 6);
// FIXME: Add trimming
// authorizationBC.trim();
CharChunk authorizationCC = authorization.getCharChunk();
Base64.decode(authorizationBC, authorizationCC);
// Get username and password
int colon = authorizationCC.indexOf(':');
if (colon < 0) {
username = authorizationCC.toString();
} else {
char[] buf = authorizationCC.getBuffer();
username = new String(buf, 0, colon);
password = new String(buf, colon + 1,
authorizationCC.getEnd() - colon - 1);
}
authorizationBC.setOffset(authorizationBC.getOffset() - 6);
}
principal = context.getRealm().authenticate(username, password);
if (principal != null) {
register(request, response, principal, Constants.BASIC_METHOD,
username, password);
return (true);
}
}
// Send an "unauthorized" response and an appropriate challenge
MessageBytes authenticate =
response.getCoyoteResponse().getMimeHeaders()
.addValue(AUTHENTICATE_BYTES, 0, AUTHENTICATE_BYTES.length);
CharChunk authenticateCC = authenticate.getCharChunk();
authenticateCC.append("Basic realm=\"");
if (config.getRealmName() == null) {
authenticateCC.append(request.getServerName());
authenticateCC.append(':');
authenticateCC.append(Integer.toString(request.getServerPort()));
} else {
authenticateCC.append(config.getRealmName());
}
authenticateCC.append('\"');
authenticate.toChars();
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
//response.flushBuffer();
return (false);
}
只要您可以访问org.apache.catalina.connector.Request,就不用担心。
那么,如何避免解析HTTP请求
stackoverflow细节中有一个惊人的答案
Use servlet filter to remove a form parameter from posted data
以及一个重要的解释:
方法
代码遵循正确的方法:
在wrapRequest()中,它实例化HttpServletRequestWrapper并重写触发请求解析的4个方法:
public String getParameter(字符串名称)
公共映射getParameterMap()
公共枚举getParameterName()
public String[]getParameterValues(字符串名称)
doFilter()方法使用包装的请求调用过滤器链,这意味着后续过滤器以及目标servlet(URL映射)将被提供包装的请求。