It's wired, but is true.
I use jmeter send http request which includes a Cookie which is like data=5ExJK/9ZyDsbiA8cuUXCBQ==. I can see the value on View Results Tree.
But when on tomcat servlet,
Cookie[] cookies = req.getCookies();
String[] cooks = null;
String username = "";
if (cookies != null) {
for (Cookie coo : cookies) {
if(coo.getName().equals("data")){
String encrypt = coo.getValue();
.....
variable encrypt becomes 5ExJK/9ZyDsbiA8cuUXCBQ. double = disappear.
When use fiddle as proxy to see the Cookie, I can see the double = with my eyes.
when use browser, variable encrypt becomes to have double =.
what has happen in tomcat servlet? how to fix it?
jmeter 3.0 r1743807,
tomcat 8.0.28
Related
I'm very new to Java Web programming and I am trying to write a method to authenticate a user, store a token in a cookie, and pass them onto the next page. I'm hung up on what the return type of the authentication should be. Should it return a Cookie object directly as the return value of authenticateUser()?
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response authenticateUser(#FormParam("username") String username,
#FormParam("password") String password) {
try {
// Authenticate the user using the credentials provided
authenticate(username, password);
// Issue a token for the user
_logger.log(Level.INFO, "----ABOUT TO LOG TOKEN TO WILDFLY");
String token = issueToken(username,"http://example.com","userToken",msInHour); //returns JWT token
_logger.log(Level.INFO, "----LOGGING TOKEN TO WILDFLY: ",token);
// Return the token on the response
//return Response.ok(token).build();
Response.createCookie(createCookie(token,username)).build();
} catch (Exception e) {
_logger.log(Level.INFO, "----ERROR in AuthService:",e);
return Response.status(Response.Status.FORBIDDEN).build();
}
}
private Cookie createCookie(String token,String uname){
//https://stackoverflow.com/questions/8889679/how-to-create-a-cookie-and-add-to-http-response-from-inside-my-service-layer
final Boolean useSecureCookie = true;
final int expiryTime = 60 * 60 * 24; // 24h in seconds
final String cookiePath = "/";
Cookie cookie = new Cookie("example.com", uname+"_"+token);
cookie.setSecure(useSecureCookie); // determines whether the cookie should only be sent using a secure protocol, such as HTTPS or SSL
cookie.setMaxAge(expiryTime); // A negative value means that the cookie is not stored persistently and will be deleted when the Web browser exits. A zero value causes the cookie to be deleted.
cookie.setHttpOnly(true);
cookie.setPath(cookiePath); // The cookie is visible to all the pages in the directory you specify, and all the pages in that directory's subdirectories
return cookie;
}
You seem to be using Jersey as your backend framework so you need to look at their documentation to see how setting cookies works. And no, you more than probably can't return the plain cookie as the browser won't understand it as such, especially since you're "producing" JSON.
How to set cookie in Jersey.
As you can see from the accepted answer the Response object should have a separate method for setting cookies, aptly named cookie().
I want to accept data from a client.
What are the pros and cons of each approach?
HttpServletRequest request = retriveRequest();
Cookie [] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if ("my-cookie-name".equals(cookie.getName())) {
String value = cookie.getValue();
//do something with the cookie's value.
}
}
or
String request.getHeader("header-name");
As I read How are cookies passed in the HTTP protocol?
Cookies are passed as HTTP headers, both in the request (client -> server), and in the response (server -> client).
getCookies, frees you from parsing the Cookie header string, and creating a java object out of it. Otherwise you will have to do something like:
String rawCookie = request.getHeader("Cookie");
String[] rawCookieParams = rawCookie.split(";");
for(String rawCookieNameAndValue :rawCookieParams)
{
String[] rawCookieNameAndValuePair = rawCookieNameAndValue.split("=");
}
// so on and so forth.
I have a filter that handles BASIC authentication over HTTPS. That means there's a header coming in named "Authorization" and the value is something like "Basic aGVsbG86c3RhY2tvdmVyZmxvdw==".
I'm not concerned with how to handle the authentication, the 401 plus WWW-Authenticate response header, JDBC lookup or anything like that. My filter works beautifully.
My concern is that we should never store a user password in a java.lang.String because they're immutable. I can't zero out that String as soon as I'm done authenticating. That object will sit in memory until the garbage collector runs. That leaves open a much wider window for a bad guy to get a core dump or somehow otherwise observe the heap.
The problem is that the only way I see to read that Authorization header is via the javax.servlet.http.HttpServletRequest.getHeader(String) method, but it returns a String. I need a getHeader method that returns an array of bytes or chars. Ideally, the request should never be a String at any point in time, from Socket to HttpServletRequest and everywhere in between.
If I switched to some flavor of form-based security, the problem still exists. javax.servlet.ServletRequest.getParameter(String) returns a String too.
Is this simply a limitation of Java EE?
Actually only string literals are keeped in String Pool area of Permgen. Created Strings are disposables.
So... Probably memory dump is one of minor problems with Basic Authentication. Others are:
The password is sent over the wire in plaintext.
The password is sent repeatedly, for each request. (Larger attack window)
The password is cached by the webbrowser, at a minimum for the length of the window / process. (Can be silently reused by any other request to the server, e.g. CSRF).
The password may be stored permanently in the browser, if the user requests. (Same as previous point, in addition might be stolen by another user on a shared machine).
Even using SSL, internal servers (behind of SSL protocol) will have access to plain text cacheable password.
At the same time, Java container has already parsed HTTP request and populate object. So, that's why you get String from request header. You probably should rewrite the Web Container to parse safety HTTP request.
Update
I was wrong. At least to Apache Tomcat.
http://alvinalexander.com/java/jwarehouse/apache-tomcat-6.0.16/java/org/apache/catalina/authenticator/BasicAuthenticator.java.shtml
How you can see, BasicAuthenticator from Tomcat project use MessageBytes (i.e. avoiding String) to perform the authentication.
/**
* 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);
}
As long you have access to org.apache.catalina.connector.Request, no worries.
So, How can you avoid parsing of HTTP request
There's an amazing answer here in stackoverflow detailing
Use servlet filter to remove a form parameter from posted data
and an important explanation:
Approach
The code follows the correct approach:
in wrapRequest(), it instantiates HttpServletRequestWrapper and overrides the 4 methods that trigger request parsing:
public String getParameter(String name)
public Map getParameterMap()
public Enumeration getParameterNames()
public String[] getParameterValues(String name)
the doFilter() method invokes the filter chain using the wrapped request, meaning subsequent filters, plus the target servlet (URL-mapped) will be supplied the wrapped request.
Thats correct but it should never store in db an actual password to check against but instead a hash on the password itself then run a hash to determine if the two hashes are identical which a password is never used but by the original user.
if you are that concerned then use ServletRequest.getInputStream() instead of HttpServletRequest.getHeader(String) in your Filter. You should be able get your HTTP request as a stream, skip till you get to Authorization header and get your password in a char [].
But all this effort might be futile as the underlying object is still a HTTPServletRequest and might contain all the headers as key val pairs in a map, details being subject to how servlet is implemented.
I'm having issues invoking twitter REST API using Google OAuth Java Client. I'm able to do the first steps correctly:
Set the authorization URL,
Get the temporary token,
Generate the final token.
Then the OAuth Javadoc says:
Use the stored access token to authorize HTTP requests to protected
resources by setting the OAuthParameters.token and using
OAuthParameters as the HttpRequestInitializer.
It's in this step that I have issues. First of all if I only set the OAuthParameters.token value I'll get a null exception because the signer isn't set so what I presently have is:
OAuthHmacSigner signer = new OAuthHmacSigner();
signer.clientSharedSecret=TWITTER_CONSUMER_SECRET;
String oauthToken = req.getParameter("oauth_token");
String oauthVerifier = req.getParameter("oauth_verifier");
OAuthGetAccessToken accessTokenRequest = new OAuthGetAccessToken(TWITTER_ACESS_TOKEN_URL);
accessTokenRequest.consumerKey=TWITTER_CONSUMER_KEY;
accessTokenRequest.signer=signer;
accessTokenRequest.transport=HTTP_TRANSPORT;
accessTokenRequest.temporaryToken=oauthToken;
accessTokenRequest.verifier=oauthVerifier;
OAuthCredentialsResponse credentials = accessTokenRequest.execute();
String token = credentials.token;
OAuthParameters params = new OAuthParameters();
params.token=token;
params.version="1.0";
params.consumerKey=TWITTER_CONSUMER_KEY;
params.signer=signer;
HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(params);
HttpResponse twResponse = requestFactory.buildGetRequest(new GenericUrl("https://api.twitter.com/1.1/account/verify_credentials.json")).execute();
The result is always:
WARNING: Authentication error: Unable to respond to any of these
challenges: {} com.google.api.client.http.HttpResponseException: 401
OK {"errors":[{"message":"Could not authenticate you","code":32}]}
If I try the Authorization header given by Twitter OAuth tool through a REST Chrome extension tool it works perfectly so it's not an account issue. When I change it for the Authorization header value computed by the Google OAuth Java client library it doesn't work.
I don't get what I'm doing wrong.
Solution: Follow the tutorial in the link provided by #Arkanon, I missed refreshing the signer token secrete through:
signer.tokenSharedSecret
I just modified the code on this page about using google-oauth-java-client to send a request to Twitter and it worked fine once I replaced the relevant block with this:
while (currentLine.equalsIgnoreCase("n")) {
System.out.println("Enter the verification PIN provided by Twitter:");
currentLine = in.readLine();
}
and then added the following to the accessToken object:
accessToken.verifier = currentLine;
Once the PIN provided by the Twitter site is typed into the Java console and you hit Enter, the process completes and the protected resource can be accessed and the desired JSON response is received.
The only other changes I made to that code were to provide the Twitter constants as follows:
private static final String CONSUMER_KEY =
"enter-your-consumer-key-here";
private static final String CONSUMER_SECRET =
"enter-your-consumer-secret-here";
private static final String PROTECTED_SERVICE_URL =
"https://api.twitter.com/1.1/statuses/home_timeline.json";
private static final String REQUEST_TOKEN_URL =
"https://api.twitter.com/oauth/request_token";
private static final String AUTHORIZE_URL =
"https://api.twitter.com/oauth/authenticate";
private static final String ACCESS_TOKEN_URL =
"https://api.twitter.com/oauth/access_token";
Maybe this is not the exact same process you're hoping to achieve, but hopefully the code on that page will help you to spot anything you might have misunderstood. (And I agree that the documentation for the Google libraries is not all it could be.)
I'm trying to get cookie in servlet using
Cookie[] cookie = request.getCookies();
but cookie is always null.
So I set them from another servlet and they appear in browser preferences.
Cookie cookie = new Cookie("color", "cyan");
cookie.setMaxAge(24*60*60);
cookie.setPath("/");
response.addCookie(cookie);
I don't understand what's wrong?
According to docs getCookies() Returns an array containing all of the Cookie objects the client sent with this request. This method returns null if no cookies were sent.
Did you add the cookie correctly? If yes, you should be able to iterate through the list of cookies returned with
Cookie[] cookies = request.getCookies();
for (int i = 0; i < cookies.length; i++) {
String name = cookies[i].getName();
String value = cookies[i].getValue();
}
If no...
Cookies are added with the addCookie(Cookie) method in the response object!
SET COOKIE
Cookie cookie = new Cookie("cookiename", "cookievalue");
response.addCookie(cookie);
GET COOKIE
Cookie[] cookies = request.getCookies();
if(cookies != null) {
for (int i = 0; i < cookies.length; i++) {
cookie=cookies[i]
String cookieName = cookie.getName();
String cookieValue = cookie.getValue();
}
}
Are you sure the client supports cookies? because if it is configure to NOT accept cookies, you will never get them back on a following request...
I had the same problem and discovered the cause in my case was that I was using the browser built into Eclipse. This does not accept cookies. When I accessed the same JSP from chrome, it worked. Perhaps you are doing the same thing I did?
It may also be the case that the browser you are using or your internet settings are set to reject cookies. Hope this helps you or any other visitor experiencing the same issue.
firstly ,you should create a cookie,and then add to response
Cookie cookie = new Cookie(name,value);
response.addCookie(cookie);