I'm creating my own PHP MVC Framework and want to manage users, for this I have a dynamic url that is created with a form action like the following:
<form action="users/delete/<?=$user->id?>" method="post">
<input type="hidden" name="_method" value="delete-submit" name="delete-submit">
<button type="submit" class="btn bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">Delete</button>
</form>
I don't know if this is the right way to do it, so I'm asking.
This redirects to /users/delete/$id but do I need to handle the form requests like this:
if(!empty($_POST['delete-submit'])) {
$user = $this->model("user");
$user->deleteUser($value);
header("Location:".ROUTE."home/users");
}
or can I just work with the url and ignore the POST request.
I wouldn't count on $_POST['delete-submit'] variable, cause for example if you use tool like Postman, you can create such request manually and delete any user, considering you are just checking if $_POST['delete-submit'] is set or not.
So if you are deleting user, there should be some authorization mechanism.
users/delete route for deleting users -> That's Correct
Then you check via $_SESSION['UserId'], which user is making such request and if he is authorized / has such permission, you delete the user..
Like...
function deleteUser($userId) {
if(user::hasDeletePermission($_SESSION['UserId']) {
'delete the user';
}
}
If you already have such authorization based mechanism and just need additional check, user really submitted / clicked on that button or not, for CSRF attacks for example, then I would create Token (some random string with numbers and characters) and save into user's session, which would be embedded into form as a hidden input element's value.
Like:
<form method="POST" action="users/delete">
<input type="hidden" name="csrfToken" value="token"/>
</form>
function deleteUser($userId) {
if($_POST['csrfToken'] === $_SESSION['csrfToken'] && user::hasDeletePermission($_SESSION['UserId']) {
'delete the user';
}
}
Related
So I tried to make a basic table with database integration, which worked flawlessly. Now I need to make a table that adds you the permission to edit the data you inserted into the form. But I need it to only give the person who made the form the permissions to edit it. Below I added my current progress without the permissions added. Currently I finished the Jetstream page with laravel, it has login and register so I'd want the username to be the authed person to edit the data. Is there any easy way to do it?
The controller I made:
`$rada= new Raja;
$rada->id=$req->id;
$rada->raja_nimi=$req->raja_nimi;
$rada->raja_asukoht=$req->raja_asukoht;
$rada->save();
return redirect('list');`
The view file:
<form action="add" method="POST">
#csrf
<input type="text" name="id" placeholder="Raja ID"> <br> <br>
<input type="text" name="raja_nimi" placeholder="Raja nimi"> <br> <br>
<input type="text" name="raja_asukoht" placeholder="Raja asukoht"> <br> <br>
<button type="submit">Lisa</button>
</form>
You'd need to store the id of the author in your controller, for example...
$rada= new Raja;
$rada->id=$req->id;
$rada->raja_nimi=$req->raja_nimi;
$rada->raja_asukoht=$req->raja_asukoht;
$rada->author_id = Auth::id();
$rada->save();
Then when you come to update the post in your controller you'd implement a check to make sure that the user owns the post...
$post = Raja::find($req->id);
if(Auth::id() == $post->author_id) {
//Update functionality here
} else {
//Redirect to 401 or send response message
}
A more Laravel appropriate way to perform a check when the user attempts to save a model would be to use a policy, you can find an example here
https://laravel.com/docs/8.x/authorization#policy-responses
I'm building a request system with Laravel 7. In the index page, there is a form for user to fill in and a submit button. If the button is clicked, the data in the form will be sent to the supervisor via email. The email which the supervisor receives, all user input data will be listed along with a "Approve" button and a "Reject" button. If the "Approve" button is clicked, the user input data will be stored in the database. All user input data is stored in $data and can be accessed in email template view. So, in the email template, how do I pass the user input data and Auth::user()->id to the controller so that the storing process will start?
I read this post and it looks useful to me, but it is using <a> to do the redirection to trigger the controller. Is it possible to pass the blade variables from the <form> in the email template view to the controller, so that I can use <button>?
Update:
index.blade.php:
<form action="/sendSupervisor" method="post">
<input type="text">
<input type="text">
<button type="submit">Send</button>
</form>
web.php:
Route::post('/sendSupervisor', 'CatViewController#sendSupervisor')->middleware('auth')->middleware('verified');
CatViewController.php:
// one of the methods
public function sendSupervisor()
{
$data = request();
Mail::to('supervisor#abc.com')->send(new ApprovalMail($data));
return redirect('/');
}
approval.blade.php:
<!-- Email Template -->
User Input: {{ $data->name }}
<form action="/approve">
<button>Approve</button>
</form>
<form action="/reject">
<button>Reject</button>
</form>
An email is not a browser, and in almost every case a <form> tag will have no effect. You cannot retrieve user input from an email, however in your case if you have two buttons in the email, and that email is being sent to a user that exists in your system, AND the input you are receiving is 'accept' or 'reject' based on which button they click, this is doable.
You can have two routes that are publicly accessible and build two "buttons" with hrefs that will carry out those actions. Like:
Route::get('email/accept/{user_id}', 'EmailController#accept');
Route::get('email/reject/{user_id}', 'EmailController#reject');
Then link to those routes in your email temple like:
Accept
Reject
Then in that controller, you can resolve the user based on id in those methods, and you can carry out any logic you would like.
A note of caution: since these links need to be publicly accessible, using id's is not ideal. You should generate some sort of token to resolve the user.
A better approach:
It's better to save the user request once he post it with a column flag called "status" that holds either ("Pending", "Approved", "Rejected"), and then send an email to the supervisor
index.blade
<form action="/sendRequest" method="post">
<input type="text">
<input type="text">
<button type="submit">Send</button>
</form>
web.php
Route::get('/sendRequest', 'MyController#sendRequest)
MyController.php
public function sendRequest(Request $request){
// UserRequest table should has column 'status' with default value 'Pending'
$userRequest = UserRequest::create($request->all());
Mail::to('supervisor#abc.com')->send(new ApprovalMail($userRequest));
return redirect('/');
}
The email template should only notify the supervisor to take an action with the request, with a button 'Take action' that opens a page in the browser for him to take the action.
email-template.blade
<!-- User data displayed for example in a table -->
<a href={{ route('takeAction', $userRequest->id) }}>
<button> Take Action <button>
</a>
web.php
Route::get('/take-action/{requestId}', 'SupervisorController#takeAction')
->name('taleAction');
SupervisorController.php
public function takeAction($requestId){
$userRequest = UserRequest::find($requestId);
return view('take-action', [$userRequest]);
}
take-action.blade
<-- User data displayed for example in a table -->
<form method='POST' action={{ route('takeActionPost', $userRequest->id) }}>
#csrf
<input type='hidden' name='action' value='Approved'>
<button type='submit'>Approve</button>
</form>
<form method='POST' action={{ route('takeActionPost', $userRequest->id) }}>
#csrf
<input type='hidden' name='action' value='Rejected'>
<button type='submit'>Rejected</button>
</form>
web.php
Route::post('/take-action/{requestId}', 'SupervisorController#takeActionPost')
->name('takaActionPost');
SupervisorController.php
public function takeActionPost(Request $request, $requestId){
$userRequest = UserRequest::find($requestId);
$userRequest->update(['status', $request->action]);
}
Of course the names I'm using here are very bad, I just want to explain my idea
Add hidden input fields in the form whose values are the user data. But I think there is a better design for your system that I'll explain in a separate answer.
What's the Problem?
Primary Key is present in Url so the data for other records can be seen easily by easily changing the Url.
Rendered html has hidden field present. Anybody can easily change other records based upon this Hidden Field.
When I edit the page. My Url looks like below.
http://localhost/Category/3
and Below is the rendered Html
<form method="POST" action="http://localhost/UpdateCategory" accept-charset="UTF-8">
<input name="_token" type="hidden" value="AmAXKmqtct6VOFbAVJhKLswEtds4VwHWjgu3w5Q8">
<input name="CategoryID" type="hidden" value="3">
<input required="required" name="Category" type="text">
<input class="btn btn-success" type="submit" value="Update">
</form>
Please suggest some Url and Form security in Laravel 5.1
There are many worksaround which shall by handled by us to avoid such incidents.
Fix 1 :
If you don't want to reach the user's by just changing the url
(i.e., Directly passing the id in url )
You shall filter the requests by
if($_SERVER['HTTP_REFERER']!='')
{
}
else
{
exit;
}
You shall have this in your Middleware or even in your view if you wish
Fix 2 : Never worry about the _token that is visible when you see form source
It is just the token that is generated by laravel app which is to identify whether the request is from authenticated source or not.
If you edit the token and pass the form you will surely get CSRF Token Mismatch Exception
Infact this is one of the great feature of Laravel.
Interesting Point : You can also find something in the headers of the browser ;)
Happy using Laravel ;)
Hello I have a web page where users can view and edit their application information. I have an Edit button. When a user clicks on this button it takes him to an edit page. Here is my code:
<form name="form3" method="post" action="pages/application_edit.php?id=<?php echo "$id[0]";?>&pwd=<?php echo "$pwd";?>">
<input type="submit" name="Submit" value="Edit Application" class="button">
</form>`
After a click the user sees this URL:`http://website.com/pages/application_edit.php?id=1&password=Flower1
How can I hide the password from the URL?
Instead of sending the values as $_GET values, send them as $_POST values to that PHP page.
<form method="POST" action="pages/application_edit.php"> // no need for the URL query string
In the PHP file
<?php
$user_id = $_POST['id']; // similar to how you'd use $_GET
....
Although the way you're approaching this is wrong, you shouldn't be passing these values between pages. At the very least your username/id should be stored as a session variable and information should be accessed when required from a database.
Either way, that's how you can send them without having them "visible".
It seems you lack session control routines.
You should manage all private options of your application (the ones you are able to perform only - and just only - when you are logged in) inside a session to avoid exposing user credentials.
You can start learning about it here.
Also, consider encrypting your HTTP requests using SSL certificate.
I have an action that takes POST data secured by sfGuard. This means that if the user is not logged in, the POST data will be sent to the log in form. Ordinarily, this is not a problem, the user continues to log in, and has to submit the data again.
Unfortunately, the log in form seems to be using the POST data as if it was submitted with the form itself. This means that it is complaining that the required username and password fields are missing, and it complains that it is missing a CSRF token. This last problem does not go away, after submitting the form, meaning the user cannot log in, anyway.
The user should not be presented with the form if not logged in, but it might be possible for the user to log out with the form still open. So I am asking in the interest of keeping the interface watertight and error-free.
Is this a shortcoming of sfGuard, can it be avoided, or am I doing something wrong altogether?
To clarify, the route looks like this:
add_subgroup:
url: /group/:id/add
class: sfPropelRoute
options:
model: Group
type: object
param: { module: subgroups, action: create }
requirements:
group_id: \d+
sf_method: [post]
The form used to submit the request is as follows:
<form action="<?php echo url_for('add_subgroup', $group) ?>" method="post">
<input type="hidden" name="group_id" value="<?php echo $group->getId() ?>" />
<input type="text" name="subgroup_id" />
<input type="submit" class="button" value="Add" />
</form>
It is a shortcoming of sfGuard, because the signin action will check for a POST request, and if so bind the form.
From the code in BasesfGuardActions.class.php:
if ($request->isMethod('post'))
{
$this->form->bind($request->getParameter('signin'));
I'm personally not a big fan of forwarding between actions in symfony, and just like in this case, I think it's more appropriate to redirect than forward. This also solves your problem because this will result in a new GET request. You can accomplish this behavior by extending sfGuardBasicSecurityFilter.
class mySecurityFilter extends sfGuardBasicSecurityFilter
{
protected function forwardToLoginAction()
{
$context = $this->getContext();
// If you want to redirect back to the original URI (note: original POST data will be lost)
$context->getUser()->setReferer($context->getRequest()->getUri());
$url = sfConfig::get('sf_login_module') . '/' . sfConfig::get('sf_login_action');
$context->getController()->redirect($url);
throw new sfStopException();
}
}
Now in app/myapp/config/filters.yml
security:
class: mySecurityFilter
This is probably because you put the code of auth the login data in the same action (probally by check whether the request is post).
However You could divide the one action into two actions. One for showing the login form, and the another is for auth the user's login data. And set your secure_action to the action which is just to show the login form.