I have two domain names (in two hosting locations). Domain 1 is a normal website with authentication. I am using Domain 2 as a place to just upload files (for storage). I am using angular-file-upload https://github.com/danialfarid/angular-file-upload from client side.
My backend code is super simple and it is current working .. but without any form of authentication.
<?php
header('Access-Control-Allow-Origin: *');
$fname = $_POST["fname"];
if(isset($_FILES['file'])){
//The error validation could be done on the javascript client side.
$errors= array();
$file_name = $_FILES['file']['name'];
$file_size =$_FILES['file']['size'];
$file_tmp =$_FILES['file']['tmp_name'];
$file_type=$_FILES['file']['type'];
$file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
$extensions = array("jpeg","jpg","png", "gif");
if(in_array($file_ext,$extensions ) === false){
$errors[]="image extension not allowed, please choose a JPEG or PNG file.";
}
if($file_size > 2097152){
$errors[]='File size cannot exceed 2 MB';
}
if(empty($errors)==true){
move_uploaded_file($file_tmp,"images/".$file_name);
echo $fname . " uploaded file: " . "images/" . $file_name;
}else{
print_r($errors);
}
}
?>
Questions:
How to make sure the user is authenticated in Domain 1 before they can upload files? I mean I could just change Access-Control-Allow-Origin but someone can easily go to Chrome Developer and do a POST upload from custom javascript.
Is there a way to do #1 without getting overly complicated? Is it possible to pass some cookie values?
I have full control of both hosting sites so I can do whatever I want.
Sharing a common secret
One way is to share a secret, wich is only known to Site A and Site B.
Suppose Site A and Site B know a common complex and non-predictable string salt [randomness source].
If Site A authenticates a user, A creates a random string rndA, a valueThrough timestamp and then computes a hash like so:
sharedHash = hash( rndA + salt + valueThrough )
Site A hands over this tuple to the client: [ sharedHash, valueThrough , randA ]
The clients hands over this tuple to Site B
Site B then verifies the client's rights using the same hash() operation.
If B computes the same sharedHash and the current timestamp is still smaller than valueThrough, client gets authenticated.
Letting Site A and Site B talk to each other
Alternatively, Site A and Site B might talk directly to each other:
Site A hands over a security token to the client
Client hands over the security token to Site B
Site B then verifies the token by directly talking to Site A
While this technique requires reachability of Site A <-> Site B, the former technique even works, if Site A and Site B can't exchange HTTP-requests directly.
In both cases Generating cryptographically secure tokens might be of interest.
Other and stronger techniques certainly exist.
How's about sharing session in db like redis or mongodb?
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 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).
How can I make an HTML (and CSS/PHP/JavaScript) document which only allows certain IP addresses on a page?
(I am not asking how to find IP address with PHP, but how to allow access to a page based on an IP address.)
put this on the top of your php file and update the allowedIps variable with the IPs that you want to allow.
$allowedIps = ['198.x.x.x', '200.x.x.x'];
$userIp = $_SERVER['REMOTE_ADDR'];
if (!in_array($userIp, $allowedIps)) {
exit('Unauthorized');
}
for non PHP files (eg .html, .css) you will have to update your .htaccess file to add file specific permission. The following SOF thread should help: (assuming you are using apache server)
.htaccess: how to restrict access to a single file by IP?
If you dont want to bother with the code, put your site on Cloudflare and block ips
Try this with PHP :
function CheckIPAccess() {
//allowed IP. Change it to the IP addresses you want to allow to access your webpage
$allowedip = '127.0.0.1';
$ip = $_SERVER['REMOTE_ADDR'];
return ($ip == $allowedip);
}
Usually, IP restrictions are done at the web-server configuration level, so that unauthorized IPs simply can't reach your code at all.
It would actually be quite messy to try to do this kind of check within your application – "you'd undoubtedly miss one" – but the server can easily do it for you.
(I do not recommend attempting to use IPs for privilege checking and so forth ... "IPs change. Frequently. Very messy. Very ...")
Even stronger yet would be firewalls, and maybe VPNs. You really want to keep intruders as far away as possible and to give them as little information as possible. (For instance, why should they even be able to detect that the web-server exists?) Strive to make the entire setup as "hardened" as possible.
I've been working on a social media management tool company for the last eight months. This will be the first major website I release and I have concerns about its security and systems. Obviously security must be a very high priority as my target customers are businesses and individuals who are looking to grow their social followings. If my company's site gets hacked and any of my client's profiles get accessed it would destroy any reputation I had built up and massively slow down progression of the company. So today I'm wanting to share with you all how I wrote the site and see if there are any security flaws I could run into, or if there's a better way to write any of the systems I have in place.
Server systems (Java side of things)
When I first started working on this company I mainly knew Java as I worked with it in a few previous jobs. While other languages may be similar and more powerful, such as C++, I decided to stick to what I knew best. I felt like I still made the correct choice when I was 30,000+ lines in as my servers were running with every little CPU usage and only using 11% - 24% of it's allocated 64MBs of memory. Figured switching at any point to C++ or similar wouldn't be worth the time for such little possible performance improvement.
I have a server running for each social profile. If a client has an account with a Facebook page and two Twitter accounts there will be three servers running for that client. Each one will load specific modules of my software depending on the social platform (Facebook vs Twitter etc). The reason why I wanted one server per social profile is that some payment plans could add more and more social profiles. With more and more social profiles I'd need more and more resources to run that server. I'd rather have the minimum assigned to each social profile server than have to constantly adjust the performance of one large server for a client with 13 social profiles (as an example). Another benefit is to be able to easily remove social profiles, or to "overflow" servers to another box if my current box becomes full and a client request another social profile server. If I would need to expand a large server due to increase request in social profiles on an already full box that may become messy. Is this wrong to do? Is there any flaws in my logic here?
These servers will handle the jobs of post scheduling, listening for "social events" (new followers, direct messages etc) and reacting accordingly to them based on my site's features.
Account registration
I have the typical registration system with an email, a password and a confirm password. I currently don't have a confirm email system but of course that'll be added before launch. Would having a "remember me" option open up any security flaws? If so what ones and what actions could I take to prevent them?
My current system doesn't use MySQL in PHP at all. I use socket communication to send the information to my Java servers to create an account in my MySQL database. There's a Java server used just for "general" communication that isn't meant to be for a specific social profile. The main reason for wanting to create an account on the Java side instead of in PHP is that once I add iOS and Android apps I can easily sent the same sockets to create an account within the app. Would sending sockets to communicate to a Java server to run the MySQL query cause any problems?
What is the absolute most correct and secure way to handle account registration?
Server to browser communication
I am using AJAX to communicate with a PHP script that uses sockets to communicate with my Java servers. Here's a basic example of an AJAX call:
$(document).on("click", ".trash", function() {
//TODO: Add a "are you sure" pop up
var element = $(this).closest(".scheduleddata");
var socialCount = element.attr("socialcount");
var botIndex = element.attr("botid");
element.css("display", "none");
element.html("");
$.ajax({
url: "/client.php",
type: "POST",
dataType: "JSON",
data: {
packet: "62:" + botIndex,
socialCount: socialCount
},
timeout: 15000,
async: true
});
});
Packet ID "62" is meant to delete a scheduled post. Each bot index is a completely unique identifier for each scheduled post. The social count is a counter for each social profile attached to that client's account. For example if I have four social profiles linked to my account I could communicate with a server that has a social count of 1 - 4. If a friend of mine makes an account and links two social profiles to their account, they can communicate with social counts 1 - 2. So the social count is only unique to each client's account. It could be completely possible to make the social counts completely unique. Using the previous example I'd have access to social counts 1 - 4 and my friend would have 5 - 6. Would that be a more ideal system to prevent possible attackers from knowing that social count #1 would always exist?
There is a client/account ID as well, as expected it is the auto incrementing integer in the accounts MySQL table. I used to require the client ID to be sent in the data of the AJAX calls but then I learned that anyone could then communicate with any servers on my service through AJAX and that's obviously not good at all. Currently I have the client ID stored in a PHP session variable when they login. So because I have the first account ever created on the site my client ID is 1, a friend of mine who is helping me test is 2, etc. The client ID is never displayed, loaded, stored, or used anywhere but the session variable. Is there any security flaws to this? If someone could edit that variable and sent the right packets they could communicate with other client's social profiles and that's not good obviously.
For my client.php code I load the address and port of the targeted server:
<?php
if(isset($_POST['packet']) && isset($_POST['socialCount'])) {
$socialCount = $_POST['socialCount'];
if($socialCount > 0) {
echo json_encode(client::send(strip_tags($_POST['packet']), null, -1, $socialCount));
}
}
class client {
public static function send($message, $address, $clientID, $socialCount) {
if(!isset($_SESSION)) {
session_start();
}
$message = strip_tags($message);
if($clientID == -1) {
$clientID = 0;
if(isset($_SESSION['client_id'])) {
$clientID = $_SESSION['client_id'];
} else {
return null;
}
}
$message .= "\n";
$port = 3000;
if(isset($_SESSION["location"][$clientID][$socialCount])) {
$location = explode(":", $_SESSION["location"][$clientID][$socialCount]);
$address = $location[0];
$port = $location[1];
} else {
if($address == null) {
if($clientID == 0 || $socialCount == 0) {
$address = "localhost";
} else {
$reply = client::send("14:" . $clientID . ":" . $socialCount, "localhost", 0, 0);
if(strcmp($reply, "-1") == 0) {
$address = "localhost";
} else {
$result = explode(':', $reply);
$address = $result[0];
$port = $result[1];
}
}
}
$_SESSION["location"][$clientID][$socialCount] = $address . ":" . $port;
}
$socket = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
try {
socket_connect($socket, $address, $port);
$message = utf8_encode($message);
$status = socket_sendto($socket, $message, strlen($message), MSG_EOF, $address, $port);
if($status != false && $next = utf8_decode(socket_read($socket, $port))) {
return substr($next, 2);
}
} catch(Exception $e) {
}
return null;
}
}
?>
I feel like most of this code could be improved, I wrote it when I first started learning PHP so please excuse any sloppyness. I am currently storing the returned address:port into a session variable so I only have to load it once. There are some parts of the company that send packets to external servers that my clients set up. So the address/port of their social profile would be public information anyways. I'm blocking incoming communication from IP addresses that are not "whitelisted". These "whitelisted" IPs will be all of my own boxes for now. So I will just be sending packets and not receiving them ideally. Are there any problems with this system?
On a side note, I believe the way Java and PHP encode unicode is different. Whenever I encode unicode and send it from the Java server to the PHP server and I decode it, it doesn't work. How could I fix this problem? I've been having problems figuring this one out for months.
Security measures already in place
As you saw above I'm already using strip_tags and removing all things I can from AJAX calls. I am also buying a standard SSL from GoDaddy, and I'll be upgrading to their most powerful SSL plan once the company starts turning a decent profit.
What are some other basic security that I should be implementing into my site?
Conclusion
As you can see for the type of company I'm trying to launch it seems that security is very important. I'd absolutely love to hear any input and advice anyone has. Thank you to anyone who spent the time to read the full post!
I have a little question about possibilities to change authentication on my C# server from Login\Password to PHP session. I am using PHP, HTML5, and JavaScript as client side, and C# server with WebSockets lib as server side. I already have authorization code, but it works only with login and password.
Well, I have the same authentication code, that checks login & password via PHP. But how I can authorize client on C# server, by using WebSockets with PHP session, instead account login & password?
Little pic:
Is this possible? And any piece of code, demo, etc. for it?
Genreate an access token that your c# server can validate. This is how i would implement it:
share a secret between your PHP and C# server (e.g. 64 randcom bytes)
When PHP validates the user/login, generate a token: it contains random characters, the user ID and a signature that was created using the shared secret. E.g. something like this:
$random = base64_encode(mcrypt_create_iv(32));
$token = $random . '_' . $userID . '_' . hash_hmac('sha256', $random . '_' . $userID, SHARED_SECRET);
The C# server can then verify this token by calcuating the HMAC signature from $random and $token with the shared secret. If correct, the C# server can work with the $userID and the MySQL databse.
As long as you pass the token to your JS code via https, you should be secure.
P.S.: You can improve the security of this scheme by adding an expiration date to the token and make your JS code request a new token in time. Dont forget to include the expiry in the signature.