Samshin on the value of the hidden field

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!

Checking the value of the hidden field

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.

select-category.png

⬇︎

click-new.png

⬇︎

post-issue.png

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.

What is 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.

Check the value with HMAC

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.

merit

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.

important point

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.

Summary

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

Samshin on the value of the hidden field
Note on the path of request.getRequestDispatcher
How to display 0 on the left side of the standard input value
Display text on top of the image
part of the syntax of ruby ​​on rails
Looking back on the basics of Java
[Ruby on Rails] Until the introduction of RSpec
Install the latest version of Jenkins on Ubuntu 16
I touched on the new features of Java 15
Specify the default value with @Builder of Lombok
Sample code to assign a value in a property file to a field of the expected type
[Ubuntu 20.04] Display the day of the week on the date / clock
How to delete / update the list field of OneToMany
The mark on the right side of the version-controlled Xcode file
Docker the development environment of Ruby on Rails project
Diffusion simulation of ink dripping on the surface of the water
[Java] How to get the maximum value of HashMap
Display the average value of the evaluation as a star
Find the approximate value of log (1 + x) in Swift
How is the next value of the Time object correct?
Try using the query attribute of Ruby on Rails
The world of clara-rules (2)
Judgment of the calendar
The world of clara-rules (4)
The world of clara-rules (1)
The world of clara-rules (3)
The world of clara-rules (5)
The idea of quicksort
Setting value of log4jdbc.properties
The idea of jQuery
[Rails] Put the same restrictions on multiple pieces of information
How to display the select field of time_select every 30 minutes
About truncation by the number of bytes of String on Android
20190803_Java & k8s on Azure The story of going to the festival
(Ruby on Rails6) Display of the database that got the id of the database
Delete all the contents of the list page [Ruby on Rails]
A note about the seed function of Ruby on Rails
Until the production deployment of hello-app with k3d on Time4VPS
From the habit of creating Value Objects for object-oriented understanding
How to change the setting value of Springboot Hikari CP
[Swift] Get the timing when the value of textField is changed
Extract the uniquely identifying value of the table created by PostgreSQL
How to check the database of apps deployed on Heroku