PUT or PATCH to change only one field value - rest

I'm thinking to create an API for users to change their password.
User table has some fields like firstname, lastname and so on.
For this API, should I use PATCH like the below?
PATCH /users/{userId}
{
"password": "new_password"
}
Or, should I use PUT?
PUT /users/{userId}/{password}
This seems awful for security.
By the way, I don't want users to change values of other fields. I think PATCH must let users to be able to change values of any fields. That's why I'm wondering.

Path info and query string will be encrypted using HTTPS/TLS - for that HTTP session. However, it is still a bad idea to put passwords there, since they are likely to be in browser history and/or server logs
PUT /users/{userId}/{password}
... that will stay in web server logs. As secure developers, we are not supposed to even store passwords in databases where they can be stolen (we are to store a hash + salt of a password). Having cleartext passwords in web server logs is worse.
Sending it in the body in a TLS session is the best.
PATCH /users/{userId}
{
"password": "new_password"
}
... and then hash+salt it and store that. Logins, you do the same process (one-way hashes match).
See this: HTTPS, URL path, and query string

From a security POV there is no difference. An attack can read both the query string and the request body. You should use TLS.
Both requests look fine to me. Their URLs and their bodies are good, solid REST.
If you don't want to accept changes to all fields, write logic in your server that rejects requests that trie to change fields that are not to be changed by the user. But this is not a question of PUT vs. PATCH or POST.

Related

REST: Best way to deal with secrets in DELETE

I need to use a sensitive value as primary identifier in a REST Api.
This would be identified as:
DELETE /api/sometoken/<sensitiveid>
Since sensitive data should not be included in URLs, I wonder what the best option is.
Sensitive ID in body
Would it be a valid solution to set the id as json in the body?
This would result in DELETE /api/sometoken with body { "id": "<sensitiveid>" }
I am not sure if this is ok, since DELETE does not directly reference an entity by url.
Abuse POST
Alternatively, I could use POST instead of DELETE, and contain the information about delete in the body or url. I assume this would be even worse.
Abstract ID
This most complex solution would probably be using a different id.
When using GET, to lookup the all sensitive ids for my subscription and then DELETE the abstract id.
Hashed ID
I thought of making an sha-256 hash and taking the first n characters to id identify the sensitive token.
DELETE /api/sometoken/<hashofsensitiveid>
Putting secrets in the body part of the request is usually a good idea, but there might be issues for GET and PUT requests: Is an entity body allowed for an HTTP DELETE request?
If it is sensitive and should not appear in some web server or traffic logs - make it simply a header.

REST API update and email address in one record where the ID has multiple records

What's the equivalent REST method and request for this sql query?
UPDATE user
SET email = 'newemail#etc.com'
WHERE email = 'oldemail#etc.com'
AND account_number = 1234
Would it be
PATCH api/users/1234/oldemail#etc.com
{
email:"newemail#etc.com"
}
or should I create a new method, something like this?
PATCH api/update-email/1234
{
oldEmail:"oldemail#etc.com",
newEmail:"newemail#etc.com"
}
Note: the account_number is not the primary key id
You are updating an existing record, you should use PATCH instead of POST.
Refs:
RESTful API Design — PUT vs PATCH
REST – PUT vs POST
RESTful API Design: 13 Best Practices to Make Your Users Happy
What's the equivalent REST method and request for this sql query?
How would you do it on a web page? You'd POST form data.
For instance, if you were to
GET /emailAddresses
and then you happened to notice that there was a mistake in the list that you wanted to fix, you would probably click on the "fix this email" link, which would load a new form
GET /fixThisEmail?oldEmail=oldemail#example.com
And then you would fill in the replacement address into the form (which would have the original address preloaded into an input control). Since the /emailAddresses resource is the cachable data that you want to change, the form submission would probably look like
POST /emailAddresses
Content-Type: application/x-www-form-urlencoded
oldEmail=oldemail#example.com&newEmail=newemail#example.com
If instead you wanted to use a remote authoring idiom, you might reasonably edit your own local copy of the email addresses list to correct the error, and then send the revised representation back to the server
GET /emailAddresses
(make the edit in your local copy)
PUT /emailAddresses
PATCH is convenient when the list is much bigger than the http headers and the edit is small relative to the size of the list.
GET /emailAddresses
(make the edit in your local copy)
PATCH /emailAddresses
... but, notice that for both PUT and PATCH, you are going to need to figure out how to extract from the request-body the things that have changed, and compute the right SQL update query to use. Getting the "old" email address to match on may be painful if it isn't present in the new representation.
You can make that somewhat simpler by using a bespoke patch document representation, but that restricts the range of general purpose clients that can use your API.

How to properly structure REST endpoints for PUT and POST

