Is this login process valid?

Login process can be done in many ways. Searching for a solution, I found that there was some consensus about the principles, but no consensus on how to implement them. Hence I did it “my way” by isolate all sensitive parts from the internet.

My question is if I am thinking in the wrong direction or if there is something I missed? (Except for the hashing and salting that will be implemented in production)

Here is a carousel description. Press “Next” to show the process…

And here is a live version. Mail: sibertius@gmail.com and password is password.

TIA!

Vague blobs on a chart, but… sure. You’ve got an extra step in there than most people would use (the ‘Auth’ block. Most implementations just go straight to the database), but I suppose that mostly depends on if you trust other machines in your “internal IP” space.

1 Like

By using a separate Auth server, I hope to isolate the authentication process. And using a separate isolated database seems to me safer.

Even if the Web server is compiled, I guess you can reverse engineering back to readable code. As the Web server is reachable on the internet. The Auth server is only reachable from within the Web server via internal IP-address.

So, you claim that this is not common practice? To “isolate” the entire login process as well as the customers databases from direct access to the internet?

The typical login process is:

  1. Browser sends credentials to Web Server.
  2. Web Server establishes connection to the Database using its own credentials.
  3. Web Server sends credentials in the form of a query to the Database.
  4. Database returns data.

Which is similar to what you have, except in your scenario, the web server logs in to another server that has access to the database. And… does what, exactly?

(Basically… in your chart, step 3/5 seem to be entirely redundant.)

The Auth Server creates session uuids and are hosting passwords based on the match of login mail and password. The step 3-5 is to “verify” that the user is also present in the users database. So no unauthorized user will access the database. While validating, the roundtrip to the database also collects user preferences and pass to the web server for faster cached access.

So…
The Web server takes the username and password from the user.
The Web server is trusted to communicate to the Auth server.
The Auth Server has a username and password to the database.
It verifies the user is in the database by… connecting to the database, and making sure that the user is in the database.
(Can you tell me why the Web server, who is trusted to communicate with the Auth server, isnt trusted just to… go directly to the database?)
It then sends the data to the web server, who sends it to the browser.

Yes. Sent from the browser form and passed via Web server to the Auth server.

An internal ip-adress (10.x.x.x) must be trusted enough?

The Auth Server has three tables. Users, Databases and Sessions. So, the Auth server also has the data credentials for connecting the users database.

Good question. But I think this is in the next step. How to query the database. Or do I get your question wrong? The login process is just to create a valid session.

Correct.

So the Web server, block 2/6, is in both the top and bottom boxes. It’s the straddle point. Which is normal.

This sentence confuses me.
You’ve put a database inside the Database Server, that the Auth server communicates with, that defines users, databases, and sessions.
The Database server has its own internal tables for users, databases, and maintains its own set of session data.
The Web server has been given the user’s credentials, has the ability to maintain a session itself…

A valid session for… what? I cant… find a reason yet for why you have this extra step. You trust the web server, it can create its own session…

It’s not an INVALID setup, it just seems to be inefficient.

Nope. It is 2 databases. The Auth Server has its own database with just 3 tables. Just for authentication. Nothing else. The Database server contains users own data.

No credentials is passed to the Web server. The db credentials stay within the safe box. Is used by the “query database step”.

What is sent to the Web server is just session_id and user preferences. Less harmful stuff.

The session_id sent to the browser is the key to communicate safe with the database in the next step.

The next step “query the database” (under construction…) may explain why I hide db credentials in the safe box. BTW, did you experienced any “spinning wheel” or other delays when you logged in the live site?

But the web server is in the safe box. Remember? You trust it implicitly because it has a 10.x.x.x address?

So we’re running 2 seperate databases, one of which the webserver can log in to, but contains no useful data.

We send the webserver to talk to this database, so that it can talk to another database, to be able to hand back data…

Doesn’t it sound like we’ve got 2 webservers?

The browser must be able to connect to the Web server via external IP address (but behind a proxy for more security). Hence more vulnerable. The Web server is outside the safe box.

But all communication FROM the Web server TO Auth, API and Database is 10.x.x.x (not reachable from internet)

Yes. The Auth database (just for login) and the users own database. Both contains valuable data (in production), but have different purposes.

The Web server does not talk directly to the Database Server. All queries go through the API server. I also thought API was a redundant step, but I am no convinced that this step increases the safety a lot. And also makes it easier to code and maintain.

Actually there are 3 web servers. Web, Auth and API. But only ONE communicate with the browser (Web server). Some call Auth and API for “micro services”

I strongly disagree with this statement. You’re doubling your effort, and need to ensure that your web server, auth server, and API server are all running the same version and update all of them.

Does it increase security? Negligibly, but sure. If i can get into your web server, I can get to your Auth server, and can do anything that the web server can do anyway.

