Checkmarx complains "Method changePassword defines oldPassword, which is designated to contain user passwords. However, while plaintext passwords are later assigned to oldPassword, this variable is never cleared from memory. It this a false positive?
#PutMapping(path = "/changepassword", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<String> changePassword(#RequestBody UserUpdate user, HttpServletRequest request, HttpServletResponse response) {
String uid= user.getId();
String oldPassword = user.getOldPwrd();
String newPassword = user.getPwrd();
userDetails.changeUserPassword(uid, oldPassword, newPassword);
return ResponseEntity.ok(SUCCESS);
}
It is considered as a best security practice to not store passwords in immutable strings and use an encrypted memory object such as SealedObject. This specialized class can store encrypted data in memory and helps ensure that it can't be easily retrieved from memory.
#PutMapping(path = "/changepassword", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<String> changePassword(#RequestBody UserUpdate user, HttpServletRequest request, HttpServletResponse response) {
String uid= user.getId();
SealedObject oldPassword = user.getOldPwrd();
SealedObject newPassword = user.getPwrd();
userDetails.changeUserPassword(uid, oldPassword, newPassword);
return ResponseEntity.ok(SUCCESS);
}
You will have to change your changeUserPassword method to handle SealedObject which involves defining a cipher and key for encryption:
Key key = getKeyFromConfiguration();
Cipher c = Cipher.getInstance(CIPHER_NAME);
c.init(Cipher.ENCRYPT_MODE, key);
List<Character> characterList = Arrays.asList(input);
password = new SealedObject((Serializable) characterList, c);
Arrays.fill(input, '\0');
Related
My api is not detecting any of the RequestParams. However it works fine with Postman, just not when being called from React. I've also verified that the formData is being loaded correctly via console.log statements:
formData = file,[object File],uploadId,173,uploadTypeId,1,dateEntered,1625025600000,description,Fourth Addendum testing,enteredBy,769
Does the formData object need to be stringified or something?
Can someone tell me what I'm doing wrong? I'm getting the following error:
Required request parameter 'uploadId' for method parameter type Long is not present
Here is my React snippet:
let formData = new FormData();
formData.append("file", file);
formData.append("uploadId",uploadId);
formData.append("uploadTypeId",uploadTypeId);
formData.append("dateEntered",dateEntered);
formData.append("description",description);
formData.append("enteredBy",enteredBy);
let myHeaders = new Headers();
myHeaders.append("Content-Type", "multipart/form-data; boundary=XXX");
myHeaders.append("type", "formData");
const serverName = "http://localhost:8080/MyApp/api/uploads/update/;
myHeaders.append("Access-Control-Allow-Credentials", 'true');
myHeaders.append("Access-Control-Allow-Origin", '*');
const jwt = sessionStorage.getItem("jwt");
//console.log("fetch jwt = ", jwt) ;
let headerJwt = "Bearer " + jwt;
if (jwt != null) {
myHeaders.append("Authorization", headerJwt);
}
//console.log("headers = ", Object.fromEntries(myHeaders));
let myInit = {method: "PUT"
,headers: myHeaders
};
let url = serverName + endpoint;
//console.log("data = " + data);
if (data)
myInit.body = data;
let returnFetch = fetch(url, myInit);
Here is my Java api snippet:
#PutMapping(value = "/update", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Upload> update(
#RequestParam("uploadId") Long uploadId,
#RequestParam("uploadTypeId") Long uploadTypeId,
#RequestParam("dateEntered") Date dateEntered,
#RequestParam("description") String description,
#RequestParam("enteredBy") Long enteredBy,
#RequestPart(value = "file") MultipartFile file)
throws JsonParseException, JsonMappingException, IOException
,SerialException, SQLException, DataAccessException
{
Upload upload = new Upload();
upload.setUploadId(uploadId);
upload.setUploadTypeId(uploadTypeId);
upload.setDateEntered(dateEntered);
upload.setDescription(description);
upload.setEnteredBy(enteredBy);
MultipartFile multipartFile = file;
byte[] bytes = multipartFile.getBytes();
SerialBlob blob = new SerialBlob(bytes);
upload.setFileBlob(blob);
String filename = multipartFile.getOriginalFilename();
upload.setFilename(filename);
long recordsUpdated = this.myService.updateUpload(upload);
return new ResponseEntity<Upload>(upload, HttpStatus.OK);
}
I found the problem!
I had to do 2 things:
Omit the content-type header.
Stringify all the additional parameters.
It works now!
I am trying a GetMapping After a PostMappng. Postmapping basically does an Update.
What I am doing is, if the condition satisfies then no need to update but simply redirect to another page. But seem it is not working, It seems a GetRequest is not Possible from a Postrequest but I have read articles and even seen solutions on Stack but dont know why dont they work for me. Any help or hint will be appreciated
#PostMapping(path = "/campaign/services/migrate")
public String migrateCampaigns(
#RequestParam(required = false, value = "campaignCount") int campaignCount,
#RequestParam(required = false, value = "client-email") String clientEmail,
#RequestParam(required = false, value = "campaign-id") List<String> campaignIds,
#RequestParam(required = false, value = "selectAllCampaigns") String selectAllCampaigns,
HttpServletRequest request,
HttpServletResponse httpResponse,
Model model) throws ServletException, IOException {
logger.info("Retrieving source and destination credential from cookies");
String url = null;
if(campaignCount >= 20) {
url = "redirect:/login/oauth/login-with-eloqua";
}
String adminsEmail = contactService.getEmail(siteNameForEmail);
String userEmail = cookieService.retrieveCookieValue(cookieDefinitionUserEmail);
String clientsEmailAddresses = adminsEmail + "," + userEmail;
migrateResponse = migrationService.migrate(campaignIds, request.getCookies(), clientsEmailAddresses);
}
//return migrateResponse;
return url;
}
#GetMapping(path = "/login/oauth/login-with-eloqua")
public String eloquaOauthLoginPage(HttpServletRequest request, Model model, HttpServletResponse response) {
return "login/oauth/login-with-eloqua";
}
There are many ways to get this approach, it's up on to you. I can see you have a class called HttpServletResponse, you can use a method that name isĀ of the class.
Example:
httpSrvltResponse.sendRedirect("/login/oauth/login-with-eloqua")`
Upgrading from:
import org.springframework.security.authentication.encoding.PasswordEncoder;
#Override
public String encodePassword(String plainPassword, Object salt) {
final String finalSalt = salt != null ? salt.toString() : "";
return DigestUtils.md5Hex(finalSalt + plainPassword);
}
#Override
public boolean isPasswordValid(String encodedPassword, String plainPassword, Object salt) {
final String enteredPassword = encodePassword(plainPassword, salt);
return encodedPassword.equals(enteredPassword);
}
To:
import org.springframework.security.crypto.password.PasswordEncoder;
#Override
public String encode(CharSequence rawPassword) {
final String finalSalt = salt != null ? salt.toString() : "";
return DigestUtils.md5Hex(finalSalt + plainPassword);
}
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
final String enteredPassword = encodePassword(plainPassword, salt);
return encodedPassword.equals(enteredPassword);
}
Not sure what to do about salt?
Not sure if I can just convert rawPassword to String to replace plainPassword?
The new methods expect that salt is part of the encoded password. As per PasswordEncoder.encoder() javadoc:
Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or greater hash combined with an 8-byte or greater randomly generated salt.
If you look at this answer it shows how BCryptPasswordEncoder encodes salt in the encoded password. The actual BCrypt encoded password format is explained here.
I have an encrypted value in database and I would like to decrypt it before sending to front-end.
When I first save the value as encrypted, it looks like -kKwj477382jle34nw in database. But if I call my getClientByUsername() function which I make the decryption thing in this function, the value in database also changes itself automatically when I set decrypted value in the object before sending the object to front-end.
#Transactional
public ResponseEntity <Client> getClientByUsername(String username) throws Exception {
Client loggedClient = clientDAO.findByUsername(username);
String data = loggedClient.getCreditCardNo();
if (null != data) {
#SuppressWarnings("static-access")
byte[] encrypted = base64.decodeBase64(data);
SecretKeySpec secretKeySpec = new SecretKeySpec(encryptionKey.getBytes(), algorithm);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
decrypted = cipher.doFinal(encrypted);
loggedClient.setCreditCardNo(new String(decrypted));
}
return new ResponseEntity < Client > (loggedClient, HttpStatus.OK);
}
Here is how I save the value as encrypted:
#Transactional
public boolean clientUpdate(String client) {
str = updateclient.getCreditCardNo();
if (null != str) {
SecretKeySpec secretKeySpec = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), algorithm);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
encrypted = cipher.doFinal(str.getBytes("UTF-8"));
updateclient.setCreditCardNo(base64.encodeToString(encrypted));
return clientDAO.updateProfileClient(updateclient);
}
How can I block hibernate to change the value when calling setter?
Update
#PersistenceContext
private EntityManager entityManager;
public Client findByUsername(String username) throws Exception {
Query query = entityManager.createNamedQuery("Client.findByUsername");
query.setParameter("username", username);
List result = query.getResultList();
return result.size() > 0 ? (Client) result.get(0) : null;
}
You need to evict this object from Hibernate's session:
void evict(Object object) throws HibernateException
Remove this instance from the session cache. Changes to the instance
will not be synchronized with the database. This operation cascades to
associated instances if the association is mapped with
cascade="evict".
P.S. Just thought, that you can also consider another approach. You can create a field in bean which will be marked as #Transient, i.e. decoupled from the database, and name it, say, creditCardNoDecrypted. The encrypted field mark as #JsonIngore (or whatever you use for serialisation).
I have this method in a Spring Controller and I would like to be sure not to create a memory leak. Does it, or does it not?
#ResponseBody
#RequestMapping(value = "/document/{id}/{filename:.*}", method = RequestMethod.GET)
public byte[] getDownloadEclaimsDocument(#PathVariable("id") final String id, #PathVariable("filename") final String fileName, final HttpServletResponse response) {
final DmsFile dmsFile = dmsService.getByFileSystemId(id);
response.setContentType(dmsFile.getContentType());
// This call gets a byte[] from a backend service
return dmsService.getFileAsByteArray(dmsFile);
}
Maybe better use org.springframework.core.io.Resource?
#ResponseBody
#RequestMapping(value = "/document/{id}/{filename:.*}", method = RequestMethod.GET)
public Resource getDownloadEclaimsDocument(#PathVariable("id") final String id,
#PathVariable("filename") final String fileName,
final HttpServletResponse response,) {
final DmsFile dmsFile = dmsService.getByFileSystemId(id);
byte[] data = dmsService.getFileAsByteArray(dmsFile);
response.setContentType(dmsFile.getContentType());
response.setContentLength(data.length);
response.setHeader("Content-Disposition",
"attachment; filename=\"" +
MimeUtility.encodeWord(dsmFile.getName(), "utf-8", "Q") + "\"");
// This call gets a byte[] from a backend service
return new ByteArrayResource(data);
}