I have an POJO in Google Web Toolkit like this that I can retrieve from the server.
class Person implements Serializable {
String name;
Date creationDate;
}
When the client makes changes, I save it back to the server using the GWT RemoteServiceServlet like this:
rpcService.saveObject(myPerson,...)
The problem is that the user shouldn't be able to change the creationDate
. Since the RPC method is really just a HTTP POST to the server, it would be possible to modify the creationDate
by changing the POST request.
A simple solution would be to create a series of RPC functions like changeName(String newName)
, etc., but with a class with many fields would require many methods for each field, and would be inefficient to change many fields at once.
I like the simplicity of having a single POJO that I can use on both the server and GWT client, but need a way to do it securely. Any ideas?
EDIT
I'm 开发者_StackOverflowresubmitting with a bounty to try and see if there are any other ideas. Perhaps my original question focused too much on the specifics of GWT. Really I think this is a generic question for any system that uses JavaBeans to pass data between a secure (servlet container) and insecure (web browser) environment.
EDIT 2
Also, to be clear, I used the field creationDate
as an example of the problem. In reality the code I'm working with is more complicated with many different fields.
I recommend you to keep your single RPC method, and use a POJO/bean mapper like Dozer or Gilead.
- With Dozer, you create a class-mapping that is used to copy properties from one object to another. If you don't specify a property in the class-mapping, it won't be copied.
- With Gilead, the @ReadOnly transport annotation should suffice.
The side-benefit is that you don't need to change your data access layer (supposing you have one). Doesn't matter if you use a ORM or not, with a relational database or not.
If the client shouldn't be able to change the creationDate and have it stick, change your serialization (e.g. your SQL UPDATE
statement) to not save that specific field. It should only be set from an INSERT
(where it will come from either the RPC endpoint server, or your database server if you set an automatic default).
I'd use a permissions based approach:
- Assign roles to users (e. g. admin user, logged in user, guest user, ...), and
- associate those roles with permissions (e. g. can read name of person, can modify name of person - maybe further limiting that to certain persons etc.)
On each request from the client, perform a check on the server, if the user is allowed to perform that action. In the case of "creation dates", this is probably never allowed for anybody. So
- if the request contains a creation date, you can show an error message or ignore the request...
- if the request doesn't contain a creation date (usual case), you create the date on the server - or if the person already has a creation date, reuse that.
The client will usually specify the person by some kind of ID (can be null for a newly created person), which the server can use to look up existing persons. Tampering with the ID shouldn't matter, because the user can only modify data that is specified by his permissions anyway.
Special case:
If you actually have to use a creation date supplied the client, because you want to know a bit more exactly when the user has clicked, the only thing you can do is to check that the supplied creation date lies between the previous request and the current request. However, you'd have to take into account the clock difference between the server and the client. And you can't guarantee the precision.
Why not make the fields private and only provide a getCreationDate() and no setCreationDate()?
You could just ignore the values for the immutable fields.
If that is not possible because of how your persistence mechanism is setup on the server, then when the request gets to the server, retrieve another instance of the POJO from the persistent store and populate the fields that are immutable with the ones you just got. That way of somebody tampered with some of the fields you do not care.
Of course encryption might be a solution also to help in avoiding tampering.
Split your objects into two parts. One contains just the fields which the client can edit. Send both objects to the client but only save the editable object when the client returns it. You can use inheritance here to make your life more simple.
If your mapping tool doesn't bend the way you want, implement a copy()
method which copies all fields from one object to another. Then you can load a fresh instance of the object from your database, copy the editable fields into it and then save the modified object.
Well...an academical (i.e. theoretically possible, but quite impractical) solution might be to hash the object's state using a throw-away public key who's private key is on the server. It might go something like this:
- the server generates public-private key pair
- the server sends the public key along with the real data to the client
- the client calculates a hash of the full object's state together with the public key
- the client sends the object and the hash to the server
- the server recalculates the hash to confirm the integrity of the package
Assuming the key-pair is changed at each requset and that the user can't interfere with the hashing process (which he can, but might be difficult enough to make it useful in some use cases), the server would be able to detect any change in the objects state done after the hash has been calculated on the client.
Again, consider this nothing more than a thought experiment...I wouldn't suggest you implement such an approach. In general, you have to be able to guarantee the logic on the client hasn't been tampered with before you get to securing the data.
精彩评论