Does it increase overhead? Yes.
Does it increase load time? Yes.
Does it increase running costs? Yes.
Does it increase failure points? Yes.
Does it increase complexity? Yes.

IMO, the benefit does not come close to the costs. YMMV.

By separating the SQL queries from the Web server and put them into the API instead, you increase the risk for SQL injection. By separate the code for connection and just focus on CRUD, you have a cleaner code.

But API or not is another question. I understand your point as it was my own opinion before I went the API route.

Of course. Everything is possible to hack. But putting extra layers of hinders you may at least delay or in best case stop intruders. One layer is that the internal IP-address is within compiled code. Not readable without reverse engineering. I guess that PHP may be human readable on the server without extra effort? So it takes time to find out the internal IP address?

This statement applies to database passwords and database ip embedded in the compiled code too. So I dont see how this changes anything?

The password will be hashed and salted in production. So even if you reach the Auth server, you may have another fence to climb?

What you’re describing is security through obscurity. Again, it’s not an invalid approach. It’s just not efficient. shrug

If your web app is likely to be attacked often, you may find value in it. If it’s not, you’re making it unnecessarily complex and slow for the 99.999999999999999999999999% of use cases, to prevent the 0.000000000000000000000001%. It’s your call on whether that’s worth it.

Here’s my process of logging in a user while it still can be improved I pretty happy with it and to me it’s pretty straight forward.

// Process the login form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Check if the submitted CSRF token matches the one stored in the session
    if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        // username and password input
        $username = strip_tags($_POST['username']);
        $password = $_POST['password'];

        // Verify the user's credentials
        if ($login->verify_credentials($username, $password)) {
            // Generate a secure login token
            $token = bin2hex(random_bytes(32));
            // Store the login token in the database
            $login->store_token_in_database($_SESSION['user_id'], $token);

            // Set a secure cookie with the login token
            setcookie('login_token', $token, [
                'expires' => strtotime('+6 months'),
                'path' => '/',
                'domain' => DOMAIN,
                'secure' => true,
                'httponly' => true,
                'samesite' => 'Lax'
            ]);

            // Store the login token in the session
            $_SESSION['login_token'] = $token;

            // Redirect the user to the dashboard
            header('Location: dashboard.php');
            exit;
        } else {
            // log error message for invalid username or password
            $error = 'Invalid username or password';
            error_log("Login error: " . $error);
        }
    } else {
        // Display an error message
        $error = 'Invalid CSRF token';
        error_log("Login error: " . $error);
        // $error = 'An error occurred. Please try again.';
    }
}

I did not see that coming :slight_smile:
You claim that my login process is too secure?

Living in Europe you have to adhere to the GDPR rules that is mandatory. I have no choice other than make it as safe as possible. And regarding speed, I will watch this closely.

But thank you for the chat!

No, I didnt say it was too secure.

You dont gain enough security for it to be worth it. It’s not just “more security = more good”, there are costs to balance against.

If the bank puts your money inside a vault, it’s secure. Bank robbers could rob the bank though, you shout.

So the bank puts your money in a vault, behind an eyeprint scanner, in a secured building that noone except the vault staff are allowed to enter.

Can the robbers still rob you? Yup. Is it harder? Yep.

The bank’s now bought a new building, a security system for retinal scanning, hired and trained an entire new staff, and have to keep paying them.

Also, when you go to withdraw your money, you have to wait 24 hours because it takes the bank that long to get your money from the vault now.

Are you too secure?

I took the time for creating a cookie once (calling the Auth Server and verified by checking the presence in the users database). ONCE per cookie lifetime (18 hours?):

func login(w http.ResponseWriter, r *http.Request) {
	log.Println("start creating cookie")
	start := time.Now()

	sess_id, sess_prefs := auth_usr(r)    // request sess_id and sess_prefs
	set_cache(sess_id, sess_prefs)        // set server cache
	set_cookie(w, r, sess_id, sess_prefs) // set browser cookie

	end := time.Now()
	elapsed := end.Sub(start)
	log.Println("cookie created", elapsed)

	if sess_id != "" {
		http.Redirect(w, r, "/home", http.StatusSeeOther)
		return
	}

	http.Redirect(w, r, "/login", http.StatusSeeOther)
}

2024/01/29 06:50:42 start creating cookie
2024/01/29 06:50:42 cookie created 17.64315ms (0,018 second)

And subsequent calls only verifying the cookie (not calling Auth server):

	log.Println("start verifying cookie")
	startverify := time.Now()

	set_header(w)
	//check session id
	if module != "auth" {
		flag := verify(w, r)
		if flag == 0 {
			module = "login"
		}
	}

	end := time.Now()
	verifyed := end.Sub(startverify)
	log.Println("cookie verified", verifyed)

2024/01/29 06:51:14 start verifying cookie
2024/01/29 06:51:14 cookie verified 11.011µs (0.000012 second)

I do not mind waiting that long for my money, if the bank is safe enough :slight_smile: