How to protect my delete.php files
$id = (int) $_GET['id'];
$delete = $connection->prepare("DELETE FROM `articles` WHERE `id` = :id");
$delete->execute(['id' => $id]);
if($delete->rowCount() > 0){
echo 'SUCCESS';
}else{
echo 'ERROR';
}
Lets say am logged in my website panal and my session is on
$_SESSION['user_id'] = My_ID;
And some hacker send a link in my email while i am logged in
<img src="http://my.website.com/panel/articles/delete.php?id=353">
That link is going to delete my article OR WORST a complete main section of my website and that is VERY DANGEROUS. So how can i secure my delete links from that
This is a very good example, why it is stated in the HTTP RFC, that GET method should be used only to request the data, while for the data manipulation it's POST method should be used.
And to protect POST forms from this kind of fraud, a usual CSRF protection have to be used. In short, each form should be signed with a secret token, stored in the session as well. So a site will be able to verify, whether the form was issued by the engine.
An example can be found here, preventing csrf in php
You can build two step confirm form
When user going to articles/delete.php?id=353, you just calculate some hash 'qweadasdasdqw' and provide link just like this articles/delete.php?id=353&hash=qweadasdasdqw
And just build some form with question 'Do you really want to delete article?'
button 'yes' will provide user to url articles/delete.php?id=353&hash=qweadasdasdqw ,
button 'no' wil provide user to list of article.
so if hash is correct, you will be delete this article
Take a look on the code
$hash = isset($_GET['hash']) ? $_GET['hash'] : null;
$id = (int) $_GET['id'];
if (isset($hash) && (md5($id) == $hash)) { // Check hash
$delete = $connection->prepare("DELETE FROM `users` WHERE `id` = :id");
$delete->execute(['id' => $id]);
if($delete->rowCount() > 0){
echo 'SUCCESS';
}else{
echo 'ERROR';
}
} else {
$hash = md5($id); // Generate hash todo use more security function
echo 'Do you really want to delete this article? yes';
}
You could do few things ,
Use post method for data manipulations instead of get.
You can also make use of tokens, make sure you ,regenerate your tokens
Don't just check if user is logged in but also make sure the logged in user has permission to delete / update that record .
Related
Here is the first question and I need your help.
I transfer form data from first page using header location method in php to second page.
On the second page I accept the data using get.
Now here the url of 2nd page, after the data is sent (i.e. form is submitted)
http://mydomain.com/site1/form1_conf.php?id=123
When user is on second page, the data on second page is being displayed according the id number from the mysql database.
Now the problem is that when the user is on second page and he changes the number (for ex. 123 to say 78) the data of id=78, from the database is displayed, which is no good.
How can I stop that?
Please Note: I can't use post, nor can I use sessions.
EDITE:
php code on first page, to transfer to second page:
// after all validations are okay
$insert = //insert into database
$result = mysql_query($insert);
if($result)
{
echo("<br>Input data is succeed");
$lastInsertedId = mysql_insert_id();
header('Location:form1_conf.php?id='.$lastInsertedId); //THIS IS THE IMPORTANT LINE
}
else
{
$message = "The data cannot be inserted.";
$message .= "<br />" . mysql_error();
}
Your problem is not with the URLs: to a power user changing cookies or POST-variables is as trivial as editing GET-variables for a regular user. You'll need some way to 'sign' the requests as being valid.
Easiest to do this is with a "pre-shared key", which you use with one-way hashes to validate requests.
Redirector:
$newURL = '/newpage?id='.$id.'&hash='.sha1('mypresharedkey'.$id);
header('HTTP/1.1 303 See other');
header('Location: '.$newURL);
die;
The other page:
$idToShow = $_GET['id'];
$hash = sha1('mypresharedkey'.$id);
if($hash != $_GET['hash'])
die("Tssss, don't play with the address bar!");
else
RenderThePage();
This ensures end users can only access pages they've been allowed to by the submit.
For your specific code:
...all prior code
$lastInsertedId = mysql_insert_id();
$timestamp = time();
header('Location:form1_conf.php?'.http_build_query([
'id' => $lastInsertedId,
'time' => $timestamp,
'hash' => sha1('some-generated-key'.$timestamp.$lastInsertedId)
]);
In the other page, including a timebomb if you want (otherwise just comment it out):
$id = $_GET['id'];
$time = $_GET['time'];
if($_GET['hash'] != sha1('some-generated-key'.$time.$id))
die('URL was tampered with');
if(time() - $time > 300)
die('URL was only valid for 5 minutes');
You need to track the user and the id that they have in your database to make sure that they haven't changed the number. So when you get the information via the GET you make sure that it is legit.
Users can change the id or even attempt to go directly to that page via the url. So you need some sort of server-side check to verify that it is ok.
You could complicate this "cheating" a bit, if you didn't pass the ID number directly, but somehow encrypted it.
Let's say, you define a salt:
define(SALT, 'long weird salt with special characters etc.');
Here comes the first part you want:
$lastInsertedId = mysql_insert_id();
$querytag = base64_encode($lastInsertedId); // just to make it less readable
$checksum = md5($querytag . SALT); // and make a hash
header('Location:form1_conf.php?id=' . $querytag . '&checksum=' . $checksum);
At the beginning of form1_conf.php, you put this:
$encodedId = $_GET['id'];
$oldChecksum = $_GET['checksum'];
$newChecksum = md5($encodedId . SALT);
$id = base64_decode($encodedId);
if($newChecksum != $oldChecksum) {
die('You Are Cheating!');
}
... do something with the $id ...
The point is that since you add SALT to the hash, some user can't simply use md5 on a changed ID, because he's missing the SALT you used.
It'd be even better if the salt wasn't the same every time.
You should never trust the url because there is always a way to manipulate the data.
So you should do validation after retreiving the data. If the result does not fit you: for example the loggedin user with the ID = 1 requests the settings page from the userid = 3 you do not show the result.
<?php
$userID = $_GET['id'];
if($userID != $expectedResult)
{
//show errormessage, redirect or show the page with the users data
}
?>
if(!$_POST['username'] || !$_POST['password'])
$err[] = 'All the fields must be filled in!';
if(!count($err))
{
$_POST['username'] = mysql_real_escape_string($_POST['username']);
$_POST['password'] = mysql_real_escape_string($_POST['password']);
$_POST['rememberMe'] = (int)$_POST['rememberMe'];
// Escaping all input data
$row = mysql_fetch_assoc(mysql_query("SELECT id,usr FROM tz_members WHERE usr='{$_POST['username']}' AND pass='".md5($_POST['password'])."'"));
if($row['usr'])
{
// If everything is OK login
$_SESSION['usr']=$row['usr'];
$_SESSION['id'] = $row['id'];
$id = $row['id'];
$_SESSION['rememberMe'] = $_POST['rememberMe'];
// Store some data in the session
setcookie('tzRemember',$_POST['rememberMe']);
}
else $err[]='Wrong username and/or password!';
}
if($err)
$_SESSION['msg']['login-err'] = implode('<br />',$err);
// Save the error messages in the session
$goHere = 'Location: /index2.php?id=' . $id;
header($goHere);
exit;
}
I have the following code that once logged in, it $_GET the id and prepends to the url like index2.php?id=5 . How do I keep this id=5 in the URL no matter WHAT link they click on??
This id is grabbed from this:
$_SESSION['usr']=$row['usr'];
$_SESSION['id'] = $row['id'];
$id = $row['id'];
What I want to do
Well way i have it setup, you login, it then sends you to the homepage such as index2.php?id=[someint] , if you click another link say 'prof.php', it removes the id=[someint] part, I want to keep it there in the url, so as long as a user is LOGGED in -- using my code above, the url might read: index.php?id=5, then go to another page it might read prof.php?id=5, etc, etc. This integer would obviously be dynamic depending on WHO logged in
Instead of passing around an ID in the URL, consider referring to the id value in the $_SESSION variable. That way the user can't modify the URL and see data they aren't supposed to see (or much worse), and you don't have to worry over appending it to every URL and reading it into a value every time you go to process a script. When the user logs in, you determine their ID - read it from a database, determine it realtime, whatever. Then store it in the $_SESSION and refer to it as needed. You can even use this as part of a check to see if the user is logged in - if they have no $_SESSION['id'] value, something is wrong and you make them log in.
The query string isn't the place for that, for a whole host of reasons. The most obvious one is that I can log in with a valid account, then change the number in the URL and it'll think I'm someone else.
Instead, just continue using the session as it's the proper way.
If you REALLY want to do it, you'd probably want to write a custom function for generating links
function makeLink ($link, $queryString = '')
{
return $link . '?id=' . (int) $_SESSION['id'] . ((strpos($queryString, '?') === 0) ? substr($queryString, 1) : $queryString);
}
called like
Click me
As a basic auth example using the ID...
<?php
// Session start and so on here
if (!isset($_SESSION['id']))
{
// Not logged in
header('Location: /login.php');
exit;
}
http://www.knowledgesutra.com/forums/topic/7887-php-simple-login-tutorial/ is a pretty straightforward full example of it.
I have this user login process page. at this point the user has entered the info and all of this works BUT I cannot figure out how to pull the encrypted password out of the DB. I need to extract with the PASSWORD() function and do not know how. I know this is not the best way to do it but its what the assignment calls for. I have the problem section commented out I think thats what needs fixing.
//sets $query to read usnername and passowd from table
$query = "SELECT username,password,first_name,last_name FROM jubreyLogin WHERE username
= '$userName' AND password=password('$userPassword')";
$result = mysql_query($query,$db);
if(mysql_error())
{
echo $query;
echo mysql_error();
}
//reads data from table sets as an array
//checks to see if user is already registered
while($row=mysql_fetch_array($result))
{
if($userName == $row['username'] /*&& $userPassword == ($row['password'])*/)
{
$login = 'Y';
$welcome = "Welcome" . " " .$row['first_name']. " " .$row['last_name'];
$userName = $row['username'];
}
}
if ($login='Y')
{
setcookie('name',$welcome,time()+60*60*24*30);
setcookie('login',"Y",time()+60*60*24*30);
$_SESSION['username_login'] = $userName;
header('Location: welcome.php');
}
Here is the modified code that I should of posted first I need it to check user entered password in this case $userPassword with the encrypted password if its a match it will send the user into the next page of the site.
You don't need to see the password in clear text ( you can't even if you wanted to). As you are checking the record both on password and username you don't need the check in your if() statement. If there is any row found, that means the username/password combination was succesfful and the user can be deemed as logged in.
Edit:
The updated code doesn't really make any difference to the actual logic. The logic stays the same, you query the database with username AND encrypted password, if there is a match that means the user has the right to login, so you proceed with setting the cookies/session data and redirect. Although I do not really see the need for the login cookie and the welcome cookie cause you could simply put in both username, fname and lname in the session. If the session on the following pages contains username that means the user has logged in.
The code can go something like this:
//sets $query to read usnername and passowd from table
$query = "SELECT username,first_name,last_name FROM jubreyLogin WHERE username = '$userName' AND password=password('$userPassword')";
$result = mysql_query($query,$db);
if(mysql_error())
{
echo $query;
echo mysql_error();
}
// were any rows returned?
if(mysql_num_rows($result)){
list($userName, $firstName , $lastName) = mysql_fetch_row($result);
$welcome = "Welcome" . " " .$firstName. " " .$lastName;
setcookie('name',$welcome,time()+60*60*24*30);
setcookie('login',"Y",time()+60*60*24*30);
$_SESSION['username_login'] = $userName;
header('Location: welcome.php');
}
You should not be encrypting your passwords, you should be hashing them. Try using a library such as phpass to be on the safe side. What you will need to do is hash the passwords and store the hashed value in the database. When a user logs in, you will hash the password they provide and compare that with the hashed value in the database. If the hashes match, the password provided is correct. If not, you send an error to the user. You never need to be able to obtain the password in plain text in order to validate a user.
Also, make sure that you are either escaping your variables using mysql_real_escape_string() or prepared statements or your script will be vulnerable to SQL injection.
I want to display the attributes of the game character, which is under the users TABLE. So, I want it to display the specific attributes of the user who has logged in, since it should be in his row. Do I need to register my users with session, because I didn't.
This is the code I used to get the sessions for the user in when login in
<?
if(isset($_POST['Login'])) {
if (ereg('[^A-Za-z0-9]', $_POST['name'])) {// before we fetch anything from the database we want to see if the user name is in the correct format.
echo "Invalid Username.";
}else{
$query = "SELECT password,id,login_ip FROM users WHERE name='".mysql_real_escape_string($_POST['Username'])."'";
$result = mysql_query($query) or die(mysql_error());
$row = mysql_fetch_array($result); // Search the database and get the password, id, and login ip that belongs to the name in the username field.
if(empty($row['id'])){
// check if the id exist and it isn't blank.
echo "Account doesn't exist.";
}else{
if(md5($_POST['password']) != $row['password']){
// if the account does exist this is matching the password with the password typed in the password field. notice to read the md5 hash we need to use the md5 function.
echo "Your password is incorrect.";
}else{
if(empty($row['login_ip'])){ // checks to see if the login ip has an ip already
$row['login_ip'] = $_SERVER['REMOTE_ADDR'];
}else{
$ip_information = explode("-", $row['login_ip']); // if the ip is different from the ip that is on the database it will store it
if (in_array($_SERVER['REMOTE_ADDR'], $ip_information)) {
$row['login_ip'] = $row['login_ip'];
}else{
$row['login_ip'] = $row['login_ip']."-".$_SERVER['REMOTE_ADDR'];
}
}
$_SESSION['user_id'] = $row['id'];// this line of code is very important. This saves the user id in the php session so we can use it in the game to display information to the user.
$result = mysql_query("UPDATE users SET userip='".mysql_real_escape_string($_SERVER['REMOTE_ADDR'])."',login_ip='".mysql_real_escape_string($row['login_ip'])."' WHERE id='".mysql_real_escape_string($_SESSION['user_id'])."'")
or die(mysql_error());
// to test that the session saves well we are using the sessions id update the database with the ip information we have received.
header("Location: play.php"); // this header redirects me to the Sample.php i made earlier
}
}
}
}
?>
you need to find which user you are logged in as. How do you log in to your system? You have several options which you can try out:
use sessions (save the userID in the session, and add that to the query using something like where id = {$id}
Get your userid from your log-in code. So the same code that checks if a user is logged in, can return a userid.
Your current code shows how you log In, and this works? Then you should be able to use your session in the code you had up before.
Just as an example, you need to check this, and understand the other code. It feels A bit like you don't really understand the code you've posted, so it's hard to show everything, but it should be something like this.
<?php
session_start();
$id = $_SESSION['user_id'];
//you need to do some checking of this ID! sanitize here!
$result = mysql_query("SELECT * FROM users" where id = {$id}) or die(mysql_error());
// keeps getting the next row until there are no more to get
while($row = mysql_fetch_array( $result )) {
}
Hello I have a question. I have set up my login system with cookies and it works. But I wonder is there a more clean version of doing this.
<?
include('../config/db_config.php');
$username = $_COOKIE['user'];
$password = $_COOKIE['pass'];
$result = mysql_query("SELECT * FROM users WHERE isadmin = 1");
while($row = mysql_fetch_array($result))
{
if($username == $row['username'] && $password == $row['password'])
{
//User entered correct username and password
echo("ALLOW");
}
else
{
//User entered incorrect username and password
echo("DENY");
}
}
?>
You see I want all my content to be shown ONLY if I am logged in as admin. So what, now only way of doing this would be ECHO'ing out my HTML/PHP/Javascript instead of echoing ALLOW because if I just include("somepage.php") there that page would still be avialable for usage without logging in, and even if I do same check there I still would be ECHO'ing out everything.
Why are you loading every user, then comparing the username and the password? Wouldn't be easier to load a single user matching the username and the password?
Loading a single user will allow to remove the while().
In PHP, don't use mysql_query; do use PDO (if need, google for it to know why it's better).
Check your input (quite optional here, I agree).
Do never store passwords in plain text format.
You can probably do something like (I haven't used PHP/PDO for years, so the code may be inexact):
if (strlen($username)> 128)
{
// Something wrong. The username is too long.
}
$hash = sha1($password);
$sth = $dbh->prepare('if exists(select * from users where isadmin = 1 and username = :username and password = :password) select 1 else select 0');
$sth->bindParam(':username', $username, PDO::PARAM_STR, 128);
$sth->bindParam(':password', $hash, PDO::PARAM_STR, 40);
$sth->execute();
$isFound = $sth->fetchAll();
if ($isFound)
{
// User entered correct username and password.
echo 'ALLOW';
}
You could set a session variable on your login page (or any page that checks the login) that stores whether or not they're logged in and it will persist across pages. Then you can simple wrap your admin html in an if statement like so:
<?php
if ($_SESSION['isAdmin'] == true) {
?>
<p>My admin html</p>
<?php
} else {
?>
<p>My non-admin html</p>
<?php
}
?>
To save the info in a session, just add this to the part where you have echo("ALLOW");:
$_SESSION['isAdmin'] = true;
You'll also want to add session_start(); to the top of the script.
I would suggest that you do something like that only once, when the user first accesses the page, and then set a $_SESSION['is_admin'] or something for the rest of the time, so that you don't have to make an extra db call each page.
You could always put your "somepage.php" above the document root. This is a common way of preventing direct execution.
For example, if your webserver looks like 'project/public_html/index.php' put your admin-only include in 'project/somepage.php' then reference it using something like include("../somepage.php").
Obviously this will need adjustment according to the real paths you use.