I would like to know what can and what can not be changed by user related to Web Development.
Mainly if users can change variables in $_SESSION.
But it would be nice to know if some techniques that seem secure are actually not.
Another example would be user changing code in JS Classes etc.
I am sorry if this question seems too trivial to someone. Studied Web Development for 2 years and would like to learn something new.
Here is a example of code where I am not sure if user can change his $_SESSION['group']
public function login() {
if(isset($_POST['login_submit'])) {
if(isset($_POST['username']) and isset($_POST['password'])) {
// Fetching user data
$stmt = $this->conn->prepare('SELECT Password FROM TB_Users WHERE Username = ?');
$stmt->execute([$_POST['username']]);
$user = $stmt->fetch();
//-----------------------
// Password Verification
if(password_verify($_POST['password'], $user['Password'])) {
//-----------------------
// Fetching Group of user so it can be used later to fetch Permissions
$stmt = $this->conn->prepare('SELECT TB_userGroups_ID FROM TB_Users WHERE Username = ?');
$stmt->execute([$_POST['username']]);
$UserGroup = $stmt->fetch();
$_SESSION['group'] = $UserGroup['TB_userGroups_ID'];
//-----------------------
// Sending user back from Login page to Main/Index page
header("Location: ../index.php");
exit();
//-----------------------
}
}
}
Many thanks for any help or redirection to sources.
If by user you mean visitors to the website and not developers (just making sure), then no they cannot unilaterally change anything in $_SESSION since those values only exist on the server. The user only has a cookie on their end that the server uses to recognize which collection of $_SESSION variables belong to them. The developer is responsible for making sure that what goes into the $_SESSION is controlled. In the example you have here, the only way $_SESSION gets changed is based on what is retrieved from the database, so assuming that content is safe, then it is protected.
My only recommendation would be to get rid of your isset($_POST['var']) stuff and replace it with filter_input(). You can use this to retrieve values from $_POST and also sanitize them. When called, you can expect null or false for missing or invalid content, so it's a little easier to control validation vs empty values. Sample:
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
Full docs on that here https://www.php.net/manual/en/function.filter-input.php
Be sure to familiarize yourself with the filter flags. FILTER_SANITIZE_STRING for instance is not a great one for passwords.
Some other things to consider:
Make sure your session cookies are flagged with HTTPOnly and Secure and if on PHP7+ also set SameSite flag to strict. HTTPOnly prevents javascript from being able to modify the cookie. These settings are in php.ini.
Make sure your users are protected from XSS where someone could access their session through malicious code injected into your site (or perhaps a completely external site). These headers can be set in an .htaccess or in a host/vhost configuration file.
Ensure that your encryption policy for passwords meets basic requirements including secure encryption methods and salts. In general you shouldn't have to retrieve a password from the database; you should be able to salt and encrypt the provided password one way and change your query to simply count the number of rows matching username and the encrypted password. This way the secured password in the database never exists on the web server (even in memory).
Related
I'm currently developing a login API for use across two different platforms. It's pretty straightforward, simply comparing an email and hashed password in the database and returning a unique token (stored as a cookie) for future data retrieval.
It works perfectly at the moment. However, any malicious user could abuse this system by visiting the API endpoint and constantly logging in, generating an infinite amount of tokens in the database.
So, what is the best approach to preventing something like this? The only solution I could develop is a unique device identifier. Still, browsers heavily restrict that information and an attacker could always spoof the data.
Here's my current logic in PHP (note that this is simplified and actually returns a JSON object):
/* /api/handle/login.php */
$email = Input::post("auth_email") ?? "";
$password = Input::post("auth_pass") ?? "";
$user = new User;
$user->login($email, $password);
// Check that the user credentials are correct.
if(!$user->loggedIn()){
echo "Failed to login.";
}
// Retrieves an authentication generated on $user->loggedIn().
echo $user->authToken();
Now I cannot generate only one token per user, as this would mean they would be signed out every time they sign in on a new device. Same thing for IP identification.
I just want everyone to know that I am in no way a professional web developer nor a security expert. Well, I'm not a beginner either. You can say that I am an amateur individual finding interest in web development.
And so, I'm developing a simple, small, and rather, a personal web app (though I'm thinking of sharing it to some friends and any individual who might find it interesting) that audits/logs every expense you take so you can keep track of the money you spend down to the last bit. Although my app is as simple as that (for now).
Since I'm taking my app to be shared to some friends and individuals as a factor, I already implemented a login to my application. Although it only needs the user key, which acts as the username and password at the same time.
I've used jQuery AJAX/PHP for the login authentication, as simple as getting the text entered by such user in the textbox, passing it to jQuery then passing it to the PHP on the server to verify if such user exists. And if yes, the user will be redirected to the main interface where his/her weekly expense will be logged.
Much for that, my main problem and interest is within the security, I've formulated a simple and a rather weak security logic where a user can't get to the main interface without having to login successfully first. The flow is like this.
when a user tries to go the main interface (dashboard.php) without successfully logging in on the login page (index.php), he will then be prompted something like "you are not able to view this page as you are not logged in." and then s/he will be redirected back to the login page (index.php)
How I've done this is rather simple:
Once a user key has been verified and the user is logged in successfully, cookies will then be created (and here is where my dilemma begins). the app will create 2 cookies, 1 is 'user_key' where the user key will be stored; and 2 is 'access_auth' where the main interface access is defined, true if logged in successfully and false if wrong or invalid user key.
Of course I'm trying to make things a little secure, I've encrypted both the cookie name and value with an openssl_encrypt function with 'AES-128-CBC' with PHP here, each and every user key has it's own unique iv_key to be used with the encryption/decryption of the cookie and it's values. I've encrypted the cookie so it wouldn't be naked and easily altered, since they won't know which is which. Of course, the encrypted text will vary for every user key since they have unique iv_keys although they have same 'key' values hard-coded in the PHP file.
pretty crazy right ?. yea i know, just let me be for that. and as how the main interface (dashboard.php) knows if a user has been logged in or not and to redirect them back to the login page (index.php) is purely easy. 'that' iv_key is stored together with the user_key row in the database.
I've attached a JavaScript in the main interface (dashboard.php) which will check if the cookie is equal to 2, if it is less than or greater than that, all those cookies will be deleted and then the user will redirected to the login page (index.php).
var x = [];
var y = 0;
//Count Cookie
$.each($.cookie(), function(z){
x[y] = z;
y++;
});
//Check if Cookie is complete
if (x.length != 2) {
//If incomplete Cookie - delete remaining cookie, prompt access denied, and redirect to login page
for (var i = 0; i < x.length; i++) {
$.removeCookie(x[i], { path: '/' });
};
alert("You are not allowed to enter this page as you are not yet logged in !.");
window.location.href = "index.php";
} else {
//If complete Cookie - authenticate cookie if existing in database
}
As you can see, the code is rather incomplete, what I want to do next after verifying that the count of the cookies stored is 2 is to dig in that cookie, decrypt it and ensure that the values are correct using the 'iv_key', the iv_key will then be used to decrypt a cookie that contains the user_key and check if it is existing in the database, at the same time the cookie that contains access_auth will also be decrypted and alter it's value depending on the user_key cookie's verification (returns true if user_key is found in database, otherwise false). Then after checking everything is legitimate, the cookies will then be re-encrypted using the same iv_key stored somewhere I don't know yet.
My question is and was, 'where is a safe location to store the encryption/decryption key?' and that is the 'iv_key'. I've read some threads and things about Session Variables, Local Storage, and Cookie. And I've put this things into consideration.
SESSION - I can use session storage of PHP to store the key in something like $_SESSION['user_key'] then access it later when needed be. But I've read an opinion saying that it is not recommended to store sensitive information including keys, passwords, or anything in session variable since they are stored somewhere on the server's public directory. And another thing is the session variable's lifespan, it lasts for around 30 minutes or so. I need to keep the key for as long as the user is logged in. The nice thing I find here is that, it'll be a little bit hard to alter the value and I don't need to encrypt it (the iv_key) here since it is server sided, and hidden to the naked eye, well not unless when being hacked of course. What I mean is, they don't appear on the debugging tools just like how localStorage and Cookies are visible there.
LOCAL STORAGE - this eliminates my problem of lifespan, since it will be stored in the localStorage vault of the browser not until I close the browser. But the problem here is that the values can easily be changed via console box of the debugger tool, I can eliminate this problem by encrypting the 'iv_key', but what's the point of encrypting the encryption/decryption key? Should I encrypt it using itself as the 'iv_key' too? Or I can use base64_encode?, which eliminates the security of needing a key, and can be decrypted so easily with no hassle.
COOKIE - this one adopts two problems, one from session variable and one from localstorage. From session variable, I mean is the lifespan. As far as I've read, cookies last for about 1 hour or so, but still depends if an expiry has been declared when setting the cookie. The other is from localStorage, since it can easily be altered via console box of the debugger tools too. Although I've already encrypted 2 Cookies beforehand, but what's the point of storing the encryption key together with the values you encrypted?, should I go on with this and encrypt the 'iv_key' by itself, just like what I might do with localStorage?.
I'm lost as to where I should save this sensitive 'encryption_key' as it is crucial in encrypting and decrypting the cookies and other information my app needs.
Why am I so devastated with such security, despite having a simple worthless app?.
Well, because I know and I believe that I can use this as a two-step further knowledge which I can used with my future projects. I maybe doing web development for fun right now. But I'm taking it to consideration as my profession. And so, I want my apps to be secure in any means.
The v2 reCaptcha has some dramatic improvements over previous iterations. When first implemented (using PHP verification btw) all it asked from my users was to check a box. Then after a few form submissions, it asked for a user to identify some images, then after a few more form submissions it asks the user to verify multiple image challenges.
Does anyone know of a way to completely turn off/ disable manual image challenges in the google recaptcha API? i.e. I want them to ONLY check the JS checkbox - like the first few times the form was completed.
I know it kind of defeats the purpose, but I'm prepared to deal with a little bit of spam if traded for a much better user experience.
I've tried:
turning off the js by adding https://www.google.com/recaptcha/api.js?manual_challenge=false (dug up the line from some old API settings)
https://www.google.com/recaptcha/api.js?fallback=false (alternative 'true' just forces a non JS version)
https://www.google.com/recaptcha/api.js?data-type=none (a shot in the dark based on their display options)
I am assuming google monitors the implementation and changes the UI intelligently. In my instance many requests from the same IP address looks like a bot and therefore requires better verification. However, it is just a single user re-submitting the same form a number of times. What I'd like to do is override this to use the minimum security always.
Google's reCaptcha assumes that each time you're challenging someone, you suspect that they're a bot, so if they have already passed a challenge, the next challenge gets progressively harder.
Thus, only challenge someone when you think they might be a bot, such as the first time they submit the form, or if they're not authenticated to your site. Once Google tells you that the user is safe, trust them unless/until you have reason to suspect that user again.
The PHP $_SESSION superglobal is probably your best bet, but as with all sessions, be certain that you're following best practices (session name fingerprinting, token entropy, session fixation attacks, mixing insecure and TLS sessions, etc.)
The way I would handle it is, when a user first successfully passes a CAPTCHA challenge, do not challenge them again.
The example below is based on the code provided by Google in their example: https://github.com/google/recaptcha/blob/master/examples/example-captcha.php
<?php
if (empty($_SESSION['isCaptchaVerified'])) {
$recaptcha = new \ReCaptcha\ReCaptcha($secret);
$resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp);
if ($resp->isSuccess()) {
// verified!
$_SESSION['isCaptchaVerified'] = true;
} else {
$errors = $resp->getErrorCodes();
}
}
...
?>
<form action="/" method="post">
...
<?php if (empty($_SESSION['isCaptchaVerified'])) { ?>
<script type="text/javascript"
src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang; ?>">
</script>
<?php } ?>
</form>
This will:
Check if the user has passed a challenge before
Present the challenge if $_SESSION['isCaptchaVerified'] is not set or falsey
Not present any challenge if $_SESSION['isCaptchaVerified'] is truish
(See the PHP manual entry on empty() for what constitutes truish and falsey in this context).
Go to your admin console in google where you set up recaptcha for the site. Click on advanced settings, reduce the security preference to the least.
Solved
I am validating my users with header variables that I display in my .net application and my question is how can I validate that the user that is on the on the current page is allowed to proceed to any other pages.
I want to check the name from an array or names and if they are not listen then it will redirect them to an error page letting them know they do not have access.
I was going to take the path of sql authentication but that would just require an additional login page and since I already check the header variables I thought I could just go about this way. Any help regarding this would be great!
You should never trust ANY data sent from the client to your server. The header-variables can easily be modified to represent anything. One could easily forge the header to spoof themself for being somebody else (like admin in worst case).
You should really consider some sort of authentication that requires a combination of username + password, I'm afraid.
If you REALLY want to rely on the headers though, add a header that identifies themself, like X-USERNAME:CSharpDev4Evr, and then just parse that one and match against the array on back-end.
I don't know any C#.NET, but here's a JavaScript-snippet showing the principle:
var headerUsername = "CSharpDev4Evr";
var validUsernames = ["Eric", "CSharpDev4Evr", "Stackoverflow", "root"];
// Check if we are in the array
// Re-direct if we're not
if (validUsernames.indexOf(headerUsername) === -1)
window.location = 'error.html';
// Proceed with other authenticated stuff here
// ...
If I have an XML database on my web server;
<Database>
<Client sid="0123456789abcdefg" name="John Doe" email="johndoe#mail.com" hash="9876543210abcdefg" salt="abcdefg9876543210">
<Setting>A Setting</Setting>
<Setting>Another Setting</Setting>
</Client>
...
</Database>
And I log in with the hash and salt, retrieve the SID, and redirect to the home page via PHP;
header("Location: home.html?sid=" . $sid);
And then use the SID in the location bar via JavaScript to retrieve the user settings from the same database, will I expose my clients' hash?
Is there a better way, or a more standard way, to set and get user settings on the web?
P.S.: Unless you have a really good reason, I really, really, really, don't want to use SQL. I prefer to be able to read my databases, and I like the tangibility and versatility of XML.
Edit: After a little more research, I learned that PHP supports a system for storing SESSION[] variables. This is perfect for me because I am, in fact, using sessions!
The W3C says:
"A PHP session variable is used to store information about, or change settings for a user session. Session variables hold information about one single user, and are available to all pages in one application."
Much better than exposing various data in the address bar. =)
As long as your DB file is inaccessable from HTTP (i.e. locked by a .htaccess or equivalent) and other protocols (i.e. not sitting in a directory accesable by anonymous FTP), the only risk is to (inadvertently) let the hash&salt be collected among a bunch of other user-related data and sent to your clients.
If you have requests equivalent to the SQL * selector, that might be somewhat of a problem. You might want to put the critical data into a different DB file and encapsulate the accesses in an interface dedicated to user registration and login, just to make sure no other piece of code will be able to grab them (even by mistake) from your main DB.