I have a REST service for managing users.
HTTP GET("/users") - get all users
HTTP GET("/users/{id}") - get data for specific user
When changing something about the user I'm not sure how structure the paths for PUT/PATCH.
Option 1:
HTTP PUT ("/users") and transfer the user data (id, first name, last name) in the request body
HTTP PATCH ("/users") and transfer the user's ID and PASSWORD in the request body
Option 2:
HTTP PUT ("/users/{id}") and transfer the user data (id, first name, last name) in the request body
HTTP PATCH ("/users/{id}") and transfer the user's ID and PASSWORD in the request body
Option 3:
HTTP PUT ("/users/{id}") and transfer the user data (id, first name, last name) in the request body
HTTP PATCH ("/users/{id}/password") and transfer the user's ID and PASSWORD in the request body
#RequestMapping(value = "/users")
public interface UserController {
#GetMapping(value = "/{id}", produces = "application/json")
User getUser(#PathVariable long id);
#PutMapping(value = "", consumes = "application/json")
void addNewUser(#RequestBody User ser);
#PatchMapping(value = "/{id}/password", consumes = "application/json")
void changeUserPassword(#RequestBody UserPasswordChange passwordChangeModel, #PathVariable String id);
I'm not sure which of these approaches is the best. I can get all the data from the request body every time but I'm not sure what should be the best path to create. Using "/users/{id}" to change details about the user makes sense because I'm changing for a specific user but since I can read the ID from the request body, the path variable is redundant here.
It's the same confusion when changing the password. Since I have just one Patch endpoint under "/users" should I still use the "/users/{id}/password" or maybe I should delete the "/password" part?
When changing something about the user I'm not sure how structure the paths for PUT/PATCH.
Both PUT and PATCH are document editing requests. "Please make your representation of this resource look like mine".
In an idealized form, I would have some sort of HTTP aware document editor. I would GET a representation of the resource from you, make edits to my local copy, then send a representation of my local copy back to you.
Getting useful work done is a side effect of passing these documents around. See Jim Webber's talk.
Options 1, I think we can simply reject on first principles. If the resource identified by /users is a collection of users, then trying to replace its representation with that of a single user is not a step in a useful direction. Semantically, if you wanted to edit or insert a user by interacting with the /users resource, you would do that by either (a) making an edit to the representation of the collection, and sending the entire representation back (PUT), or by sending a description of the diff back to the server (PATCH).
Option 2, has a subtler issue -- if the password is part of the representation of the /users/id resource, then the password should also be part of the body of the PUT request. PUT and PATCH are different ways of passing our local representation of the resource back to the server; we shouldn't be thinking of them as having different information about the resource.
It's perfectly reasonable to separate the password into a different resource from the rest of the user. Unless the representation of the password resource is very large relative to the password itself, I would expect PUT rather than PATCH to be used in most cases.
So you are suggesting that option 3 would make the most sense?
Not quite - both resource designs (either with all of the information available in one resource, or with the information divided between two resources) are fine. You pick one, and then make sure the idioms you use for updating the resource(s) are appropriate.

Should I allow user-provided values to be passed through a query string?

I'm adding a search endpoint to a RESTful API. After reading this SO answer, I'd like the endpoint to be designed like:
GET /users?firstName=Otis&hobby=golf,rugby,hunting
That seems like a good idea so far. But the values that I'll be using to perform the search will be provided by the user via a standard HTML input field. I'll guard against malicious injections on the server-side, so that's not my concern. I'm more concerned about the user providing a value that causes the URL to exceed the max URL length of ~2000 characters.
I can do some max-length validation and add some user prompts, etc, but I'm wondering if there's a more standard way to handle this case.
I thought about providing the values in the request body using POST /users, but that endpoint is reserved for new user creation, so that's out.
Any thoughts? Thanks.
I see these possible solutions:
not actually a solution. Go with the query parameter and accept the length constraints
go with the POST solution that shouldn't be designed as you mention. As you point out, if you POST a user to .../users you will create a new user entity. But this is not what you want to do. You want to submit a search ticket to the server that will return a list of results matching your criteria. I'll design something as such
POST .../search/users passing in the body a representation of your search item
distribute the query both server side and client side. Say you have complex criteria to match. Set up a taxonomy of them so that the most strict ones are handled server side. Thus, the server is able to return a manageable list of items you can subsequently filter on the client side. In this approach you can save space in the query string by sending to the server only a subset of the criteria you want to meet in your search.

authenctication token in a queryString

Our current implementation of the REST API uses apiKey inside queryString for all type of request(PUT, POST, GET). I feel it's wrong but can't explain why(maybe the apiKey can be cashed somewhere between server and client). Something like:
POST /objects?apiKey=supersecret {name: 'some'}
So, is it a security problem? Please describe both HTTP and HTTPS connection case
HTTP
Your supersecret values can be seen and intercepted by thirdparties whenever you send it from the client to the server or vice versa irrespective of whether you use PUT,POST, etc. This is even true when you use cookies for storing those values instead of query string.
HTTPS:
When the data is in transit between your client and server it cannot be intercepted since its protected by https, even if it is in query string. But most people consider sending data in query string as bad, since many system logs the query strings. For eg most servers are configured to print the access logs with the path & query parameters. Also if its from a browser it can be stored in your browser history.