实际上只有字符串字面量保存在 Permgen 的字符串池区域。创建的字符串是一次性的。
所以...内存转储可能是基本身份验证的小问题之一。其他是:
- 密码以明文形式通过网络发送。
- 每次请求都会重复发送密码。 (更大的攻击窗口)
- 密码由网络浏览器缓存,至少在窗口/进程的长度内。 (可以由对服务器的任何其他请求静默重用,例如 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(即避免使用字符串)来执行身份验证。
/**
* 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()
公共枚举 getParameterNames()
公共字符串[] getParameterValues(字符串名称)
doFilter() 方法使用包装的请求调用过滤器链,这意味着后续过滤器以及目标 servlet(URL 映射)将被提供包装的请求。