This is the 17th day of System Engineer Advent Calendar 2016.
The 16th day was Mr. Uga's Points when applying SPA locally in a non-SPA Web application. It was a timely content that I wanted to refer to in the next project immediately!
When you want to route the value on the screen to register something, embed it in the hidden field, and does the value match at the time of registration? I think I will check it.
For example, suppose you have an application like submit by selecting a category, opening a new issue creation screen, writing content.
⬇︎
⬇︎
On the registration screen, embed the category ID in the hidden field.
<input type="text" name="content"/>
<input type="hidden" name="categoryId" data-th-value="${category.id}"/>
<button type="submit">Save</button>
The registration process [checks if this value exists in the category table](https://github.com/backpaper0/seac2016/blob/no-hmac/src/main/java/com/example/IssueController. java # L50).
@PostMapping("new")
String post(@RequestParam String content, @RequestParam Long categoryId) {
//Existence check again at the time of registration
categoryDao.selectById(categoryId).orElseThrow(BadRequest::new);
Issue entity = new Issue();
entity.content = content;
entity.categoryId = categoryId;
issueDao.insert(entity);
return "redirect:/";
}
This check seems to be a little overloaded because it accesses the DB, and I've always thought that it would be troublesome if the number of values that had to be checked increased. Generally, when the registration screen is displayed, the value obtained by searching the category is embedded, but why do I have to check the existence even in the registration process! I feel like.
However, if you rewrite the HTML, you can submit the impossible value, and you have to check it properly.
So, I thought about checking the value using HMAC.
A hash with a key. Even if you know the value, you cannot get the hash value without knowing the key. Also, even if you hash the same value, you will get different hash values if the keys are different. For more information, go to Gugu.
Change the part where the existence check of the category was performed by accessing the DB to the check using HMAC.
First, when you open the registration screen, [Hash category ID with HMAC](https://github.com/backpaper0/seac2016/blob/hmac-verify/src/main/java/com/example/IssueController.java# L47) and then Embed that value in the view.
@GetMapping("new")
String blank(Model model, @RequestParam Long categoryId) throws GeneralSecurityException {
//Category existence check
Category category = categoryDao.selectById(categoryId).orElseThrow(BadRequest::new);
//Make a hash and embed it in the hidden field
String hash = verifier.hash(categoryId);
model.addAttribute("category", category);
model.addAttribute("hash", hash);
return "new-issue";
}
<input type="text" name="content"/>
<input type="hidden" name="categoryId" data-th-value="${category.id}"/>
<input type="hidden" name="hash" data-th-value="${hash}"/>
<button type="submit">Save</button>
Hash processing looks like this.
public String hash(Long value) throws GeneralSecurityException {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
byte[] hash = mac.doFinal(String.valueOf(value).getBytes());
return IntStream.range(0, hash.length).mapToObj(i -> String.format("%02x", hash[i]))
.collect(Collectors.joining());
}
In the registration process, Hash the submitted category ID with HMAC and compare it with the submitted hash value /java/com/example/IssueController.java#L60).
if (verifier.verify(hash, categoryId) == false) {
throw new BadRequest();
}
Now you have a check to prevent the value from being altered, just like the existence check.
By the way, if there are multiple values, if you connect all the values with :
or .
and hash, you only need one hash value, so the payload of the HTTP request response will not swell.
Since it is not a strict existence check, inconsistency will occur if the category that was present when the registration screen is displayed disappears during the registration process. (Although it is prevented by the foreign key
constraint in the sample)
Therefore, you have to be careful about applying it only to data that does not change during the day (for example, department in the case of business system).
Also, for the reasons mentioned above, you may want to invalidate the hash value across days. In that case, Hash the date together is recommended. In this way, if you hash the time stamps together, you can have an expiration date, and if you hash each session ID together, you can create a valid hash for each user.
Another caveat is key management. You can get a random value when you start the application and use it as the key, but if you have multiple instances of the application in a redundant configuration, you need to enable sticky sessions or share the key among all the instances. there is. I think that what kind of management is done on a case-by-case basis.
I thought about how to use HMAC to validate the value embedded in the hidden field. There are some caveats, but I think there are many situations where it can be applied.
Sample code is on GitHub.
No-hmac branch checking for existence using DB, hmac-verify checking with HMAC //github.com/backpaper0/seac2016/tree/hmac-verify) Branch, then mix the date into the hash to set the expiration date master There are branches, so please compare each one.
Above ⛄️
Recommended Posts